Javascript基礎之-原型(prototype)
首先呢,prototype是物件裡的一個內建屬性,並且呢,這個屬性是對於其他物件的一個引用。所以呢,思考下面的例子:
var obj = {
a: 2
}
var myObj = Object.create(obj);
console.log(myObj.a); // 2
console.log(myObj === obj); // false
console.log(Object.getPrototypeOf(myObj) === obj); // true
Object.getPrototypeOf(myObj).a = 4
console.log(obj.a); // 4
這裡可以看到,實際上Object.create()是新建了一個物件,並且這個物件的prototype是obj的一個引用,所以呢,如果咱們直接修改prototype裡面的值,原物件也就跟著變了。
很簡單吧,那麼如果執行如下程式碼的話,會發生什麼呢?
myObj.a = 10;
是不是認為,還跟上面那個一樣的,obj.a也變成10了呢?實際上不是的,他的執行機制要比咱們想的稍微複雜一點點。
實際上要分三種情況來看:
如果在prototype鏈上存在這個屬性,並且沒有標記為只讀,那麼就會在本物件上新建一個新的同名屬性。
如果在prototype鏈上存在這個屬性,並且標記為只讀,那麼將無法修改已有屬性或在本物件上新建一個同名屬性,如果是嚴格模式的話,還會報錯。
如果在prototype鏈上只是存在此setter,那麼一定會呼叫此setter,並不會新增屬性到物件上,更不會重新定義這個setter
很枯燥是吧,來看例子,對照著上面的情況,好好的理解一下:
var obj = { a: 2, set c(num) { console.log('exec it'); } } var myObj = Object.create(obj); myObj.a = 10; console.log(obj.a); // 2 console.log(myObj.a); // 10 Object.defineProperty(obj, 'b', { value: 3, writable: false }) myObj.b = 10; console.log(myObj.b); // 3 myObj.c = 20; // "exec it" console.log(myObj.c); // undefined
假如上面的已經理解了,那麼可以思考下下面的執行結果:
var obj = {
a: 2
}
var myObj = Object.create(obj);
console.log(++myObj.a); // 3
console.log(obj.a); // 2
這個在咱們實際的編碼中時有發生,看程式碼是想把a改成3,但是由於上面第一種情況的影響,實際上是新建了一個同名屬性3,並且賦值給了myObj。
上面我們談論的都是普通物件的prototype的一些特性,接下來,咱們就要講關於new關鍵字相關的一些知識點了,思考下面的例子
function Foo() {}
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // true
var b = new Foo();
Object.getPrototypeOf(b).saySomething = function () {
console.log('say something');
}
a.saySomething(); // "say something"
很明顯,在new的過程中呢,生成了一個新物件,並且把Foo.prototype引用到了新物件的prototype。那麼因為是引用,所以通過b改變其原型上的prototype的值,Foo.prototype裡也會跟著改變。
那麼new的過程,是不是一定引用的是函式的prototype呢?也不一定,比如說下面的例子。
function Foo() {
return {
a: 3
}
}
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // false
console.log(Object.getPrototypeOf(a) === Object.prototype); // true
console.log(a.a); // 3
在這個例子中,由於new的時候,返回的是一個物件,所以最後實際上a最終引用的是Foo最後返回的那個小物件,所以其prototype就是Object.prototype,而不是Foo.prototype
甚至說,Foo.prototype也是可以被改變的,不過在這時候,new出來的物件,其prototype就是被改過的那個物件。
var protoObj = {
b: 10
}
function Foo() {}
Foo.prototype = protoObj;
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // true
console.log(Object.getPrototypeOf(a) === protoObj); // true
console.log(a.b); // 10
你看,如果prototypeObj修改了預設的Foo.prototype,所以最後,實際上形成了這麼一個引用鏈:a.prototype => foo.prototype => protoObj=>Object.prototype。
所以說結論吧,在new的時候,實際上執行會包含這麼幾步,
如果有return並且返回的是一個物件的話,則直接返回return後的那個物件。
反之,則新建一個物件。
並且吧函式的prototype引用到新建物件的prototype中。
所以說,原型,可以理解為我本來物件有一個prototype,引用著其他的物件,當我這個物件的prototype引用了另一個物件的prototype,一般情況會到Object.prototype為止,這樣就組成了一個原型鏈,原型鏈也就是互相引用的引用鏈。而這個引用鏈是可以根據自己的需求去改。
好了,簡短的一小節就完事了,如果有不明白的,或者有疏漏的地方,或者有什麼地方想和我討論的,可以留言給我哦。