JS原型繼承工做原理

當查找一個對象的屬性時,JS會向上遍歷原型鏈,直到找到給定的屬性名稱爲止,若是沒找到就是undefined。
前端

大多數的JS的實現用_proto_屬性來表示一個對象的原型鏈,如下代碼展現了JS引擎如何查找屬性。
函數

function getProperty(obj, prop) {
    if (obj.hasOwnProperty(prop))
        return obj[prop]

    else if (obj.__proto__ !== null)
        return getProperty(obj.__proto__, prop)

    else
        return undefined}

讓咱們舉一個比較簡單的例子,假設有個三維點,座標x、y、z,同時有print打印方法。測試

如今咱們建立一個對象point,具備x、y、z和print屬性,爲了能建立一個新的三維座標點,咱們須要建立一個新的對象,使得它的_proto_指向point,繼承point。相似C++中的OOP,例如point爲基類,建立新的point對象爲原先point的子類。this

var Point = {
    x: 0,
    y: 0,
    z: 0,
    print: function () { console.log(this.x, this.y, this.z); }};
    var p = {x: 10, y: 20, z: 30,__proto__: Point};p.print(); // 10 20 30

可是js工程師通常都不會這樣寫原型繼承,他們以下寫出:spa

function Point(x, y) {
    this.x = x;
    this.y = y;
    this.z = z;}
    Point.prototype = {
    print: function () { console.log(this.x, this.y, this.z); }};
    var p = new Point(10, 20, 30);
    p.print(); // 10 20 30

這裏涉及到了new運算符的工做原理prototype

new後面跟的不是類,而是構造函數 ,用new構造函數生成實例對象,有一個很明顯的缺點,就是每一個實例沒法共享同一個屬性和方法。在C++中,若是類中定義了一個static成員,那麼全部該類的實例都共享該成員。而JS中每個實例的對象都有本身屬性的副本,這樣比較浪費空間,並且沒法實現數據的共享。code

基於以上new構造函數的缺陷,JS創始人爲構造函數添加了prototype屬性對象

prototype屬性的引入

JS規定每個構造函數都有prototype屬性,該屬性指向另外一個對象,另外一個對象中全部的屬性和方法都會被構造函數的實例引用。繼承

這個屬性包含一個對象,全部須要共享的屬性和方法放入這個對象中,而不須要共享的屬性和方法放入構造函數中。實例對象一旦建立成功,就會自動引用prototype對象中的方法和屬性,即實例對象的屬性和方法分爲兩種,一種是本地的,即放入構造函數中的屬性和方法,一種是引用的,即放入prototype對象。例如如下代碼:
ci

function DOG(name){
    this.name = name;
  }
  DOG.prototype = { species : '犬科' };

  var dogA = new DOG('大毛');
  var dogB = new DOG('二毛');

  alert(dogA.species); // 犬科
  alert(dogB.species); // 犬科

在這個例子中,species屬性放入prototype對象中,那麼實例dogA和dogB共用species屬性。只要其中一個實例的species發生改變,則會影響全部實例的species

Prototype模式的驗證方法

isPrototypeOf

這個方法用來判斷prototype對象和某個實例之間的關係,例如

alert(DOG.prototype.isPrototypeOf(dogA)); //true
hasOwnProperty

每個實例對象都有一個該方法,用來判斷該實例中的某個屬性是來自本地屬性,仍是繼承自原型對象屬性。例如

alert(dogA.hasOwnProperty(name))//true
in運算符

in運算符能夠判斷某個屬性是否屬於實例對象,無論是本地屬性仍是繼承自原型對象屬性。如

alert("name" in dogA); //true
alert("species" in dogA); //true

in運算符還能夠遍歷某個對象的全部屬性,如

for (var prop in dogA) {
alert(prop);
}

測試

有一道阿里的在線前端筆試題,題目以下:

如今有以下的代碼:

var foo = 1;
function main(){
    console.log(foo);
    var foo = 2;
    console.log(this.foo)
    this.foo = 3;}

1.請給出如下兩種方式調用函數時,輸出的結果,並說明緣由

var m1 = main();
var m2 = new main();

2.若是須要var m1= main()產生的結果與前面m2產生結果同樣,應該如何改造main函數

第一題解答:首先根據JS的變量提高規則,能夠知道,全局的foo被main函數屏蔽了,main函數在內部定義了一個foo同名的變量,該變量在第一個console以前只定義而未賦值,故爲undefined(undefined有兩種狀況會出現,已定義未賦值,未定義)。而在第二個console的時候,this指向的是window,故輸出爲1

相關文章
相關標籤/搜索