继承

第一种继承方式

function Animal () {
      this.bigName = 'animal'
    }
    Animal.prototype.eat = function () {
      console.log('eat')
    }
    function Bear (name) {
      Animal.call(this, name)
      this.name = name
    }
   
    Bear.prototype = new Animal()
    Bear.prototype.constructor = Bear
    Bear.prototype.say = function () {
      console.log('say')
    }
    let bear = new Bear('small Bear')

Object.create

  • Object.create和Object.setPrototypeOf的区别?
    1.Object.create的用法
// 第一个参数为原型链上的属性
// 第二个属性为实例上的属性
var obj = Object.create({protytypeProp: 666}, {
  p1: {
    value: 123,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  p2: {
    value: 'abc',
    enumerable: true,
    configurable: true,
    writable: true,
  }
});

Object.create实现

function create (prototype) {
    const Noop = function () {}
    Noop.prototype = prototype
    return new Noop()
}

Object.create在继承中的应用

function Animal () {
      this.bigName = 'animal'
    }
    Animal.prototype.eat = function () {
      console.log('eat')
    }
    function Bear (name) {
      Animal.call(this, name)
      this.name = name
    }
   
    Bear.prototype = Object.create(Animal.prototype, {
       constructor: {
         value: Bear
       }
    })
    Bear.prototype.say = function () {
      console.log('say')
    }
    let bear = new Bear('small Bear')

Object.setPrototypeOf

1.Object.setPropertyOf的用法

// .Object.setPrototypeOf将一个对象设置为另一个对象的原型对象。
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);

Object.getPrototypeOf(a) === b // true
a.x // 1

Object.setPrototypeOf的实现

// 仅适用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
  obj.__proto__ = proto;
  return obj; 
}

Object.setPrototypeOf在继承中的应用

function Animal () {
      this.bigName = 'animal'
    }
    Animal.prototype.eat = function () {
      console.log('eat')
    }
    function Bear (name) {
      Animal.call(this, name)
      this.name = name
    }
    Object.setPrototypeOf(Bear.prototype, Animal.prototype)
    Bear.prototype.say = function () {
      console.log('say')
    }
    let bear = new Bear('small Bear')

ES6的继承

子类必须在constructor方法中调用super方法,否则新建实例时会报错。如果子类不写constructor方法,内部会自动补充一个空的构造函数。

class Parent {}
class Child extends Parent {
    
}
let c = new Child()
Child.prototype.__proto__ === Parent.prototype

super

super()在这里相当于A.prototype.constructor.call(this)。

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

ES6和ES5的区别

  1. ES5先是在构造函数中得到了this对象,再调用Parent.call(this, ...args)将父类的属性添加到 this上。ES6得先调用super得到this指向,再将自己的属性附加到this上。

  2. ES6 可以继承原生数据结构,而ES5不可以,原因是原生数据类型会忽略传入的this

function myArray () {
  Array.call(this)
}
let arr = new myArray()
function myArray () {
  Array.call(this)
}
myArray.prototype = Object.create(Array.prototype, {
    constructor: {
      value: myArray,
      writeable: true,
      enumerable: true,
      writeable: true
    }
})
let arr = new myArray()
arr[0] = 1
arr.length === 0 //true

注意点

  1. 因为ES6继承的机制(上面描述的),所以需要先调用super,再使用this,否则会报错
class Parent {}
class Child extends Parent {
    constructor (name) {
      this.name = name
      super()
      this.name = name
    }
}
new Child() // Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  1. 继承后的关系为什么要这样设计呢?
class P1 {}
class P2 extends P1 {}
class P3 extends P2 {}
Object.getPrototypeOf(P3) === P2 // true
P3.__proto__ === P2 // true
P2.__proto__ === P1 // true
P3.prototype.__proto__ === P2.prototype // true
P2.prototype.__proto__ === P1.prototype // true
// Object.getPrototypeOf模拟
function getPrototypeOf (obj) {
  return obj.__proto__
}

因为同时要继承静态属性

class A {
}

class B {
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

const b = new B();
  1. super作为方法只能用在子类的构造函数中调用,用在其他地方就会报错
  2. super还能作为对象使用,构造函数或者普通函数都可以。这时候super指的就是Parent.prototype
  3. super作为对象还能在静态方法中调用,这时候super指向的就是父类自身,而不是Parent.prototype
  4. 看一个babel转义后的例子,里面的继承方式其实很简单。
    代码
class P {
  constructor () {
    this.x =1
  }
  aaa () {
    console.log('aaa')
  }
}
P.x = 1

class C extends P {
    
}

let v = new C ()

编译后

 "use strict";

