zoukankan      html  css  js  c++  java
  • 再说说__proto__和prototype以及js的继承

    1.__proto__和prototype

    JS中的原型链已经是一个老生常谈的问题,毕竟也是JS 这门语言的特色之一了。

    这里写图片描述
    首先“万物皆对象“,虽然这句话一直有争议,但是有它的道理的,null类型这些的争论这里就不说了。
    对象中有个属性__proto__,被称为隐式原型,这个隐式原型指向构造改对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。这个实例可能是如图中的new Foo()出来的实例。

    构造该对象的f1,f2构造函数是fuction Foo(),它的原型是Foo.prototype,那么f1,f2就指向了构造该对象的构造函数的原型,也就是Foo.prototype,那么构造函数Foo()它的proto指向哪里了,还是找它的构造函数,它的构造函数是Function(),那么它的proto就指向了Fuction.prototype,沿着proto这条路最上就是Object.prototype,Object的proto就是null了。

    刚刚说的对象有个proto属性,方法也是对象,方法中除了有proto之外(这个proto指向构造该函数/对象的构造原型,也就是上一层了),还有prototype,这个属性就是原型属性,他是一个指针,指向一个对象,这个对象就叫原型对象,这里放着包含所有实例共享的属性和方法,这个原型对象里面有一个属性constructor,这个属性也包含一个指针,指回了原构造函数。

    1.构造函数Foo()构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。

    2.原型对象Foo.prototypeFoo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。

    3.实例f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,这样子就可以像上面1所说的访问原型对象的所有方法。

    4.构造函数Foo()除了是方法,也是对象,它也有__proto__属性,指向谁呢?指向它的构造函数的原型对象。函数的构造函数不就是Function嘛,因此这里的__proto__指向了Function.prototype。其实除了Foo(),Function(), Object()也是一样的道理。原型对象也是对象,它的__proto__属性,又指向谁呢?同理,指向它的构造函数的原型对象。这里是Object.prototype.最后,Object.prototype的__proto__属性指向null。

    5.对象有属性__proto__,指向该对象的构造函数的原型对象。
    方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。

    6.再看图。

    2.继承

    1.父类的实例作为子类的原型

    function Animal(name) {
      // 属性
      this.name =  name || "Animal";
      // 实例方法
      this.sleep = function() {
        console.log(this.name + '正在睡觉!');
      }
    }
    
    // 原型方法
    Animal.prototype.eat = function(food) {
      console.log(this.name + '正在吃' + food);
    };
    
    
    function Cat() { 
    }
    Cat.prototype = new Animal();
    Cat.prototype.name = 'cat';
    
    var cat = new Cat();
    console.log(cat.name);
    console.log(cat.eat('fish'));
    console.log(cat.sleep());
    console.log(cat instanceof Animal); //true 
    console.log(cat instanceof Cat); //true
    

    cat.proto === Cat.prototype //true

    构造cat对象的构造函数是Cat(),它的原型是Cat.prototype,那么隐式原型__proto__就指向构造该对象的构造函数的原型对象,那么cat的__proto__就指向的是Cat.prototype。

    Cat.prototype.proto === Animal.prototype //true
    原型对象也是对象,是对象就有__proto__,Animal的实例返回给了这个原型对象,那么这个原型对象的隐式原型__proto__就指向的是构造该对象的构造函数的原型,我们看看这个原型对象的构造函数是谁
    这里写图片描述

    那么Animal()构造函数的原型对象就是Animal.prototype了。自然就有上面true的结果了。

    这种方法的继承的缺点:

    1. 父类的引用属性和原型对象的引用属性是所有实例共享的
    2. 创建子类实例时,无法向父类构造函数传参
    3. 不能多继承
      第一个致命缺点,因为我们每个实例各自的属性互不干扰才对:
      这里写图片描述

    注意原型上的方法/属性是共享的
    这里写图片描述

    2.构造继承

    没有用到原型,使用父类的构造函数来增强子类实例,等于直接是复制父类的实例属性给子类。

    经典继承也叫做 “借用构造函数” 或 “伪造对象” 。其基本思想是:在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,因此可以通过使用apply() 和call() 方法也可以在新创建的对象上执行构造函数。(JS高程)

    function Cat(name){
      Animal.call(this);
      this.name = name || 'Tom';
    }
    
    // Test Code
    var cat = new Cat();
    console.log(cat.name);
    console.log(cat.sleep());
    console.log(cat instanceof Animal); // false
    console.log(cat instanceof Cat); // true
    

    特点:

    解决了1中,子类实例共享父类引用属性的问题
    创建子类实例时,可以向父类传递参数(通过call的后面参数)
    可以实现多继承(call多个父类对象)

    缺点:

    实例并不是父类的实例,只是子类的实例
    只能继承父类的实例属性和方法,不能继承原型属性/方法
    无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

    3.组合继承

    通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

    function Cat(name){
      Animal.call(this);
      this.name = name || 'Tom';
    }
    Cat.prototype = new Animal();
    
    //组合继承也是需要修复构造函数指向的。
    
    Cat.prototype.constructor = Cat;
    
    // Test Code
    var cat = new Cat();
    console.log(cat.name);
    console.log(cat.sleep());
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); // true
    

    这种方式看似是原型继承和构造继承的组合,弥补了构造继承只能继承实例属性/方法,不能继承原型属性/方法的缺点,也弥补了原型继承引用属性共享的问题,可向父类传参,函数可复用,即是子类的实例,也是父类的实例。

    缺点就是调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
    这里写图片描述

  • 相关阅读:
    B.Icebound and Sequence
    Educational Codeforces Round 65 (Rated for Div. 2) D. Bicolored RBS
    Educational Codeforces Round 65 (Rated for Div. 2) C. News Distribution
    Educational Codeforces Round 65 (Rated for Div. 2) B. Lost Numbers
    Educational Codeforces Round 65 (Rated for Div. 2) A. Telephone Number
    Codeforces Round #561 (Div. 2) C. A Tale of Two Lands
    Codeforces Round #561 (Div. 2) B. All the Vowels Please
    Codeforces Round #561 (Div. 2) A. Silent Classroom
    HDU-2119-Matrix(最大匹配)
    读书的感想!
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/9339118.html
Copyright © 2011-2022 走看看