JS的原型和继承

126 阅读2分钟

JS的原型和继承

原型和原型链

  • 原型对象: 构造函数都有一个prototype[原型]属性,这个属性的值就是原型对象。原型对象可以让构造函数创建的对象实例 共享它包含的属性和方法。在Safari、firefox、chrome在每个对象上都支持一个属性__proto__,它指向实例的构造函数的 原型。
  • 原型链:对象的原型指向原型对象,多个原型的层层相连,形成原型链
      //通过以下代码解释原型以及原型链
      function Person(name) {
        this.name = name;
      }
      var zhangsan = new Person('zhangsan');
      console.log(zhangsan.__proto__ === Person.prototype);
      console.log(Person.prototype.__proto__ === Object.prototype)
    
    Person是构造函数,zhangsan是构造行数Person创建的实例,Person的原型则是Person.prototype,通过打印,可以直到,它与zhangsan.__proto__相等。Person.prototype指向的对象,是Object构造函数创建的实例,所以Person.prototype.__proto__会等于Object.prototype,这样,Person构造函数的原型和Object构造函数的原型就构成了原型链

instanceof、constructor、typeof、isPrototypeOf

  • instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否在要检测对象的原型链上
  • constructor是每一个实例对象都拥有的属性,而这个属性也相当于是一个指针,它指向于创建当前对象的对象
  • typeof 返回一个表达式的数据类型的字符串,包括number,boolean,string,object,undefined,function
  • isPrototypeOf是用来判断指定对象object1是否存在于另一个对象object2的原型链中,例如:object1.isPrototypeOf(object2)

继承的几种方式以及优缺点

  • 原型继承

      function Parent(name) {
        this.name = name;
        this.colors = ['red', 'green', 'origin'];
      }
      function Child(params) {
        this.age = params.age;
      }
      Child.prototype.sayName = function() {
        console.log(`this.name=${this.name}`);
      }
      Parent.prototype.pushColor = function(color) {
        this.colors.push(color);
      }
      Child.prototype = new Parent('parent1');
      var child1 = new Child({age: 20});
      var child2 = new Child({age: 30});
      child1.pushColor('yellow');
      console.log(child1.colors); // ["red", "green", "origin", "yellow"]
      console.log(child2.colors); // ["red", "green", "origin", "yellow"]
    

    缺点:Parent方法执行了2次,没办法在不影响所有实例的情况下,给构造函数传递参数,如上例子,原本只想给child1的colors增加yellow,结果child2也增加了,不符合初衷

  • call改变上下文继承

      function Parent(name) {
        this.name = name;
      }
      function Child(name) {
        this.age = 18;
        Parent.call(this, name);
      }
      Child.prototype.sayName = function() {
        console.log(`this.name=${this.name}`);
      }
      Parent.prototype.pushColor = function(color) {
        this.colors.push(color);
      }
      var child1 = new Child('child1');
      console.log(child1.name);  // child1
      console.log(child1.pushColor()); // 报错  TypeError: child1.pushColor is not a function
    

    缺点:没有继承Parent原型上的方法和属性

  • 组合继承

      function Parent(name) {
        this.name = name;
      }
      function Child(params) {
        this.colors = ['red', 'green', 'origin'];
        Parent.call(this, params)
      }
      Parent.prototype.pushColor = function(color) {
        this.colors.push(color);
      }
      Child.prototype = new Parent('parent1');
      var child1 = new Child('child1');
      var child2 = new Child({age: 30});
      child1.pushColor('yellow'); 
      console.log(child1.colors); // ["red", "green", "origin", "yellow"]
      console.log(child2.colors); // ["red", "green", "origin"]
    

    缺点:Parent方法执行了2次

  • 组合继承优化1

      function Parent(name) {
        this.name = name;
      }
      function Child(params) {
        Parent.call(this, params)
      }
      Parent.prototype.sayName = function() {
        console.log(`this.name=${this.name}`);
      }
      Child.prototype = Object.create(Parent.prototype);
      var child1 = new Child('child1');
      console.log(child1.name);
      console.log(child1.sayName());
    

    缺点: Child构造函数的constructor不是指向本身

  • 组合继承优化2

      function Parent(name) {
        this.name = name;
      }
      function Child(params) {
        Parent.call(this, params)
      }
      Parent.prototype.sayName = function() {
        console.log(`this.name=${this.name}`);
      }
      Child.prototype = Object.create(Parent.prototype);
      Child.prototype.constructor = Child;
      var child1 = new Child('child1');
      console.log(child1.name);
      console.log(child1.sayName());
    
  • class继承

      class Parent {
        static color () {
          return 'red'
        }
        constructor() {
            this.name = 'parent';
        }
        sayName () {
            console.log(this.name)
        }
      }
      class Child extends Parent {
        constructor () {
          super();
          this.age = 18;
        }
        sayAge() {
          console.log(this.age)
        }
      }
      var child1 = new Child();
      console.log(child1.age);
      console.log(child1.sayName());