【重学前端】JS基础-原型和原型链

  • 原型和原型链

    • 1.理解原型设计模式以及JavaScript中的原型规则

      • 原型设计模式

        • 概念:是指原型实例指向创建对象的种类,并通过拷贝这些原型创建新的对象,是一种用来创建对象的模式,也就是创建一个对象作为另一个对象的prototype属性。

        • 实现原型模式

          • 方法一:使用 Object.create(prototype, optionalDescriptorObjects)

            var vehiclePrototype = {
                model:"保时捷",
                getModel: function () {
                    console.log('车辆模具是:' + this.model);
                }
            };
            
            var vehicle = Object.create(vehiclePrototype,{
                "model":{
                    value:"法拉利"
                }
            });
            
            vehicle.getModel();
            
          • 方法二:使用 prototype

            var vehiclePrototype = {
                init: function (carModel) {
                    this.model = carModel || "保时捷";
                },
                getModel: function () {
                    console.log('车辆模具是:' + this.model);
                }
            
            };
            
            function vehicle(model) {
                function F() { };
                F.prototype = vehiclePrototype;    
                var f = new F();
                f.init(model);    return f;
            }
            var car = vehicle('法拉利');
            car.getModel();
            
        • 原型规则

          1. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性;
          var arr = [];
          arr.a = 1;
          
          1. 所有的引用类型(数组、对象、函数),都有一个_proto_属性(隐式原型),属性值是一个普通的对象;

          2. 所有的函数,都具有一个prototype(显式原型),属性值也是一个普通对象;

          3. 所有的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;(obj.proto === Object.prototype);

          4. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中去寻找;

    • 2.instanceof的底层实现原理,手动实现一个instanceof

      • instanceof的实现实际上是调用JS的内部函数 [[HasInstance]] 来实现的

      • 实现原理:只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。

      • var arr = {};
        console.log(new_instance_of(arr,Object));
        
        function new_instance_of(leftValue, rightValue){
            let rightProto = rightValue.prototype;
            leftValue = leftValue.__proto__;
            while(true){
                if(leftValue === null){
                    return false;
                }
                if(leftValue === rightProto){
                    return true;
                }
                leftValue = leftValue.__proto__;
            }
        }
        
    • 4.实现继承的几种方式以及他们的优缺点

      https://www.cnblogs.com/humin/p/4556820.html
      
      1. 原型链继承(推荐指数:★★)

        • 核心:将父类的实例作为子类的原型
        • 优点:
          1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
          2. 父类新增原型方法/原型属性,子类都能访问到
          3. 简单,易于实现
        • 缺点:
          1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
          2. 无法实现多继承
          3. 来自原型对象的所有属性被所有实例共享
          4. 创建子类实例时,无法向父类构造函数传参
      2. 构造继承(推荐指数:★★)

        • 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
        • 优点:
          1. 解决了1中,子类实例共享父类引用属性的问题
          2. 创建子类实例时,可以向父类传递参数
          3. 可以实现多继承(call多个父类对象)
        • 缺点:
          1. 实例并不是父类的实例,只是子类的实例
          2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
          3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
      3. 实例继承(推荐指数:★★)

        • 核心:为父类实例添加新特性,作为子类实例返回
        • 优点:不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
        • 缺点:
          1. 实例是父类的实例,不是子类的实例
          2. 不支持多继承
      4. 拷贝继承(推荐指数:★)

        • 优点:支持多继承
        • 缺点:
          1. 效率较低,内存占用高(因为要拷贝父类的属性)
          2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
      5. 组合继承(推荐指数:★★★★)

        • 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
        • 优点:
          1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
          2. 既是子类的实例,也是父类的实例
          3. 不存在引用属性共享问题
          4. 可传参
          5. 函数可复用
        • 缺点:调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
      6. 寄生组合继承(推荐指数:★★★★)

        • 核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
        • 优点:堪称完美
        • 缺点:实现较为复杂
    • 5.至少说出一种开源项目(如Node)中应用原型继承的案例

      
      
    • 6.可以描述new一个对象的详细过程,手动实现一个new操作符

      // ES5构造函数
      let Parent = function (name, age) {
          //1.创建一个新对象,赋予this,这一步是隐性的
          // let this = {};
          //2.给this指向的对象赋予构造属性
          this.name = name;
          this.age = age;
          //3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的
          // return this;
      };
      const child = new Parent();
      
      winter重学前端专栏中原理的描述:
      • 以构造器的prototype属性为原型,创建新对象;
      • 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;
      • 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。
      
      简单来说就是new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。
      
      // 构造器函数
      let Parent = function (name, age) {
          this.name = name;
          this.age = age;
      };
      Parent.prototype.sayName = function () {
          console.log(this.name);
      };
      //自己定义的new方法
      let newMethod = function (Parent, ...rest) {
          // 1.以构造器的prototype属性为原型,创建新对象;
          let child = Object.create(Parent.prototype);
          // 2.将this和调用参数传给构造器执行
          let result = Parent.apply(child, rest);
          // 3.如果构造器没有手动返回对象,则返回第一步的对象
          return typeof result  === 'object' ? result : child;
      };
      //创建实例,将构造函数Parent与形参作为参数传入
      const child = newMethod(Parent, 'echo', 26);
      child.sayName() //'echo';
      
      //最后检验,与使用new的效果相同
      child instanceof Parent//true
      child.hasOwnProperty('name')//true
      child.hasOwnProperty('age')//true
      child.hasOwnProperty('sayName')//false
      
    • 7.理解es6 class构造以及继承的底层实现原理

      • 理解?

      • 原理?

      • 类的创建

        //定义类
        class Person{
          // 类的静态方法,相当于Person.test = function(){console.log("类的静态方法");}
          static test() {
            console.log("类的静态方法");
        
          }
          
          //constructor构造函数
          constructor(name,age){
            console.log("调用构造函数");
            this.name = name;
            this.age = age;
          }
        
          //类的一般方法,定义在实例对象的原型对象上,相当于Person.prototype.show = function(){console.log("this.name,this.age");}
          show(){
            console.log(this.name,this.age);
          }
        }
        
        let person1 = new Person("wzh",25);
        console.log(person1);
        person1.show();
        Person.test();
        
      • 类的继承

        class Child extends Person{
        
          constructor(name,age,sex){
            super(name,age);  //调用父类构造函数构造子类
            this.sex = sex;
          }
        
          //重写父类同名函数
          show(){
            console.log(this.name,this.age,this.sex);
          }
        }
        
        let child = new Child("wzl",24,"男");
        child.show();
        

本文作者:AlubNoBug

本文链接:https://www.cnblogs.com/AlubNoBug/p/13926684.html

posted @ 2020-11-04 16:42  AlubNoBug  阅读(94)  评论(0编辑  收藏  举报