前言
在寫復雜的 JavaScript 應用之前,充分理解原型鏈繼承的工作方式是每個 JavaScript 程序員必修的功課。
JavaScript中并沒有類(class);Js是基于原型(prototype-based)來實現的面向對象(OOP)的編程范式的,但并不是所有的對象都擁有prototype這一屬性
var a = {},b = 'shi';
var fn = function(){};
a.prototype // undefined
b.prototype // undefined
fn.prototype // Object {constructor: function}
prototype
屬性只有 function
對象定義時才有,函數本身也是對象。想要明白原型問題我們先明白幾個基本概念。
一、function、Function、Object和{}
function
是JavaScript的關鍵字,用于定義函數變量,一般有2種定義方式
function f1(){
console.log('This is function f1!');
}
typeof(f1); //=> 'function'
var f2 = function(){
console.log('This is function f2!');
}
typeof(f2); //=> 'function'
Function
是函數類型實例的構造函數(constructor), 本質也是函數,類似還有Object
或String
、Number
等,都是Js內置類型實例的構造函數,構造函數主要作用就是創建相應類型的實例,實現原型鏈。
var f3 = new Function("console.log('This is function f3!');");
f3(); //=> 'This is function f3!'
typeof(f3); //=> 'function'
typeof(Function); //=> 'function'
Object,它用于生成對象類型,其簡寫形式為{},其實和function和Function的關系類似。
var o1 = new Object();
typeof(o1); //=> 'object'
var o2 = {};
typeof(o2); //=> 'object'
typeof(Object); //=> 'function'
二、prototype 和 _proto_
prototype
屬性只有函數類型對象才有,上面說的很清楚了。而__proto__
是所有JavaScript對象都內置的屬性[[Prototype]],而這個屬性指向構造函數(類似父類)的prototype
屬性,從而繼承屬性.
舉個例子:
var Person = function(){};
Person.prototype.type = 'Person';
Person.prototype.maxAge = 100;
var p = new Person();
console.log(p.maxAge);
p.name = 'rainy';
// 修正Person.prototype.constructor為Person本身
Person.prototype.constructor === Person; //=> true
p.__proto__ === Person.prototype; //=> true
console.log(p.prototype); //=> undefined
一圖勝千言
Person是一個函數類型的變量,因此自帶了prototype屬性,prototype屬性中的constructor又指向Person本身;通過new關鍵字生成的Person類的實例p1,通過__proto__
屬性指向了Person的原型。
注意:
遵循ECMAScript標準,someObject.[[Prototype]] 符號是用于指派 someObject 的原型。這個等同于 JavaScript 的__proto__
屬性。從 ECMAScript 6 開始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()訪問器來訪問。
三、原型鏈
原型鏈是基于 __proto__
的。JavaScript 對象有一個指向一個原型對象的鏈。當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依此層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
舉例說明:
// Node
var Obj = function(){};
var o = new Obj();
o.__proto__ === Obj.prototype; //=> true
o.__proto__.constructor === Obj; //=> true
Obj.__proto__ === Function.prototype; //=> true
Obj.__proto__.constructor === Function; //=> true
Function.__proto__ === Function.prototype; //=> true
Object.__proto__ === Object.prototype; //=> false
Object.__proto__ === Function.prototype; //=> true
Function.__proto__.constructor === Function;//=> true
Function.__proto__.__proto__; //=> {}
Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true
o.__proto__.__proto__.__proto__ === null; //=> true
來張圖更直觀一些
到了這里,其實有個問題大家沒注意,new
關鍵詞到底起了什么作用呢?
其實開頭的圖已經給出答案:new
關鍵詞的最主要的作用就是完成上圖所示實例與父類原型之間關系的串接,并創建一個新的對象.
有興趣深入看這里:JS 的 new 到底是干什么的?
文筆有限,才疏學淺,文中若有不對之處,還望告知。
個人博客
參考文章