1. 程式人生 > >深入理解JavaScript系列(5):強大的原型和原型鏈

深入理解JavaScript系列(5):強大的原型和原型鏈

JavaScript 不包含傳統的類繼承模型,而是使用 prototypal 原型模型。
雖然這經常被當作是 JavaScript 的缺點被提及,其實基於原型的繼承模型比傳統的類繼承還要強大。實現傳統的類繼承模型是很簡單,但是實現 JavaScript 中的原型繼承則要困難的多。

<script>
     //【1】base
    var BaseCalculator =function () {
        //為每個實列都宣告一個小數位數
        this.decimalDigits=2;
    }


    //【2】 使用原型擴充套件兩個物件方法
    BaseCalculator.prototype.add=function
(x, y) {
return x+y; } BaseCalculator.prototype.subtract=function (x, y) { return x-y; } // 【3】定義一個類 (1)讓Calculator整合它的add(x,y)和subtract(x,y)這2個function Calculator=function () { //為每個實列都宣告一個整數數字 this.tax=5; } // (2)Calculator的原型是BaseCalculator它的實列,所以不管你建立多少個Calculator物件例項,他們的原型指向的都是同一個例項
Calculator.prototype=new BaseCalculator(); var cal=new Calculator(); console.log(cal.add(1,1)) console.log(cal.subtract(2,1)) console.log(cal.tax) console.log(cal.decimalDigits)
</script>
3)通過將BaseCalculator的原型賦給Calculator的原型,這樣你在Calculator的例項上就訪問不到那個decimalDigits值了,如果你訪問如下程式碼,那將會提升出錯。
不想讓Calculator訪問BaseCalculator的建構函式裡宣告的屬性值


var
Calculator = function () { this.tax= 5; }; Calculator.prototype = BaseCalculator.prototype;

重寫原型:
在使用第三方JS類庫的時候,往往有時候他們定義的原型方法是不能滿足我們的需要,但是又離不開這個類庫,所以這時候我們就需要重寫他們的原型中的一個或者多個屬性或function,我們可以通過繼續宣告的同樣的add程式碼的形式來達到覆蓋重寫前面的add功能,程式碼如下:

     // 重寫原型:
    Calculator.prototype.add=function (x, y) {
        return x+y+this.tax;
    }
    var calc=new Calculator();
    console.log(calc.add(1,1))

這樣,我們計算得出的結果就比原來多出了一個tax的值,但是有一點需要注意:那就是重寫的程式碼需要放在最後,這樣才能覆蓋前面的程式碼。

原型鏈

這裡寫圖片描述


 function Foo() {
        this.value = 24;
    }

    Foo.prototype = {
        method: function () {
            console.log('Foo method')
        }

    }

    function Bar() {

    }

    Bar.prototype = new Foo();//【1】bar原型為Foo
    Bar.prototype.foo = 'hello world';

    //修正bar.prototype.constructor為bar本身
    Bar.prototype.constructor = Bar;

    var test = new Bar();

    //原型鏈


    test.method();
    console.log(test.value += 6);
    console.log(test.value);
    console.log(test.foo)
    console.log('------------------------test for in------------------')
    for (var i in test) {
        console.log(test[i])
    }

上面的例子中,test 物件從 Bar.prototype 和 Foo.prototype 繼承下來;因此,它能訪問 Foo 的原型方法 method。同時,它也能夠訪問那個定義在原型上的 Foo 例項屬性 value。需要注意的是 new Bar() 不會創造出一個新的 Foo 例項,而是重複使用它原型上的那個例項;因此,所有的 Bar 例項都會共享相同的 value 屬性

屬性查詢:
當查詢一個物件的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止,到查詢到達原型鏈的頂部 - 也就是 Object.prototype - 但是仍然沒有找到指定的屬性,就會返回 undefined

 //屬性查詢
    console.log('-------------------------------屬性查詢---------------------')

    function foo1() {
        this.add=function (x, y) {
            return x+y;
        }
    }

    foo1.prototype.add=function (x, y) {
        return x+y+10;
    }

    Object.prototype.subtract=function (x, y) {
        return x-y;
    }

    var fo1=new foo1();


    var a=fo1.add(1,2)
    console.log(a)

通過程式碼執行,我們發現subtract是安裝我們所說的向上查詢來得到結果的,但是add方式有點小不同,這也是我想強調的,就是屬性在查詢的時候是先查詢自身的屬性,如果沒有再查詢原型,再沒有,再往上走,一直插到Object的原型上,所以在某種層面上說,用 for in語句遍歷屬性的時候,效率也是個問題

還有一點我們需要注意的是,我們可以賦值任何型別的物件到原型上,但是不能賦值原子型別的值,比如如下程式碼是無效的:
這裡寫圖片描述

hasOwnProperty函式:
hasOwnProperty是Object.prototype的一個方法
他能判斷一個物件是否包含 自定義屬性 而不是原型鏈上的屬性
JavaScript 中唯一一個處理屬性但是不查詢原型鏈的函式。

    Object.prototype.bar = 11;
    var foo = {goo: undefined};

    console.log(foo.bar)//11

    console.log('bar' in foo)//true

    console.log(foo.hasOwnProperty('bar'))////false

    console.log(foo.hasOwnProperty('goo'))//true

只有 hasOwnProperty 可以給出正確和期望的結果,這在遍歷物件的屬性時會很有用。 沒有其它方法可以用來排除原型鏈上的屬性,而不是定義在物件自身上的屬性。

但有個噁心的地方是:JavaScript 不會保護 hasOwnProperty 被非法佔用,因此如果一個物件碰巧存在這個屬性,就需要使用外部的 hasOwnProperty 函式來獲取正確的結果。

console.log('------------------不被保護的 hasOwnProperty----------------')

    var foo = {
        hasOwnProperty: function () {
            return false;
        }, bar: 'Here be dragons'
    };
    var bl=foo.hasOwnProperty('bar');
    console.log(bl);
    console.log('------------------解決方法----------------');
    // 使用{}物件的 hasOwnProperty,並將其上下為設定為foo
   var bl1= ( {}).hasOwnProperty.call(foo, 'bar'); // true
    console.log(bl1)

當檢查物件上某個屬性是否存在時,hasOwnProperty 是唯一可用的方法。同時在使用 for in loop 遍歷物件時,推薦總是使用 hasOwnProperty 方法,這將會避免原型物件擴充套件帶來的干擾,我們來看一下例子:
這裡寫圖片描述

這裡寫圖片描述