JavaScript 常被描述爲一種基於原型的語言——每一個對象擁有一個原型對象,對象以其原型爲模板、從原型繼承方法和屬性。原型對象也可能擁有原型,並從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱爲原型鏈。瀏覽器
JavaScript 中使用 new 操做符,經過構造函數初始化新對象。咱們先使用構造函數建立一個對象。函數
function Ninja() { this.swung = true; } const ninja = new Ninja() console.log(ninja.swung) // true
在上面例子中,簡單的經過 Ninja 構造器實例一個對象。接下來咱們經過這個例子詳細展開。this
在 JavaScript 中,函數能夠有屬性。每一個函數都有一個特殊的屬性叫做原型(prototype)。例如:spa
function Ninja() { } Ninja.prototype.swung = true const ninja = new Ninja() console.log(ninja.swung) // true
上面例子中 Ninja 函數的 prototype 屬性指向的是一個對象,被構建的實例對象 ninja 的原型也指向這個對象。關於 ninja 原型則在下面詳細展開:prototype
在 JavaScript 中,實例對象的原型屬性是內置屬性(使用標記 [[prototype]])。ES6以前 ECMAScript 標準沒有提供接口訪問這個屬性的,可是許多瀏覽器都實現了 JavaScript 非標準的內置屬性__proto__來訪問對象屬性。3d
function Ninja() { this.swung = true; } const ninja = new Ninja() console.log(ninja.__proto__ === Ninja.prototype) // true
每個JavaScript對象(除了null)都具備__proto__屬性,這個屬性會指向該對象的原型。code
接着在看下面這個例子:對象
function Ninja() { this.swung = true; } const ninja = new Ninja() console.log(Object.getPrototypeOf(ninja) === Ninja.prototype) // true
ES6開始,ECMAScript 標準提供了 Object.getPrototypeOf() 和 Object.setPrototypeOf() 訪問器來訪問和設置原型。__proto__它本質上是一個內部屬性,而不是一個正式的對外的 API,只是因爲瀏覽器普遍支持,才被加入了 ES6,因此建議不要使用。blog
上面例子中構造函數原型和實例對象原型是相同的,它們的原型中還包含一個特殊的屬性 constructor 用於指向構造函數。繼承
每一個原型都有一個 constructor 屬性指向關聯的構造函數。
function Ninja() { } console.log(Ninja.prototype.constructor === Ninja) // true
當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止。
舉個例子:
function Ninja() { } Ninja.prototype.swung = true const ninja = new Ninja() ninja.swung = false console.log(ninja.swung) // false delete ninja.swung console.log(ninja.swung) // true
原型鏈的最頂層即原型鏈的終點Object.prototype
爲null,咱們能夠打印:
console.log(Object.prototype.__proto__) // null
null 表示「沒有對象」,即該處不該該有值。
因此 Object.prototype.__ proto__ 的值爲 null 跟 Object.prototype 沒有原型,其實表達了一個意思。
因此查找屬性的時候查到 Object.prototype 就中止查找了。