javascript的继承

//8.继承

构造函数方式继承

构造函数方式继承的缺点:构造函数的方法会在实例化的时候多次执行,没有必要。

    //8.1构造函数方式继承
    function SuperType1(){
        this.property = true
        this.arr = [1,2,3]
        this.fuc = function () {
            console.log('just test')
        }
    }
    function SubType1() {
        SuperType1.call(this)
        this.subproperty = false
    }
    var obj1 = new SubType1()
    var obj2 = new SubType1()
    obj1.arr.push(4)
    console.log(obj1.arr)//[1, 2, 3, 4]     引用类型没有互相污染,但是fuc方法每次实例话都会新生成一次,没必要。
    console.log(obj2.arr)//[1, 2, 3]     引用类型没有互相污染,但是fuc方法每次实例话都会新生成一次,没必要。

原型方式继承

原型方式继承缺点:如果父级构造函数内有引用类型,如果此时有两个实例对象,其中一个实例对象改变了引用类型的值,则另一个实例对象的引用类型值也会相应改变。这不是我们期望的。

    //8.2原型方式继承 (为什么原型继承,因为构造函数方式继承的话,每次new一个实例,相同的方法都会重新生成一次,没有必要)
    function SuperType2(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType2.prototype.fuc = function(){//将构造函数继承里的fuc提出来到原型继承里
        console.log('just test')
    }
    function SubType2(){
        this.subproperty = false
    }
    SubType2.prototype = new SuperType2()//将SubType2的prototype设置为需要继承的构造函数的实例
    var obj3 = new SubType2()
    var obj4 = new SubType2()
    obj3.arr.push(4)
    console.log(obj3.arr)//[1, 2, 3, 4]     原型链继承方式,会让引用类型的值发生改变互相污染
    console.log(obj4.arr)//[1, 2, 3, 4]     原型链继承方式,会让引用类型的值发生改变互相污染
    obj4.property = false
    console.log(obj3.property)//true    基本类型值不会污染
    console.log(obj4.property)//false   基本类型值不会污染

组合方式继承(构造函数+原型链)

由于构造函数方式继承会导致函数内方法多次实例化(没有必要),原型链继承又有可能导致引用类型值的改变。所以现在考虑将两者相结合。

    //8.3 构造函数+原型链组合方式继承(思路:把属性放在构造函数里,把方法提出到prototype里,子构造函数的protoype指向父构造函数的实例)
    function SuperType3(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType3.prototype.fuc = function(){//将构造函数继承里的fuc提出来到原型继承里
        console.log('just test')
    }
    function SubType3(){
        //继承属性
        SuperType3.call(this)//等价于下方注释部分,但由于是继承,所以要这样写,因为大多数时候你并不知道父构造函数里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType3.prototype = new SuperType3()
    var obj5 = new SubType3()
    var obj6 = new SubType3()
    obj5.arr.push(4)
    console.log(obj5.arr)//[1, 2, 3, 4]     引用类型放在构造函数内,没有对其他实例造成污染
    console.log(obj6.arr)//[1, 2, 3]        引用类型放在构造函数内,没有对其他实例造成污染

组合方式优化1

上述组合方式继承还有问题,就是:
(1)SubType3.prototype = new SuperType3()时会调用SuperType3;
(2)SubType3内部执行SuperType3.call(this)时又会调用一次SuperType3。

//8.4 组合方式优化1
    function SuperType4(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType4.prototype.fuc = function(){//将构造函数继承里的fuc提出来到原型继承里
        console.log('just test')
    }
    function SubType4(){
        //继承属性
        SuperType4.call(this)//等价于下放注释部分,但由于是继承,所以要这样写,因为大多数时候你并不知道父构造函数里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType4.prototype =  SuperType4.prototype
    var obj7 = new SubType4()
    var obj8 = new SubType4()
    obj7.arr.push(4)
    console.log(obj7.constructor)//ƒ SuperType4()

组合方式优化2

上述优化1依然有问题,虽然SuperType4只调用了一次,但是

...
SubType4.prototype =  SuperType4.prototype
...

这里又会导致SubType4实例化对象的constructor实际是指向SuperType4的。
所以,我们将此处修改为:

...
SubType4.prototype =  SuperType4.prototype
SubType4.prototype.constructor = SubType4
...
//加Object.create()前

这样SubType4的原型对象的contructor属性就指向SubType4了。但这样就够了吗?我们发现,此时SuperType4.prototype.constructor也指向了SubType4,这不是我们期望的。
所以还需要做以下这步:

...
SubType4.prototype =  Object.create(SuperType4.prototype)
SubType4.prototype.constructor = SubType4
...
//加Object.create()后

对比一下前后两者区别:

//console.log(SuperType4.prototype)
{fuc: ƒ, constructor: ƒ}
   fuc:ƒ ()
   constructor:ƒ SuperType4()  //若直接SubType4.prototype =  SuperType4.prototype,SuperType4将会被替换成SubType4
   __proto__:Object

//console.log(Object.create(SuperType4.prototype))
SuperType4 {}
   __proto__:  //通过SubType4.prototype =  Object.create(SuperType4.prototype)创建一个新的对象,则会在__proto__平级处会增加一个constructor: SubType4。下方的SuperType4并不会被替换。
      fuc:ƒ ()
      constructor:ƒ SuperType4()
      __proto__:Object

上述代码中,Object.create(SuperType4.prototype)基本等价于:

function object(o){//返回一个函数的实例,该实例的原型对象等于传入的参数
  function F(){};
  F.prototype = o;
  return new F();
}
object(SuperType4.prototype)

也就是说,Object.create()方法会创建一个新的对象,对象的prototype即为传入的参数。

最终,我们得到了最优的继承方式:

    function SuperType4(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType4.prototype.fuc = function(){//将构造函数继承里的fuc提出来到原型继承里
        console.log('just test')
    }
    function SubType4(){
        //继承属性
        SuperType4.call(this)//等价于下放注释部分,但由于是继承,所以要这样写,因为大多数时候你并不知道父构造函数里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType4.prototype =  Object.create(SuperType4.prototype)
    SubType4.prototype.constructor = SubType4
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容