      function _typeof(obj) {
        if (
          typeof Symbol === "function" &&
          typeof Symbol.iterator === "symbol"
        ) {
          _typeof = function _typeof(obj) {
            return typeof obj;
          };
        } else {
          _typeof = function _typeof(obj) {
            return obj &&
              typeof Symbol === "function" &&
              obj.constructor === Symbol &&
              obj !== Symbol.prototype
              ? "symbol"
              : typeof obj;
          };
        }
        return _typeof(obj);
      }

      function _possibleConstructorReturn(self, call) {
        if (
          call &&
          (_typeof(call) === "object" || typeof call === "function")
        ) {
          return call;
        }
        return _assertThisInitialized(self);
      }

      function _assertThisInitialized(self) {
        if (self === void 0) {
          throw new ReferenceError(
            "this hasn't been initialised - super() hasn't been called"
          );
        }
        return self;
      }

      function _getPrototypeOf(o) {
        _getPrototypeOf = Object.setPrototypeOf
          ? Object.getPrototypeOf
          : function _getPrototypeOf(o) {
              return o.__proto__ || Object.getPrototypeOf(o);
            };
        return _getPrototypeOf(o);
      }

      function _inherits(subClass, superClass) {
        if (typeof superClass !== "function" && superClass !== null) {
          throw new TypeError(
            "Super expression must either be null or a function"
          );
        }
        subClass.prototype = Object.create(superClass && superClass.prototype, {
          constructor: { value: subClass, writable: true, configurable: true }
        });
        // 注意,这里用于继承父类的静态属性
        if (superClass) _setPrototypeOf(subClass, superClass);
      }

      function _setPrototypeOf(o, p) {
        _setPrototypeOf =
          Object.setPrototypeOf ||
          function _setPrototypeOf(o, p) {
            o.__proto__ = p;
            return o;
          };
        return _setPrototypeOf(o, p);
      }

      function _instanceof(left, right) {
        if (
          right != null &&
          typeof Symbol !== "undefined" &&
          right[Symbol.hasInstance]
        ) {
          return right[Symbol.hasInstance](left);
        } else {
          return left instanceof right;
        }
      }

      function _classCallCheck(instance, Constructor) {
        if (!_instanceof(instance, Constructor)) {
          throw new TypeError("Cannot call a class as a function");
        }
      }

      function _defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
          var descriptor = props[i];
          descriptor.enumerable = descriptor.enumerable || false;
          descriptor.configurable = true;
          if ("value" in descriptor) descriptor.writable = true;
          Object.defineProperty(target, descriptor.key, descriptor);
        }
      }

      function _createClass(Constructor, protoProps, staticProps) {
        if (protoProps) _defineProperties(Constructor.prototype, protoProps);
        if (staticProps) _defineProperties(Constructor, staticProps);
        return Constructor;
      }

      var P =
        /*#__PURE__*/
        (function() {
          function P() {
            _classCallCheck(this, P);

            this.x = 1;
          }

          _createClass(P, [
            {
              key: "aaa",
              value: function aaa() {
                console.log("aaa");
              }
            }
          ]);

          return P;
        })();

      P.x = 2;

      var C =
        /*#__PURE__*/
        (function(_P) {
          _inherits(C, _P);

          function C() {
            _classCallCheck(this, C);

            return _possibleConstructorReturn(
              this,
              _getPrototypeOf(C).apply(this, arguments) // 在这里继承实例属性,注意传进去的是C,所以_getPrototypeOf得到的是P
            );
          }

          return C;
        })(P);

      var v = new C();

编译后代码的解释

原型链

怎么理解Function.prototype === Function.__proto__ === Object.__proto__

  1. Function.prototype是个函数!任何函数的__proto__都是Function.prototype
  2. Object也是个构造函数,所以Object.__proto__指向Function.prototype

怎么理解Object.prototype.__proto__ === null
只是表示这个对象没有原型对象而已,是原型链的终点而已,没啥其他的。

解决任何原型链问题的三句话:

  1. 当 new 一个函数的时候会创建一个对象,『函数.prototype』 等于 『被创建对象.__proto__

  2. 一切函数都是由 Function 这个函数创建的,所以Function.prototype === 『被创建的函数.__proto__

  3. 一切函数的原型对象都是由 Object 这个函数创建的,所以Object.prototype === 『一切函数.prototype.__proto__

几个点

  • 要注意继承实例属性、静态属性
  • 要继承父类的实例属性,都是靠在子类中callapply调用实现。
  • 这些继承方式都可以直接通过修改proto来修改父类的方法,不是很安全

你可能感兴趣的:(继承)