前言
瞭解JavaScript的原型與原型鏈有助於我們更深層次的理解這門語言,看過很多相關的文章,寫的都很好,下面是根據自己的理解,一步步揭開原型與原型鏈
正文
一、資料型別
在JavaScript中有6種資料型別: String, Number, Boolean, Undefined, Null, Object。(Symbol暫時不討論),其中String, Number, Boolean, Undefined, Null屬於值型別,Object屬於引用型別。
二、typeof操作符
先來看看使用typeof操作符能返回那些資料型別:
typeof undefined; // 'undefined'
typeof 100; // 'number'
typeof '123'; //'string'
typeof true; //'boolean'
typeof null; // 'object'
typeof {}; // 'object'
typeof []; // 'object'
typeof function() {}; // 'function'
typeof /\w/g; 'object'
typeof new String(); // 'object'
typeof new Number(); // 'object'
typeof new Date(); // 'object'
// ...
複製程式碼
除了string, number, undefined, boolean基本型別外,函式,物件,正則,null, new String(), new Number(), new Date()都是屬於物件,上面可以看出typeof無法檢測出引用型別的資料型別。
所以typeof一般用來檢測基本型別,引用型別的具體資料型別一般都用instanceof操作符,但是要弄清楚其中的原理, 首先需要知道什麼是物件?
三、什麼是物件
簡單的說,一切引用型別都是物件,物件是屬性的集合
四、物件與函式的關係
我們平時開發中大多數情況下會這樣寫:
var obj = {id: 0, name: 'abc'};
var arr = [1, 2, 3];
複製程式碼
上面的寫法是一種語法糖,其本質是這樣:
var obj = new Object();
obj.id = 0;
obj.name = 'abc';
var arr = new Array(1, 2, 3);
複製程式碼
可以看出,obj和arr都是由函式用過new操作符建立,在這個過程中,經歷了4個階段:
- 建立一個新物件;
- 將函式的作用域賦給新物件;
- 執行函式中的程式碼;
- 返回新物件。
在JavaScript的內建物件中,String, Object, Array, RegExp, Date, Function 等物件都是由函式建立。函式建立了物件,函式屬於物件,函式自己建立了自己。是時候該prototype上場了。
五、prototype原型
每一個函式都有一個prototype屬性,屬性值是一個Object例項物件, 且該物件預設都有一個constructor屬性,指向函式本身
function Foo() {};
Foo.prototype instanceof Object // true
Foo.prototype.constructor === Foo; // true
複製程式碼
Object已經為我們證實了這一點
那麼先來實現一下:
function User(name) {
this.id = 0;
this.name = name;
};
User.prototype.getUserName = function() {
return this.name;
};
var user = new User('xm', 10);
user.getUserName(); // xm
複製程式碼
上面的程式碼中,User是一個函式,user是通過對User函式使用new操作符建立出來的物件,並且能直接呼叫定義在User函式原型中的方法。可以把這個user列印出來看一下
發現這個物件下面有一個__proto__的屬性。這個__proto__就是物件身上的隱式原型。六、__proto__隱式原型
每一個物件都有一個隱式原型__proto__ , 指向該物件建構函式的prototype的屬性
//還是上面的程式碼
user.__proto__ === User.prototype; // true
複製程式碼
那麼這裡也就證實了定義在建構函式prototype原型中的方法和屬性是被他的例項所共享的。 由於函式的prototype屬性是一個物件,即Object的例項,所以,函式的prototype也有一個隱式原型__proto__,指向Object.prototype,此時就有一個特例,即:
Object.prototype.__proto__ === null; // true
複製程式碼
同時也就說明不能在沿著__proto__這條鏈往下走了,也就是原型鏈的終點。
七、原型鏈
當試圖得到一個物件的屬性時,如果這個物件本身沒有這個屬性,那麼會沿著這個物件的__proto__這條鏈去找,這就是原型鏈, 原型鏈的終點是null
如圖所示:
八、instanceof操作符
instanceof操作符用來檢測一個建構函式的prototype是否在一個物件的原型鏈上
先來看看使用instanceof能返回那些引用型別的資料型別:
(new String('123')) instanceof String; // true
(new String('123')) instanceof Object; // true
(new Number(1)) instanceof Number; // true
(new Number(1) instanceof Object; // true
(function() {}) instanceof Function; // true
(function() {}) instanceof Object; // true
(new Date()) instanceof Date; // true
(new Date()) instanceof Object; // true
(/\w/g) instanceof RegExp; // true
(/\w/g) instanceof Object; // true
[] instanceof Array; // true
[] instanceif Object; // true
({}) instanceof Object; // true
Object instanceof Function; // true
Function instanceof Object; // true
Function instanceof Function; // true
// ...
複製程式碼
根據instanceof操作符的檢測規則,來分析一下後面3個:
Object.prototype === Function.prototype.__proto__; // true
Function.prototype.__proto__ === Object.prototype; // true
Function.protoType === Function.__proto__; // true
複製程式碼
瞭解了原型鏈,instanceof操作符的檢測的過程也就知道了。
總結
- 所有函式都有一個prototype屬性,屬性值是一個Object例項物件,即該建構函式例項的原型。
- 所有物件都有一個__proto__隱式屬性,指向這個物件的建構函式的prototype。
- 當試圖得到一個物件的屬性時,如果這個物件本身沒有這個屬性,那麼會沿著它的__proto__這條鏈去找,這條鏈就是原型鏈。
- 原型鏈的終點是null
如果有遺漏或者不對的地方,還請指正,先謝過~~~///(^v^)\~~