最近在看《你不知道的JavaScript》系列叢書一汽,發(fā)現(xiàn)了平時(shí)容易誤解昧识、混淆或者違背常理的一些特性乌逐,在此進(jìn)行總結(jié)一下取董。
[[Prototype]]屏蔽屬性
var a = {a:2}
var b = Object.create(a)
a.a
>2
b.a
>2
b.a++
>2
b.a
>3
a.a
>2
原因:++操作相當(dāng)于b.a = b.a + 1量淌。因此++操作首先通過[[Prototype]]查找a屬性并從a.a獲取當(dāng)前屬性值2匈织,然后給這個(gè)值加1剑按,接著用[[put]]將值3賦給b中新建的屏蔽屬性a淌铐。
JavaScript中的“類”
在ES6之前杰刽,我們都是用一些方法近似實(shí)現(xiàn)類的功能菠发。然而實(shí)際上JavaScript的類與其他語言并不一樣。
繼承
在其他語言(如Java)中贺嫂,定義一個(gè)子類滓鸠,相對(duì)于父類它就是一個(gè)獨(dú)立并且完全不同的類。子類會(huì)包含父類行為的原始副本第喳,也可以重寫所有繼承的行為甚至定義新行為糜俗。類的繼承本質(zhì)上是復(fù)制。
在JavaScript中曲饱,類繼承和實(shí)例化悠抹,并不會(huì)自動(dòng)執(zhí)行復(fù)制行為。在JavaScript中只有對(duì)象扩淀,并不存在可以實(shí)例化的類楔敌。一個(gè)對(duì)象并不會(huì)復(fù)制到其他對(duì)象,它們會(huì)被關(guān)聯(lián)起來驻谆。
實(shí)例
function Foo(){this.a = 1}
f = new Foo()
Object.getPrototypeOf(f) === Foo.prototype
>true
JavaScript中不能創(chuàng)建一個(gè)類的多個(gè)實(shí)例卵凑,只能創(chuàng)建多個(gè)對(duì)象庆聘。new Foo()會(huì)生成一個(gè)新對(duì)象,新對(duì)象的內(nèi)部鏈接[[Prototype]]關(guān)聯(lián)的是Foo.prototype對(duì)象勺卢。
最后我們得到了兩個(gè)對(duì)象掏觉,它們之間互相關(guān)聯(lián)。實(shí)際上值漫,我們并沒有從"類”中復(fù)制任何行為到一個(gè)對(duì)象中澳腹,只是讓兩個(gè)對(duì)象關(guān)聯(lián)。這是和其他編程語言不同的地方杨何。
“類”和“委托”
function Foo(who){
this.me = who;
}
Foo.prototype.identify = function () {
return "I am "+this.me;
};
function Bar(who){
Foo.call(this,who);
}
//創(chuàng)建一個(gè)新的Bar.prototype對(duì)象并關(guān)聯(lián)到Foo.prototype
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.speak = function () {
console.log("Hello,"+this.identify());
};
var b1 = new Bar("b1");
var b2 = new Bar("b2");
b1.speak();
b2.speak();
>Hello,I am b1
>Hello,I am b2
以上例子是“類”模式酱塔。
Foo = {
init:function (who) {
this.me = who;
},
identify:function () {
return "I am " + this.me;
}
};
Bar = Object.create(Foo);
Bar.speak = function () {
console.log("Hello,"+this.identify());
};
var b1 = Object.create(Bar);
var b2 = Object.create(Bar);
b1.init("b1");
b2.init("b2");
b1.speak();
b2.speak();
>Hello,I am b1
>Hello,I am b2
以上是“委托”模式。
以上均用到了Object.create(...)函數(shù)危虱。它會(huì)創(chuàng)建一個(gè)新對(duì)象羊娃,并把它關(guān)聯(lián)到指定對(duì)象。這樣我們才能充分發(fā)揮[[prototype]]機(jī)制的威力(委托)(查找屬性當(dāng)對(duì)象本身不存在該屬性時(shí)查找原型鏈)埃跷。
"類“模式的代碼中常常會(huì)出現(xiàn)大量” Foo.call(this,who);"這樣丑陋的代碼(從”子類”中引用”父類“方法)蕊玷。相比而言,“委托”模式更為簡(jiǎn)潔明了弥雹。
值得多提一句的是垃帅,在很多書籍中,使用“類”模式時(shí)剪勿,常常使用了兩種錯(cuò)誤的方式來創(chuàng)建原型關(guān)聯(lián)贸诚。
//直接引用的方式,當(dāng)修改Bar.prototype時(shí)會(huì)直接影響Foo.prototype
Bar.prototype = Foo.prototype;
//如果函數(shù)Foo有一些副作用厕吉,比如寫日志酱固、給this添加屬性等等,會(huì)影響B(tài)ar的shi'li
Bar.prototype = new Foo();
閉包的“正確定義”
閉包:當(dāng)函數(shù)可以記住并訪問所在的詞法作用域头朱,即使函數(shù)在當(dāng)前詞法作用域之外執(zhí)行运悲,這時(shí)就產(chǎn)生了閉包