? JS的原型與原型鏈卿嘲,是很容易讓新手忽略的問題颂斜,相較于普通語法,它更難以理解一些拾枣,在開發(fā)上也較不常見沃疮。但這并不意為這它不重要,相反梅肤,它很重要司蔬。
?
# prototype 與 __proto__
? prototype
與 __proto__
(雙下劃線) 很容易混淆,它們之間的指向有些復雜姨蝴,避免難以理解俊啼,我們嘗試用最簡單的圖示將其說明清楚他們的區(qū)別
? 我們知道,原型本質也是一個對象左医,其他對象可以通過它實現(xiàn)屬性繼承授帕。看看一下圖
在大多數(shù)情況下浮梢,__proto__
可以理解為 構造器的原型跛十,即:__proto__ === constructor.prototype
(Object.create 方式除外)這里只是初步理解,不太準確秕硝,請繼續(xù)往下看
? 用代碼解釋芥映,代碼很簡單,但是希望看官自己敲一敲看看控制臺表現(xiàn)远豺,實踐了理解才更加深刻屏轰。如下:
var a = {};
console.log(a.prototype); // undefined
console.log(a.__proto__); // Object ()
var b = function() {
}
console.log(b.prototype); // b {}
console.log(b.__proto__); // function() {}
? 當然,如果定義 var a = ""
; 那么將輸出
> _proto_屬性指向誰
? __proto__
的指向取決于對象創(chuàng)建時的實現(xiàn)方式憋飞。以下圖表列出了三種常見方式創(chuàng)建對象后霎苗,__proto__
分別指向誰
- 字面量方式
var a = {};
console.log(a.__proto__); // Object {}
console.log(a.__proto__ === a.constructor.prototype); // true
- 構造器方式
var A = function() {};
var a = new A();
console.log(a.__proto__); // A {}
console.log(a.__proto__ === a.constructor.prototype); // true
? 即 由構造函數(shù)A 構造出的實例函數(shù)a, 那么a的原型就是構造函數(shù)A
- Object.create 方式
var a1 = {};
var a2 = Object.create(a1);
console.log(a2__proto__); // Object {a: 1}
console.log(a.__proto__ === a.constructor.prototype); // false
(此處即為圖1中的例外情況)
# 原型鏈
? 由于__proto__
是任何對象都有的屬性,而js里萬物皆對線個榛做,所以會形成一條__proto__
連起來的鏈條基括, 遞歸訪問__proto__
必須最終到頭裆悄,且值為null
.
? 當js引擎查找對象的屬性時,縣查找對象本身是否存在該屬性,如果不存在鼓寺,會在原型鏈上查找刺桃,但不會查找自身的prototype
var A = function() {};
var a = new A();
console.log(a.__proto__); // A {}; 實例對象a的原型是構造器A的原型對象
console.log(a.__proto__.__proto__); // Object {} 構造器A的原型是function Object的原型對象
console.log(a.__proto__.__proto__.__proto__); // null
# 實例分析
? 在理解原型與原型鏈的概念區(qū)別信息之后儒拂,我們來看看如下案例(注意大小寫規(guī)范蹬碧,意義不一致)
function Person(name) {
this.name = name;
}
function Mother() { }
Mother.prototype = {
age: 18,
home: ['Beijing']
}
Person.prototype = new Mother();
var p1 = new Person('Jack');
var p2 = new Person('Mark');
console.log(p1); // 見下圖
console.log(p2);
console.log(p1.age); // 18
? 在這個例子中,
Person
就是一個構造函數(shù)避凝,我們使用new
創(chuàng)建了一個實例對象person
舞萄。并且眨补,將這個實例對象的原型指向Mother
。
? 那么倒脓,在執(zhí)行console.log(p1)
時撑螺,我們知道,它會先去找p1
的實例屬性崎弃,在實例屬性中找不到后再去找原型屬性甘晤,最終打印出18
? p2
的打印結果,與p1
類似饲做,除了name
屬性外
# 可能的應用場景
? 我們知道线婚,繼承了原型的原型對象之后,實例對線繼承了原型對象屬性盆均,它的值會受原型對象屬性值的修改而修改塞弊,或者我們可以理解成,實例對象的‘指針’指向了原型對象的屬性中缀踪,只是地址引用而非值引用。所以虹脯,原型對象屬性的值更改了之后驴娃,實例對象的屬性自然也跟著更改
? 假使有這樣一種場景,在一個頁面上有多個請求循集,每個請求中有多個相同的參數(shù)值唇敞,在這些參數(shù)值中,有部分的參數(shù)是受控于一個控件咒彤,及修改一個控件疆柔,多個請求需要重新發(fā)起或數(shù)據(jù)更新。
? 對于這樣的屬性镶柱,我們即可設置一個構造器旷档,并將這個共用的屬性設置在原型對象的屬性上,如果控件觸發(fā)事件改變歇拆,我們只需要修改原型鏈中的值即可鞋屈,不需要修改每個請求的參數(shù)值
# 后語
? 業(yè)精于勤荒于嬉,行成于思毀于隨.
? 勵志成長的人故觅,共勉!