• [我的理解]Javascript的原型与原型链


    一、原型与原型链的定义

    • 原型:为其他对象提供共享属性的对象

        注:当构造器创建一个对象,为了解决对象的属性引用,该对象会隐式引用构造器的"prototype"属性。程序通过constructor.prototype可以直接引用到构造器的"prototype"属性。并且添加到对象原型里的属性,会通过继承与所有共享此原型的对象共享。

    • 原型链:每个由构造器创建的对象,都有一个隐式引用(叫做对象的原型)链接到构造器的"prototype"属性。再者,原型可能有一个非空隐式引用链接到它自己的原型,以此类推,这叫做 原型链

    二、ES5中的Function与Object类型

    理解Function与Object类型的之间的关系,对我们理解原型和原型链有很重要的帮助。

    var fn = Function;
    console.log("fn的原型:" + fn.prototype);
    console.log("fn的原型链:" + fn.__proto__); 
    console.log("fn的原型等于fn的原型链:" + ( fn.prototype === fn.__proto__ ) );
    console.log("fn的原型的原型链:" + fn.prototype.__proto__);
    var obj = Object;
    console.info("obj的原型:" + obj.prototype);
    console.info("obj的原型链:" + obj.__proto__);
    console.info("obj的原型不等于obj的原型链:" + (obj.prototype === obj.__proto__));
    console.info("obj的原型的原型链:" + obj.prototype.__proto__);

    输出结果如下:

    fn的原型:function () {}
    fn的原型链:function () {}
    fn的原型等于fn的原型链:true
    fn的原型的原型链:[object Object]
    obj的原型:[object Object]
    obj的原型链:function () {}
    obj的原型不等于obj的原型链:false
    obj的原型的原型链:null

    根据输出结果我们不难看出,Function与Object存在相互引用的关系,以及其他特点总结如下:

    • Function.prototype.proto === Object.prototype:Function的原型的原型链等于Object的原型
    • Function.proto === Object.proto:Function与Object的原型链是相等的
    • Function.proto === Function.prototype:Function的原型等于Function的原型链,在ECMAScript5.1的规范中是如此说明的:Function的prototype是一个函数对象,他内部的[[prototype]]属性值是标准内置的Object的prototype对象
    • Object.prototype.proto === null:Object的原型的原型链为null

    关系图:

    • Function的prototype的__proto__是引用的Object的prototype,这是Function的原型的原型链
    • 而Object的__proto__是引用了Function的prototype的。

    2.1、其他原生类型

    原生类型大致可以分两类,一类是继承于Function的,另一类是继承于Object。

    • 继承于Function类型:String、Number、Boolean、Array、Date、RegExp、Error等,他们的__proto__(原型链)都指向了Function,而他们的prototype是各自类型的标准内置对象,这也就证明他们的prototype是继承于Object的,所以prototype.__proto__指向Object的prototype。
    • 继承于Object类型:Math、JSON等。因为是对象,所以没有构造函数,也没有自己的内置原型对象(prototype属性)。但还是有原型链的,他的__proto__指向Object的prototype。

    2.2、总结

    • Function是函数(类)的基础原型
    • Object是对象的基础原型
    • 运行时创建一个对象,会将构造器的prototype属性引用复制给对象的__proto__上,这里的创建一个对象,只能是new方式。
    • 用function关键字定义的类,做为Function类型的实例他本身有一个原型链(本身继承于Object),另外通过new创建的实例对象也有属于自己的原型链(prototype到__proto__的转换)。

    三、实现继承(原型继承)

         前面描述了Function、Object和其他原生类型的关系,在这里我们深入了解Function对象的类特性,这里我们使用function这个类,他是Function的实例,拥有Function类型的所有方法。

    3.1、ES5.1

    function Parent(){
        this.name = 'parent';
    }
    Parent.prototype.getName = function(){
        console.log(this.name);
    }
    
    function Child(){
        Parent.call(this); //在this对象上增加属性
        this.cName = 'child';
    }
    Child.prototype = Parent.prototype; //复制parent的prototype所有的内容,包含构造器
    Child.prototype.constructor = Child; //恢复构造器为子类,不恢复也不影响其new
    
    var _child = new Child();

    实现继承的步骤:

    • Parent的prototype赋值给Child的prototype,使其Child拥有Parent的原型方法或属性(子类与父类的prototype进行合并)
    • 将Child.prototype.constructor指向Child函数,因为constructor是指向其构造器的方法,被Parent赋值后,其实是指向了Parent的构造器,所以需要改回来。
    • 在Child的构造器中用Call执行Parent的构造器,实例构造器的继承执行(顺序执行父类、子类的构造函数)。

    总结:

    • 原型的继承实际上是共享原型上的属性和方法,所以更改基类原型上的属性和方法会影响到子类。但构造器中对this做的绑定则是实例独立的。
    • function关键字定义的类(Parent、Child)的__proto__都是指向function的构造函数
    • function关键字定义的类所有prototype的__proto__都是指向了Object的prototype。

    3.2、ES2015(ES6)

    在es6中实现继承就相当的简单了,不需要像es5中那么步骤来实现,继承实现如下:

    class Parent {
        constructor(){
            this.name = 'parent';
        }
        getName(){
            console.log(this.name);
        }
    }
    class Child extends Parent {
        constructor(){
            super();
            this.cName = 'child';
        }
    }
    
    var _child = new Child();
    • Class的__proto__指向父类的定义,包含构造函数。
    • Class的prototype.__proto__是指向父类的prototype,表示方法的继承。

    四、产生的改变

    • ES5中用Function实现面向对象,而ES6提供了Class。
    • ES6的Class对原型与原型链更加规范化。
    • ES6提供了操作__proto__对象的方法,分别如下:
      • Object.setPrototypeOf(obj,protype):设置对象的__proto__(原型对象
      • Object.getPrototypeOf(obj):读取对象的__proto__(原型对象)
    • ES5中可以直接对__proto__赋值,但不建议这样使用。
    • ES5中对子类的prototype进行赋值后,还需要重定向prototype.constructor到子类的构造函数。
  • 相关阅读:
    HttpClient调用RestFul接口(post和get方式)
    mysql权限异常
    javascript:用法
    Java哈希值HashCode理解
    Java的CountDownLatch和CyclicBarrier的理解和区别
    Java并发编程与技术内幕:ThreadGroup线程组应用
    面试官: 谈谈什么是守护线程以及作用 ?
    java 成员变量 静态成员变量 方法 静态方法初始化顺序
    【java并发核心一】Semaphore 的使用思路
    threadlocal原理及常用应用场景
  • 原文地址:https://www.cnblogs.com/cqhaibin/p/6624128.html
Copyright © 2020-2023  润新知