zoukankan      html  css  js  c++  java
  • JS: 原型与原型链

    原型与原型链

    javascript 创建对象


    类与构造函数是大多数编程语言所拥有的,而借鉴了 C 与 JAVA 的 javascript 也是有类和构造函数的,不过 javascript 的实现不太一样。

    // before ES6
    // 构造函数模式
    function Person(name){
      this.name = name;
        this.sayName = function() {
          console.log(this.name);
        }
    }
    
    var p = new Person();
    console.log(p instanceof Person); // true
    

    上面的例子中,Person就是构造函数,p就是它的实例,但是它对共享性不太好,所以有了原型模式。

    // 原型模式
    function Person(){}
    Person.prototype.name = 'kobe';
    Person.prototype.sayName = function() {
      console.log(this.name);
    }
    
    var p1 = new Person();
    var p2 = new Person();
    
    p1.sayName(); // kobe
    p2.sayName(); // kobe
    

    原型模式创建的实例都可以共享Person.prototype的属性和方法,而prototype是 javascript 实现类的一个关键所在。

    prototype


    在 javascript 中,每个函数都有一个prototype属性,这个属性指向函数的原型对象,上面的例子,可以用下图解释,图中用Person.prototype表示 Person 的原型对象。

    prototype

    constructor


    Person 是构造函数,有prototype指针指向原型对象,而Person.prototype也有一个constructor属性指向构造函数。

    constructor

    简单来说,就是 javascript 会在函数创建的时候为函数添加一个prototype属性,这个属性是指向函数的原型对象的指针,而原型对象也会获得一个constructor属性,这个属性是一个指向函数本身的指针,即指向构造函数。

    function Person(){}
    Person.prototype.name = 'kobe';
    Person.prototype.sayName = function() {
      console.log(this.name);
    }
    
    Person.prototype.constructor == Person; // true
    

    __proto__


    函数有prototype,那对象呢?其实对象内部也有一个[[prototype]]指针,这个指针指向创建实例对象的构造函数的原型对象。

    在 ES5 之前的语言标准里,是不允许访问这个属性的,但浏览器都实现了一个__proto__[[prototype]]进行访问操作,所以 ES5 增加了一个Object.getPrototypeOf()方法来访问[[prototype]]

    Object.getPrototypeOf(object) 方法返回指定对象的原型(内部[[Prototype]]属性的值)。

    在 ES5 中,如果参数不是一个对象类型,将抛出一个 TypeError 异常。在 ES6 中,参数会被强制转换为一个 Object。

    建议在代码中使用Object.getPrototypeOf()来访问内部[[Prototype]]属性,不建议使用非标准的__proto__,为了方便区别,文中使用__proto__来表示[[prototype]]

    回到上面的例子,当调用 Person 来创建实例的时候,该实例会获得一个__proto__指针指向构造函数的原型对象。

    proto

    正是因为创建的 p1、p2 实例都拥有__proto__指向构造函数(Person)的原型对象(Person.prototype),它们才能共享Person.prototype的属性和方法。

    function Person(){}
    Person.prototype.name = 'kobe';
    Person.prototype.sayName = function() {
      console.log(this.name);
    }
    
    let p1 = new Person();
    let p2 = new Person();
    
    Object.getPrototypeOf(p1) == Object.getPrototypeOf(p2); // true
    

    简单来说,就是在 javascript 中,每一个对象(除null)在创建的时候都会将其与另一个对象进行关联,而关联的这个对象就是所谓的原型。

    使用isPrototypeOf()方法就能确定这种关联:

    Person.prototype.isPrototypeOf(p1); // true
    Person.prototype.isPrototypeOf(p2); // true
    

    屏蔽性


    还是这个例子:

    function Person(){}
    Person.prototype.name = 'kobe';
    Person.prototype.sayName = function() {
      console.log(this.name);
    }
    
    let p1 = new Person();
    // 访问 p1 的 name
    p1.name; // kobe
    

    当我们通过实例 p1 访问name属性的时候,javascript 引擎会先在实例 p1 中查找name,当发现 p1 没有这个属性,就会通过__proto__向它指向的原型对象进行查找,找到就返回这个属性的值,也就是说访问name执行了两次查找。

    如果 p1 自己添加了name属性,就会在第一次查找时返回值:

    // 在 p1 中添加 name
    p1.name = 'jordan';
    
    // 只会执行一次查询
    p1.name; // jordan
    
    // 原型中的值并未改变
    Object.getPrototypeOf(p1).name; // kobe
    

    也就是说,在实例上添加name属性后,该属性就会屏蔽原型中的那个属性,但并不会改变原型中的该属性的值。

    原型链


    既然在 javascript 中,所有对象(除null)都有__proto__,而实例 p1 指向Person.prototype,那Person.prototype对象的__proto__又指向哪里呢?

    Object.getPrototypeOf(Person.prototype) == Object.prototype; // true
    

    答案是Object.prototype,因为原型对象是通过构造函数 Object 来创建的,所以它指向Object.prototype

    Object.prototype

    那 Object.prototype 的__proto__又指向哪里呢?它指向这条链的终点:null,代表无。

    Object.getPrototypeOf(Object.prototype); // null
    

    null

    回到上面所说的查找name属性,如果在第二次都没有查找到name属性,就会沿着这条链继续向上查找,直到终点都没有找到,就会返回 undefined。

    function Person(){}
    
    let p1 = new Person();
    p1.name; // undefined
    

    上图中虚线部分由原型相互关联组成的链状结构就是所谓的原型链。

    备注


    不得不说,《javascript高级程序设计》这本书把原型讲得很透彻,今天再看一遍依然感觉通透。

  • 相关阅读:
    C# WinForm界面上实现按条件检索数据
    DevExpress中XtraEditors.RadioGroup 控件如何保存获取选中的值及读取数据库中的值
    在QTP Test中利用vbs和cmd实现重新启动QTP
    VBS操作Excel的一点问题总结
    利用vbs维护qtp的虚拟对象的坐标
    Smoke Test和BVT Test的区别
    小结一下VS2012新开发环境的设置经历
    关闭EF4.x Code First的级联删除Cascade Delete
    关于Entity Framework 4.0/4.1数据验证的一点体会
    CentOS 6.3 Minimal yum 安装 PostgreSQL 9.2.3
  • 原文地址:https://www.cnblogs.com/guolao/p/10450288.html
Copyright © 2011-2022 走看看