zoukankan      html  css  js  c++  java
  • 【重学前端】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

  • 相关阅读:
    Jquery实现类似百度的搜索框
    Spring mvc 初始化过程
    Git学习笔记(2)-Eclipse中Git插件使用
    Git学习笔记(1)
    Tomcat7设置环境变量供java代码读取
    webpack+gulp实现自动构建部署
    netty 粘包问题处理
    java 并发工具类CountDownLatch & CyclicBarrier
    add spring-boot modules to maven project
    Spring Boot (#1 quick start)
  • 原文地址:https://www.cnblogs.com/AlubNoBug/p/13926684.html
Copyright © 2011-2022 走看看