• JavaScript 原型-原型链-手写函数继承


    JavaScript原型和原型链

    原型和原型链的概念

    是什么?
    函数在定义时会自动添加prototype属性显式原型属性,默认指向一个空Object对象,该对象称为显式原型对象。显式原型对象有一个constructor属性,指向构造函数
    实例在创建对象时会自动添加__proto__隐式原型属性,对象隐式原型的值=对应构造函数的显式原型的值

    原型链指隐式原型链
    访问一个对象的属性时
    1.现在自身属性中查找,找到返回
    2.没有找到,再沿__proto__这条链上找,找到返回
    3.最终没找到,返回undefined
    原型链的尽头是Object.prototype.__proto__ === null

    特殊:Function.__proto__ === Function,prototype
    Function即使构造函数也是Function的实例对象,所有的函数(包括Function)都是Function的实例

    有什么用?
    1.实现继承
    2.数据共享,节约内存空间

    特殊原型链 了解

    Fn.prototype instanceof Object; //true
    Object.prototype instanceof Object; //Object.prototype 的 __proto__是null,所以返回false
    Function.prototype instanceof Object;//true
    
    Object instanceof Object //true
    Object instanceof Function //true
    Fuction instanceof Function //true
    Function instanceof Object //true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    理解
    Function 可以看成是构造函数,也可以看成Function的实例
    Object 可以看成构造函数,也可以看成Function的实例
    在这里插入图片描述

    继承

    继承对于JS来说就是父类拥有的方法和属性、静态方法等,子类也要拥有。

    面试题

    • ES5继承的实现方法有哪些?
    • 寄生组合继承的缺点是什么?
    • ES6的继承属于哪种?

    原型链继承

    将父类的实例作为子类的原型,共享父类的实例属性/方法和原型上的属性和方法

    function Parent(){
        this.name = 'parent'
        this.play = [1,2,3]
    }
    function Child(){
        this.type = 'child';
    }
    Child.prototype = new Parent();//执行Parant
    Parent.prototype.id = '1';
    var child1 = new Child(); //执行Child    
    console.log(child1.name)//parent
    console.log(child1.id)//1
    var child2 = new Child();
    child1.play[0] = 2;
    console.log(child2.play)//[2,2,3]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    缺点

    1. 父类的所有引用属性(play)会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
    2. 子类型实例不能给父类型构造函数传参

    在这里插入图片描述

    构造函数继承

    继承实例属性:子类的构造函数里调用父类的构造函数,让每个实例都有自己的属性

    function Parent(name){
        this.name = name
        this.play = [1,2,3]
    }
    function Child(name){
    	//第二个参数可以传参
        Parent.call(this,name);//子类的每个实例都会将父类中的属性复制一份。
        /*
        child.name = 'child1'
        child.play = [1,2,3]
        */
    }
    Parent.prototype.id = '1'
    
    var child1 = new Child('child1'); //---ES5和ES6的区别 先创造子类的实例,执行构造器函数调用 Parent.call(this),继承父类实例的方法
    
    console.log(child1.name)//child1
    console.log(child1.id)//undefined
    var child2 = new Child();
    child1.play[0] = 2;
    console.log(child2.play)//[1,2,3]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    优点

    1. 可以在子类构造器中向父类传递参数
    2. 父类的引用属性不会被共享

    缺点

    1. 子类实例不能访问父类原型上,只能继承父类的例属性和方法

    组合继承 原型链继承+构造函数继承

    1.继承实例属性:子类的构造函数里调用父类的构造函数,防止父类引用类型被修改
    2.继承原型上的属性和方法: 将父类的实例作为子类的原型,访问父类原型。

    这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性

    function Parent(name){
      this.name = name
      this.play = [1,2,3]
    }
    function Child(name){
      //子类的每个实例都会将父类中的属性复制一份,访问时优先访问自己作用域中的
      Parent.call(this,name); //调用一次父类构造器
    }
    //继承原型上的属性和方法
    Child.prototype = new Parent();//调用一次父类构造器
    Child.prototype.constructor = Child;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    优点

    1. 父类原型上的方法可以复用
    2. 可以在Child构造函数中向Parent构造函数中传参
    3. 父类构造函数中的引用属性不会被共享

    缺点

    1. 调用了两次父类构造器,原型链上会存在两份相同的属性和方法

    在这里插入图片描述

    寄生组合继承 extends√

    1. 实例原型链:子类的实例可以拥有父类的方法,通过Son.prototype.__proto__ = Father.prototype
    2. 构造器原型链:子类可以拥有父类的静态方法,通过Son.__proto__=Father

    如果在一个方法前,加上static关键字表示是静态方法,静态方法通过类来调用。

    function Parent(name){
      this.name = name
      this.play = [1,2,3]
    }
    function Child(name){
      //子类的每个实例都会将父类中的属性复制一份,访问时优先访问自己作用域中的
      Parent.call(this,name); //调用一次父类构造器
    }
    
    //子类可以看见父类的方法
    //先创建父类的实例
    
    Child.prototype = Object.create(Parent.prototype); //ES5-ES6继承的区别:先创建父类的实例,然后再调用构造器函数修改this。
    
    //Object.create创建的是一个新对象,所以需要显式指定constructor属性
    Child.prototype.constructor = Child;
    //子类可以看见父类的静态方法
    Child.__proto__ = Parant;	
    
    
    //方法封装
    function prototype(child, parent) {
        let prototype = object(parent.prototype);
        prototype.constructor = child;
        child.prototype = prototype;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    优点
    1.只调用一次父类构造函数
    2.Child可以向Parent传参
    3.父类原型上的方法可以复用
    4.父类的引用属性不会被共享

    在这里插入图片描述

    new操作符的原理,手写new函数

    1. 创建一个空的实例对象
    2. 实例对象的__proto__隐式原型属性指向函数的prototype的显式原型属性
    3. 修改函数的this指向实例对象
    4. 执行函数,如果函数执行返回对象将其对象返回,否则返回实例对象
    function _new(fn,...args){
      if(!typeof fn ==='function') throw new Error('fn应该为函数')
      //  let obj = Object.create(fn.prototype); 实现下面两步
      let obj = {};
      obj.__proto__ = fn.prototype;
      let res = fn.call(obj,...args);
      return res instanceof Object ? res : obj;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    数商云SRM系统询比价有何优势?供应商平台助力汽车零部件企业快速查找供应商
    熹微~~~基于Vue开发的昏暗风格的响应式网页!
    腾讯待办停止运营怎么办?导出的ics文件数据怎么打开查看
    单商户商城系统功能拆解30—营销中心—积分签到
    DP - OOD - SRP
    Multi-task Classification Model Based On Multi-modal Glioma Data
    3D项目中用到的一些算法
    【进阶版】机器学习之支持向量机细节回顾及原理完善(09)
    B203-若依框架应用
    为运行 Parallels Ubuntu 20.04 虚拟机的 用户安装 PX4-Autopilot
  • 原文地址:https://blog.csdn.net/qq_41370833/article/details/126076635