向書本學(xué)習,還要向?qū)嵺`學(xué)習、向生活學(xué)習。消化已有知識,
而且要力求有所發(fā)現(xiàn)、有所發(fā)明、有所創(chuàng)造
2019/4/7 11:21:33
1 對象相關(guān)的一些語言特性
1.1 一切皆為對象
JavaScript里所有的東西都是對象. 對象是屬性的集合. 數(shù)字, 字符串, 布爾值等原始值是"偽對象", 它們同樣擁有屬性, 但是是在棧上分配并按值傳遞. 而其他的對象是堆上分配并按引用傳遞.
一個很重要的概念是, 函數(shù)也是對象, 能夠作為變量的值, 返回值, 參數(shù)或者屬性的值. 函數(shù)對象特殊的地方是能通過"xxx()"語法執(zhí)行包含在xxx函數(shù)對象內(nèi)的代碼. 因為這一特殊性, typeof xxx 將會返回function, 但這只是一種便利設(shè)施.
1.2 對象的屬性可以動態(tài)添加和刪除
var foo = new Object();
// 為foo對象添加bar屬性
foo.bar = "foobar";
alert(foo.bar); //foobar
// 刪除foo對象的bar屬性
delete foo.bar;
alert(foo.bar); //undefined
1.3 除了宿主對象, 其它對象皆由構(gòu)造函數(shù)創(chuàng)建
要有對象, 就先要有創(chuàng)建對象的方法.
在C++/Java等語言, 這個方法就是實例化XXX類的一個實例xxx.
而在JavaScript的世界里實際沒有類的東西, 當然仍然可以用"類"和"實例"等慣用語來描述JavaScript中類似的行為, 但其機制是完全不同的. JavaScript的對象是由構(gòu)造函數(shù)創(chuàng)建的, 每個對象都有constructor屬性表示創(chuàng)建該對象的構(gòu)造函數(shù):
function Test() { this.a = "hello"; }
var test = new Test(); // 由Test構(gòu)造函數(shù)創(chuàng)建
alert(test.constructor);
var o = { a: "hello" };
//實際相當于
var o_ = new Object();
o_.a = "hello"; // 由Object構(gòu)造函數(shù)創(chuàng)建
alert(o.constructor);
構(gòu)造函數(shù)也是對象, 那構(gòu)造函數(shù)是由什么創(chuàng)建? 內(nèi)建的Function函數(shù):
function Test(a, b)
{
alert(a+b);
}
// 相當于:
Test = new Function(["a", "b"], "alert(a+b);");
Function函數(shù)又是由什么創(chuàng)建? 實際上Function是本機代碼實現(xiàn)的固有對象. 不過為了一致性, Function也有constructor屬性, 該屬性指向它自己. 接上面的代碼:
/* 輸出 function Function(){
[nATIve code]
}
*/
alert(Test.constructor);
alert(Test.constructor.constructor === Test.constructor); // true
alert(Test.constructor === Object.constructor); // true
2 原型prototype
2.1 prototype的概念
prototype是構(gòu)造函數(shù)的一個屬性, 該屬性指向一個對象. 而這個對象將作為該構(gòu)造函數(shù)所創(chuàng)建的所有實例的基引用(base reference), 可以把對象的基引用想像成一個自動創(chuàng)建的隱藏屬性. 當訪問對象的一個屬性時, 首先查找對象本身, 找到則返回; 若不, 則查找基引用指向的對象的屬性(如果還找不到實際上還會沿著原型鏈向上查找, 直至到根). 只要沒有被覆蓋的話, 對象原型的屬性就能在所有的實例中找到.
原型默認為Object的新實例, 由于仍是對象, 故可以給該對象添加新的屬性:
// prototype默認為new Object(); 為了方便, 記為p_obj
function Person(name) {
this.name = name;
}
// 為 p_obj 增加 sayName 屬性
Person.prototype.sayName = function(){
alert(this.name);
}
var john = new Person("John"); // john 的 base reference指向p_obj
var eric = new Person("Eric"); // eric 的 base reference也是指向p_obj
// 注意sayName代碼中的this將指向?qū)嵗蟮膶ο?this綁定)
john.sayName(); // john對象本身沒有sayName屬性, 于是訪問原型對象p_obj的sayName屬性
eric.sayName(); // 訪問同一個原型對象p_obj的sayName屬性
var tmp = Person.prototype;
tmp.boss = "David";
// 于這個運行點, p_obj已經(jīng)被修改
// 根據(jù)上述屬性訪問流程, 新的修改(boss屬性)能反映到所有的實例, 包括已經(jīng)創(chuàng)建和即將創(chuàng)建的
alert("John's boss is " + john.boss);
alert("Eric's boss is " + eric.boss);
// hisCar和sayCar屬性將增加到j(luò)ohn對象而不是p_obj對象..
john.hisCar = "Audi";
john.sayCar = function(){
alert(this.name + " has a car of " + this.hisCar);
}
john.sayCar();
// ..因此下一句將錯誤, 因為eric對象本身和原型p_obj都沒有sayName屬性
/* eric.sayCar(); */
2.2 原型鏈
除了能修改prototype指向的對象, 還能修改prototype指向哪一個對象, 即為prototype賦予一個不同的對象. 這可以實現(xiàn)一種簡單的繼承:
function Superman() {}
Superman.prototype.sayHello = function(){
alert("I'm a superman.");
}
function SupermanCan(skill){
this.skill = skill;
}
// 為prototype賦予Superman的實例..
SupermanCan.prototype = new Superman();
// ..再動態(tài)添加新的屬性
SupermanCan.prototype.sayMore = function(){
this.sayHello(); // 調(diào)用"父類"的方法
alert("I can " + this.skill);
}
var david = new SupermanCan("fly");
// output: I'm a superman. I can fly
david.sayMore();
如果先實例化出一個對象, 再為構(gòu)造函數(shù)prototype賦予一個不同的對象, 將會: 已經(jīng)創(chuàng)建的對象的基引用不變, 將來創(chuàng)建的對象的基引用為新的原型對象:
var f1 = {echo: function() { alert("sound"); } };
function Foo() {};
var foo = new Foo(); // foo的基引用指向Object實例
Foo.prototype = f1;
/* 未定義, 因為這是"foo對象自己或者基引用指向的對象有echo屬性嗎?"
而不是"foo對象自己或者Foo.prototype指向的對象有echo屬性嗎?" */
alert(foo.echo);
var foo2 = new Foo(); // foo2的基引用指f1對象
foo2.echo(); // output: sound
所有的構(gòu)造函數(shù)的prototype都不能為空, 就是說Superman.prototype = null 會被解釋引擎無視; 另一方面, Object構(gòu)造函數(shù)也有prototype屬性(該屬性是只讀的, 可以為原型增加屬性,但不能賦予不同的對象), 故因此可以有多層的原型鏈, 但原型鏈的根必定會是Object.prototype . 這意味著給Object.prototype增加屬性影響到所有對象:
Object.prototype.echo = function() {
alert("hello");
}
// echo屬性將增加到所有對象固有對象和自定義對象
var arr = new Array();
arr.echo();
Array.echo();
function ObjCons() {
this.dummy = "d";
}
var obj = new ObjCons();
obj.echo();
ObjCons.echo();
3. 構(gòu)造函數(shù)和new的實質(zhì)
構(gòu)造函數(shù)是一個地地道道的函數(shù), 一個函數(shù)之所以能成為構(gòu)造函數(shù), 是因為new運算符:
this.msg = "window";
function Test()
{
alert(this.msg);
}
Test(); // window
var test = new Test(); // undefined. 因為test對象沒有定義msg屬性
二者區(qū)別在于如何切入對象: Test() 在某個對象(例子中為window)的上下文上執(zhí)行代碼, 即this指向這個對象; new Test()創(chuàng)建一個新對象, 并以這個新的對象為上下文(this指向新對象)執(zhí)行代碼, 然后返回這個新對象.
假如有個函數(shù):
function Test() {
var dummy = "have money";
this.wish = dummy;
doSomeThing();
}
結(jié)合以上的所有論述, 可以推測new Test()行為的偽代碼表示為:
創(chuàng)建一個新對象temp;
temp.constructor = Test;
temp.(base reference) = Test.prototype; // 這一句先于代碼體執(zhí)行, 意味著構(gòu)造函數(shù)里的this.xxx能訪問原型對象的屬性xxx
bind: this = temp; // 將this綁定到temp對象
// 開始執(zhí)行函數(shù)代碼
var dummy = "have money";
this.wish = dummy; // 為temp對象添加wish屬性
doSomeThing();
....
// 結(jié)束執(zhí)行函數(shù)代碼
return temp;
這個未必會符合內(nèi)部的二進制實現(xiàn), 但卻能很好地解釋了JavaScript的特性.
深圳市南山區(qū)南山街道南海大道西桂廟路北陽光華藝大廈1棟4F、4G-04
咨詢電話:136 8237 6272
大客戶咨詢:139 0290 5075
業(yè)務(wù)QQ:195006118
技術(shù)QQ:179981967