zoukankan      html  css  js  c++  java
  • JS中的prototype和__proto__

    在开始之前要明确一点,“在JS里,万物皆对象”,方法(Function)是对象,方法的原型(Function.prototype)也是对象。因此,它们都会具有对象共有的特点。

     

     

    一、prototype和__proto__分别是什么?

     

    prototype(显式原型)是对象的一个属性(每个对象都有一个prototype属性),这个属性是一个指针,指向一个对象,通过它可以向对象添加属性和方法。

     

    __proto__(隐式原型)是对象的一个内置属性,是JS内部使用寻找原型链的属性,也就是说,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次向上查找。

     

     注意:用chrome和FF都可以访问到对象的__proto__属性,IE不可以。所以我们建议避免使用__proto__,因为它存在兼容性问题。

     

    二者关系:一个对象的隐式原型指向构造该对象的构造函数的原型

     

     

    二、创建对象实例的new做了些什么?

     

    var Person = function(){};
    Person.prototype.sayName = function() {
        alert("My Name is Jacky");
    };
    
    Person.prototype.age = 27;
    var p = new Person();

    console.log(p.age);  // 27 p.sayName();      //
    My Name is Jacky
    
    

    这段代码大家都能看懂,但是有人思考过这个问题吗:我们并没有显式设置p这个实例的age属性,为什么p.age可以打印出27?是什么帮我们悄悄做了处理?答案就是__proto__。

     

    当执行 var p =  new Person() 时 ,悄然发生了以下事情:

    var p={};
    p.__proto__
    =
    Person.prototype;  // 一个对象的隐式原型指向构造该对象的构造函数的原型


    Person.call(p);

     

    为了证明关键的第二步代码的正确性,我们执行:

    alert(p.__proto__ === Person.prototype);  // true

    有这层等价关系存在,就可以保证实例化对象p可以找到原型链上的属性。

    p是一个引用指向Person的实例化对象,我们在Person的原型上定义了一个sayName方法和age属性,当我们执行p.age时,会先在this的内部查找(也就是构造函数内部),如果没有找到,就沿着原型链向上追溯。

    这里的向上追溯是怎么向上的呢?就是通过__proto__属性来链接到原型(也就是Person.prototype)进行查找。最终在原型上找到了age属性。

     

     

    三、一个更复杂的示例

     

    var Person = function() {};
    Person.prototype.say = function() {
        console.log("Person say");
    }
    Person.prototype.salary = 50000;
    
    var Programmer = function() {};
    Programmer.prototype = new Person();
    Programmer.prototype.writeCode = function() {
        console.log("Programmer writes code");
    };
    Programmer.prototype.salary = 500;
    
    var p = new Programmer();
    p.say(); // Person say
    p.writeCode(); // Programmer writes code
    console.log(p.salary); // 500

    大家可以先自己思考一下,在new Programmer()时悄然发生了什么?

     

     

     

     

    ------------------------------------思考分割线------------------------------------------

     

     

     

     

    在var p = new Programmer()时发生了以下事情:

    var p = {};
    p.__proto__ = Programmer.prototype;
    p.__proto__ = new Person();
    p.__proto__.__proto__ = Pserson.prototype;
    Person.call(p.__proto__);
    Programmer.call(p);

    当我们调用 p.say() 时,p 中是没有 say 属性,于是到 p 的 __proto__ 属性中去找,也就是 Programmer.prototype,此时 Programmer.prototype 是等于 new Person(),但 new Person() 中也没有 say 属性,于是又到 new Person().__proto__ 中找,此时 new Person().__proto__ 等于 Pserson.prototype 的,我们定义了 Person.prototype.say=function(){}; 所以,p 在 Person.prototype 中就找到了这个方法。 

     

    四、一张粗糙的理解图

     

     

     

     

    1.构造函数Foo()
    构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。

     

    2.原型对象Foo.prototype
    Foo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。

     

    3.实例
    f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,这样就可以访问原型对象的所有方法。

     

    另外:
    构造函数Foo()除了是方法,也是对象(万物皆对象),它也有__proto__属性,指向谁呢?指向它的构造函数的原型对象,而函数的构造函数是Function,因此Foo.__proto__  =  Function.prototype。

    Function(), Object()也是一样的道理。Function是由Object构造出来的,所以Function的__proto__指向了Object.prototype

    最后,Object.prototype的__proto__属性指向null

     

  • 相关阅读:
    Quartus 自定义一个IP核,通过AXI总线被HPS系统控制(未完待续)
    IR 发送数据的上升沿和下降沿的判断(边沿检测)
    机器学习特征工程和优化方法
    最大期望算法(EM)
    主题模型(Topic Model)
    马尔科夫(Markov)
    【mysql-02-2】使用文档-建表约束
    【mysql-02-3】使用文档-范式
    【mysql-02-1】使用文档-基本语法
    Linux身份鉴别机制原理
  • 原文地址:https://www.cnblogs.com/Double-Zhang/p/7682211.html
Copyright © 2011-2022 走看看