【javascript】javascript继承的多种方式

1, 原型链继承

	function Super() { }; 
	function Sub() { }; 
	Sub.prototype = new Super()	 

会出现的问题:
  a,原型对象的属性,如果是一个引用类型,那么所有实例都会共享这一个引用类型[比如是数组],某一个实例修改了该数组,那么其他实例获取该属性也会是改变之后的结果。
   b,第二个问题,没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

2,构造函数继承,在子类型的构造函数里面里面调用父类型的的构造函数,利用call 或者apply

      function Person(name, age) { 
      	  this.name = name, this.age = age 
      }
      function Worker(name, age, salary, phone) { 
          this.salary = salary
          this.phone = phone
          Person.call(this, name, age)
      }

会出现的问题:
   a, 子类型没有办法公用父类型的方法。属性其实只是表层上的引用。

3, 组合继承
方法采用原型继承【方法可以公用】,属性采用构造器的继承方式。

   
   function SuperType(name) { 
   	 this.name = name; 
   	 this.colors = ["red", "blue", "yellow"]
   }
   SuperType.prototype.sayName = function() { 
  	 console.log(this.name)
   }

   function SubType(name, age) { 
     SuperType.call(this, name); 
     this.age = age
   }
   SubType.prototype = new SuperType()
   SubType.prototype.sayAge = function() {
   	console.log(this.age)
   }

   var in1 = new SubType("k1", 18)
   in1.colors.push('black')
   in1.sayAge()
   in1.sayName()
   in1.colors // what is result ?

   var in2 = new SubType("k2", 28)
   in2.sayAge()
   in2.sayName()
   in2.colors // what is result ?

4,原型式继承【不是原型链继承】

     function object(o) {
          function F() {}
          F.prototype = o
          return new F()
     }

ES5 提供了Object.create()方法来规范原型链继承。这个方法和上面的object方法实现同样的功能,但是他提供第二个参数,第二个参数和Object.defineProperty(obj, 'name', { })的第三个参数,描述符是一样的。
例子:

    const obj = { 
    	name: 'kev', 
    	colors: ['yellow', 'green', 'blue'] 
    }
    const copyPaste = Object.create(obj, 
    	{ name:  { 
    		value: 'kevin',
    		enumerable: true
    	 }   
   })

5, 寄生式继承【以第4种原型式继承为基础】
寄生式继承和原型式继承紧密相关的一种思路,也是会调用Object.create方法,但是会以某种方式增强返回的对象。

    function createAnother(original) {
          // 这个object方法来自第4步
          const clone = object(original)
          clone.sayHi = function() {
              console.log('hi')
          }
          return clone
     }

第六种,寄生组合式继承
   其实到现在我认为第3种是最自然的,结合了原型和构造函数。但是问题来了:我们调用了两次超类SuperType的构造函数

   看完前面5种,我自己都认为常用的应该是第3种继承方式最好,结合原型和构造函数。但是存在问题:我们调用了两次超类SuperType的构造函数。

   第一次是指定SubType.prototype = new SuperType(),这时候SubType的原型对象上面会有两个属性,name,colors。
   第二次是实例化对象in1的时候,又调用了一次SuperType的构造函数,这时候对象in1又创建了实例属性name和colors。

   实际上我们只需要继承原型对象上面的方法,属性我们可以不在原型对象上面创建。

   概念来了,寄生组合式继承是指的是通过构造函数来继承属性,通过原型链的方式来继承方法。
   我们不用为了指定子类型SubType的原型(对象)而去调用父类或者超类的构造函数。避免子类的原型对象有多余的属性,我们可以利用Object.create() 或者手写的object()方法来返回一个对象tempObj,这个对象tempObj的原型tempObj.__proto__或者Object.getPrototypeOf(temoIbj) 都是等于SuperType.prototype的。

   关键总结:我们都是为了将SubType的原型对象设置为SuperType的实例,但是组合继承调用了构造函数,SubType的原型对象是有多余的属性的, 而且如果SuperType构造函数有一些副作用 比如写日志,修改状态,注册到其他对象,给this添加数据属性等,就会影响SubType的"后代"。 所以我们设置SubType的原型对象为另外一个特殊的对象,这个特殊的对象是通过Object.create(SuperType.prototype)来返回的,这个特殊对象没有多余的属性,但是.__proto__为SuperType.prototype, 继承了prototype.sayName方法。

   但是因为我们重写了prototype,失去了默认的constructor属性,这一步之后代码长下面这样:

   // 超类
   function SuperType(name) { 
      this.name = name; 
      this.colors = ["red", "blue", "yellow"] 
   }
   SuperType.prototype.sayName = function() { 
   	  console.log(this.name) 
   }
   
   // 子类
   function SubType(name, age) { 
   	  SuperType.call(this, name); 
   	  this.age = age 
   }
   // A 设置prototype
   SubType.prototype = Object.create(SuperType.prototype)
   // B 恢复默认的Constructor
   SubType.prototype.constructor = SubType
   SubType.prototype.sayAge = function() {
      console.log(this.age)
   }

我们将A和B下面的两句代码抽成一个方法:

  function inheritPrototype(subType, superType) {
    // or object(superType.prototype)
    const proto = Object.create(superType.prototype) 
    proto.constructor = subType
    subType.prototype = proto
  }

所以最后的结果是这样:

  // 超类
  function SuperType(name) { 
    this.name = name;
    this.colors = ["red", "blue", "yellow"] 
  }
  SuperType.prototype.sayName = function() { 
    console.log(this.name) 
  }

  // 子类
  function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age 
  }
  inheritPrototype(SubType, SuperType)
  SubType.prototype.sayAge = function() {
    console.log(this.age)
  }


等等—还没有完。。。

ES6 新增了一个方法Object.setPrototypeOf(obj, prototype), 我们可以直接设置原型对象【但是注意兼容性】,这样我们修改一下这个inheritPrototype方法:

    function inheritPrototype(subType, superType) {
      Object.setPrototypeOf(subType.prototype, superType.prototype)
      subType.constructor = subType
      subType.prototype.constructor = subType
    }

在这里插入图片描述

more:
之前关于prototype的一点记录

来源《js高级程序设计》第6章第三节
《你不知道的js》原型章节

posted on 2019-01-23 21:55  狂奔的冬瓜  阅读(227)  评论(0编辑  收藏  举报