詳解JavaScript中基于原型prototype的繼承特性

字號:


    這篇文章主要介紹了詳解JavaScript中基于原型prototype的繼承特性,JavaScript中缺少類等面向?qū)ο蟮闹匾匦?因而談到繼承也顯得十分古怪...需要的朋友可以參考下
    JavaScript 中的繼承比較奇葩,無法實(shí)現(xiàn)接口繼承,只能依靠原型繼承。
    原型鏈
    原型就是一個(gè)對象,通過構(gòu)造函數(shù)創(chuàng)建出來的實(shí)例會有指針指向原型得到原型的屬性和方法。這樣,實(shí)例對象就帶有構(gòu)造函數(shù)的屬性方法和原型的屬性方法,然后將需要繼承的構(gòu)造函數(shù)的原型指向這個(gè)實(shí)例,即可擁有這個(gè)實(shí)例的所有屬性方法實(shí)現(xiàn)繼承。
    看下面演示代碼:
    //聲明超類,通過構(gòu)造函數(shù)和原型添加有關(guān)屬性和方法
    function Super(){
      this.property = true;
    }
    Super.prototype.getSuperValue = function() {
      return this.property;
    };
    //聲明子類的構(gòu)造函數(shù)
    function SubType() {
      this.subproperty = false;
    }
    //將子類的原型指向超類的實(shí)例,得到超類的一切
    SubType.prototype = new Super();
    SubType.prototype.constructor = SubType;
    SubType.prototype.getSubValue = function(){
      return this.subproperty;
    };
    //由子類創(chuàng)建對象,測試是否繼承超類方法和屬性
    var instance = new SubType();
    console.log(instance.getSuperValue());
    所有函數(shù)的默認(rèn)原型都是 Object 的實(shí)例,因此默認(rèn)原型都會包含一個(gè)內(nèi)部指針,指向 Object.prototype。
    使用 instanceof 和 isPrototypeOf 可以確定原型和實(shí)例的關(guān)系:
    instance instanceof Object;
    Object.prototype.isPrototypeOf(instance);
    使用原型鏈的時(shí)候,需要謹(jǐn)慎的定義方法。子類需要重寫超類型的某個(gè)方法或者擴(kuò)充,一定要放在替換原型的語句后面,這樣才能生效。此外,通過原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對象字面量創(chuàng)建原型方法,這樣會重寫原型鏈:
    ......
    SubType.prototype = new Super();
    SubType.prototype = {
      ....
    };
    這會更換指針指向新對象,從而重寫了原型鏈。
    原型鏈的繼承方法是有缺陷的,主要有兩個(gè)問題:
    1,來自包含引用類型值的原型,會被所有實(shí)例共享。
    前面文章介紹過包含引用類型值的原型屬性會被所有實(shí)例共享,一個(gè)實(shí)例修改,其他實(shí)例會隨之改變,因此需要在構(gòu)造函數(shù)中定義屬性。而原型鏈繼承的時(shí)候,無論超類中屬性是在構(gòu)造函數(shù)還是原型中定義,全部都變成了實(shí)例對象被子類繼承,從而對子類的實(shí)例產(chǎn)生影響。
    2,創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。
    原型鏈的繼承,直接將子類原型指向超類的實(shí)例,這時(shí)候可以向超類傳遞參數(shù)。但是當(dāng)子類創(chuàng)建實(shí)例的時(shí)候,只能向子類的構(gòu)造函數(shù)傳遞參數(shù),而不能向超類的構(gòu)造函數(shù)傳遞參數(shù)。
    因此實(shí)際應(yīng)用中,很少單獨(dú)使用原型鏈。
    相關(guān)的一些代碼實(shí)踐
    鑒別一個(gè)原型屬性
    function hasPrototypeProperty(object, name) {
      return name in object && !object.hasOwnProperty(name);
    }
    在構(gòu)造函數(shù)中使用原型對象
    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
      constructor: Person,
      sayName: function () {
        console.log(this.name);
      },
      toString: function() {
      }
    };
    var person1 = new Person('Nicholas');
    var person2 = new Person('Greg);
    console.log(person1 instanceof Person); // true
    console.log(person1.constructor === Person); // true
    console.log(person1.constructor === Object); // false
    console.log(person2 instanceof Person); // true
    console.log(person2.constructor === Person); // true
    console.log(person2.constructor === Object); // false
    對象繼承
    var person1 = {
      name: 'Nicholas',
      sayName: function () {
        console.log(this.name);
      }
    };
    var person2 = Object.create(person1, {
      name: {
        configurable: true,
        enumerable: true,
        value: 'Greg',
        writable: true
      }
    });
    person1.sayName(); // Nicholas
    person2.sayName(); // Greg
    console.log(person1.hasOwnProperty('sayName')); // true
    console.log(person1.isPropertyOf(person2)); // true
    console.log(person2.hasOwnProperty('sayName')); // false
    模塊模式
    var person = (function () {
      var age = 25;
      function getAge() {
        return age;
      }
      function growOlder() {
        age++;
      }
      return {
        name: 'Nicholas',
        getAge: getAge,
        growOlder: growOlder
      };
    }());
    作用域的構(gòu)造函數(shù)
    function Person(name) {
      this.name = name;
    }
    Person.prototype.sayName = function() {
      console.log(this.name);
    };
    var person1 = Person('Nicholas');
    console.log(person1 instanceof Person); // false
    console.log(typeof person1); // undefined
    console.log(name); // Nicholas