• javascript prototype原型链的原理


    javascript prototype原型链的原理

    说到prototype,就不得不先说下new的过程。

    我们先看看这样一段代码:

        <script type="text/javascript">
    var Person = function () { };
    var p = new Person();
    </script>

    很简单的一段代码,我们来看看这个new究竟做了什么?我们可以把new的过程拆分成以下三步:

    <1> var p={}; 也就是说,初始化一个对象p。

    <2> p.__proto__=Person.prototype; 2个下划线+proto+2个下划线

    <3> Person.call(p);也就是说构造p,也可以称之为初始化p。

    关键在于第二步,我们来证明一下:

     <script type="text/javascript">
    var Person = function () { };
    var p = new Person();
    alert(p.__proto__ === Person.prototype);
    </script>

    这段代码会返回true。说明我们步骤2的正确。

    那么__proto__是什么?我们在这里简单地说下。每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。

    按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox的引擎将他暴露了出来成为了一个共有的属性,我们可以对外访问和设置。

    好,概念说清了,让我们看一下下面这些代码:

     <script type="text/javascript">
    var Person = function () { };
    Person.prototype.Say = function () {
    alert("Person say");
    }
    var p = new Person();
    p.Say();
    </script>

    这段代码很简单,相信每个人都这样写过,那就让我们看下为什么p可以访问Person的Say。

    首先var p=new Person();可以得出p.__proto__=Person.prototype。那么当我们调用p.Say()时,首先p中没有Say这个属性,于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我们在上面定义了Person.prototype.Say=function(){}; 于是,就找到了这个方法。

    好,接下来,让我们看个更复杂的。

     var Person = function () { };
            Person.prototype.Say = function () {
                alert("Person say");
            }
            Person.prototype.Salary = 50000;
            var Programmer = function () { };
            Programmer.prototype = new Person();
            Programmer.prototype.WriteCode = function () {
                alert("programmer writes code");
            };
            Programmer.prototype.Salary = 500;
            var p = new Programmer();
            p.Say();
            p.WriteCode();
            alert(p.Salary);

    们来做这样的推导:

    var p=new Programmer()可以得出p.__proto__=Programmer.prototype;

    而在上面我们指定了Programmer.prototype=new Person();我们来这样拆分,

    var p1=new Person();Programmer.prototype=p1;那么:

    p1.__proto__=Person.prototype;

    Programmer.prototype.__proto__=Person.prototype; ..

    Programmer.prototype = new Person();

    由根据上面得到p.__proto__=Programmer.prototype。可以得到p.__proto__.__proto__=Person.prototype。

    好,算清楚了之后我们来看上面的结果,p.Say()。由于p没有Say这个属性,于是去p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也没有Say,那就去p.__proto__.__proto__,也就是Person.prototype中去找,于是就找到了alert(“Person say”)的方法。

    其余的也都是同样的道理。

    这也就是原型链的实现原理。

    最后,其实prototype只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在new的时候有着一定的价值,而原型链的本质,其实在于__proto__!


    转自:http://www.cnblogs.com/kym/archive/2010/01/09/1643062.html

    另外一篇:

    An Object's __proto__ property references the same object as its internal [[Prototype]] (often referred to as "the prototype"), which may be an object or null (in the case of Object.prototype.__proto__). This property is an abstraction error, because a property with the same name, but some other value, could be defined on the object too. If there is a need to reference an object's prototype, the preferred method is to use Object.getPrototypeOf.

    var proto = obj.__proto__;

    When an object is created, its __proto__ property is set to reference the same object as its internal [[Prototype]] (i.e. its constructor's prototype object).  Assigning a new value to __proto__ also changes the value of the internal [[Prototype]] property, except where the object is non–extensible.

    当1个对象创建时,__proto__属性被设置于引用相同的【prototype】对象。赋一个值给__proto__属性会改变【prototype】属性,除非对象不可扩展。

    To understand how prototypes are used for inheritance, see the MDN article Inheritance and the prototype chain.

    Example

    In the following, a new instance of Employee is created, then tested to show that its __proto__ is the same object as its constructor's prototype.

    // Declare a function to be used as a constructor
    function Employee() {
    /* initialise instance */
    }

    // Create a new instance of Employee
    var fred = new Employee();

    // Test equivalence
    fred.__proto__ === Employee.prototype; // true

    At this point, fred inherits from Employee, however assigning a different object to fred.__proto__ can change that:

    1
    2
    // Assign a new object to __proto__
    fred.__proto__ = Object.prototype;

    Now fred no longer inherits from Employee.prototype, but directly from Object.prototype, and loses the properties it originally inherited from Employee.prototype.

    However, this only applies to extensible objects, a non–extensible object's __proto__ property cannot be changed:

    1
    2
    3
    4
    var obj = {};
    Object.preventExtensions(obj);
     
    obj.__proto__ = {}; // throws a TypeError

     一篇很好的文章:

    转自:http://blog.vjeux.com/2011/javascript/how-prototypal-inheritance-really-works.html

    Everywhere on the web we read that Javascript has prototypal inheritance. However Javascript only provides by default a specific case of prototypal inheritance with the new operator. Therefore, most of the explanations are really confusing to read. This article aims to clarify what is prototypal inheritance and how to really use it on Javascript.

    Prototypal Inheritance Definition

    When you read about Javascript prototypal inheritance, you often see a definition like this:

    When accessing the properties of an object, JavaScript will traverse the prototype chain upwards until it finds a property with the requested name. Javascript Garden

    Most Javascript implementations use __proto__ property to represent the next object in the prototype chain. We will see along this article what is the difference between __proto__ and prototype.

    Note__proto__ is non-standard and should not be used in your code. It is used in the article to explain how Javascript inheritance works.

    The following code shows how the Javascript engine retrieves a property (for reading).

    function getProperty(obj, prop) {
      if (obj.hasOwnProperty(prop))
        return obj[prop]
     
      else if (obj.__proto__ !== null)
        return getProperty(obj.__proto__, prop)
     
      else
        return undefined
    }

    Let's take the usual class example: a 2D Point. A Point has two coordinates xy and a method print.

    Using the definition of the prototypal inheritance written before, we will make an object Point with three properties: xy and printIn order to create a new point, we just make a new object with __proto__ set toPoint.

    var Point = {
      x: 0,
      y: 0,
      print: function () { console.log(this.x, this.y); }
    };
     
    var p = {x: 10, y: 20, __proto__: Point};
    p.print(); // 10 20

    Javascript Weird Prototypal Inheritance

    What is confusing is that everyone teaches Javascript prototypal inheritance with this definition but does not give this code. Instead they give something like this:

    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    Point.prototype = {
      print: function () { console.log(this.x, this.y); }
    };
     
    var p = new Point(10, 20);
    p.print(); // 10 20

    This is completely different from the code given above. Point is now a function, we use a prototype property, the new operator. What the hell!?

    How new works

    Brendan Eich wanted Javascript to look like traditional Object Oriented programming languages such as Java and C++. In those, we use the new operator to make a new instance of a class. So he wrote a new operator for Javascript.

    • C++ has the notion of constructor, that initializes the instance attributes. Therefore, the newoperator must target a function.
    • We need to put the methods of the object somewhere. Since we are working on a prototypal language, let's put it in the prototype property of the function.

    The new operator takes a function F and arguments: new F(arguments...). It does three easy steps:

    1. Create the instance of the class. It is an empty object with its __proto__ property set toF.prototype.(设置__proto__为F.prototype,正如前面讲过的。
    2. Initialize the instance. The function F is called with the arguments passed and this set to be the instance.
    3. Return the instance

    Now that we understand what the new operator does, we can implement it in Javascript.

         function New (f) {
    /*1*/  var n = { '__proto__': f.prototype };
           return function () {
    /*2*/    f.apply(n, arguments);
    /*3*/    return n;
           };
         }

    And just a small test to see that it works.

    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    Point.prototype = {
      print: function () { console.log(this.x, this.y); }
    };
     
    var p1 = new Point(10, 20);
    p1.print(); // 10 20
    console.log(p1 instanceof Point); // true
     
    var p2 = New (Point)(10, 20);
    p2.print(); // 10 20
    console.log(p2 instanceof Point); // true

    Real Prototypal Inheritance in Javascript

    The Javascript specifications only gives us the new operator to work with. However, Douglas Crockford found a way to exploit the new operator to do real Prototypal Inheritance! He wrote the Object.create function.

    Object.create = function (parent) {
      function F() {}
      F.prototype = parent;
      return new F();
    };

    This looks really strange but what it does is really simple. It just creates a new object with its prototype set to whatever you want. It could be written as this if we allow the use of __proto__:

    Object.create = function (parent) {
      return { '__proto__': parent };
    };

    The following code is our Point example with the use of real prototypal inheritance.

    var Point = {
      x: 0,
      y: 0,
      print: function () { console.log(this.x, this.y); }
    };
     
    var p = Object.create(Point);
    p.x = 10;
    p.y = 20;
    p.print(); // 10 20

    Conclusion

    We have seen what prototypal inheritance is and how Javascript implements only a specific way to do it.

    However, the use of real prototypal inheritance (Object.create and __proto__) has some downsides:

    • Not standard__proto__ is non-standard and even deprecated. Also native Object.create and Douglas Crockford implementation are not exactly equivalent.
    • Not optimized: Object.create (native or custom) has not yet been as heavily optimized as the newconstruction. It can be up to 10 times slower.

    Some further reading:

    Bonus

    If you can understand with this picture (from the ECMAScript standard) how Prototypal Inheritance works, you get a free cookie!

    另外一篇好文:

    http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

    之前我对Javascript的原型链中, 原型继承与标识符查找有些迷惑,

    如, 如下的代码:

    1. function Foo() {};
    2. var foo new Foo();
    3. Foo.prototype.label "laruence";
    4. alert(foo.label); //output: laruence
    5. alert(Foo.label);//output: undefined

    今天看到了如下这个图:

    另外, 在Javascript Object Hierarchy看到:

    The prototype is only used for properties inherited by objects/instances created by that function. The function itself does not use the associated prototype.

    也就是说, 函数对象的prototype并不作用于原型链查找过程中,(就是:原型仅仅用于被函数创建的对象或实例,函数本身不使用相关的原型,可以类比类方法和对象方法的区别

    今天在firefox下发现(因为firefox通过__proto__暴露了[[prototype]]), 真正参与标识符查找的是函数对象的__proto__,

    1. function Foo() {};
    2. var foo new Foo();
    3. Foo.__proto__.label "laruence";
    4. alert(Foo.label); //output: laruence
    5. alert(foo.label);//output: undefined

    而, 显然的:

    1. function Foo() {};
    2. alert(Foo.__proto__ === Foo.prototype); //output: false

    另外, 也解释了,

    1. alert(Object.forEach); // undefined
    2.  
    3. Function.prototype.forEach function(object, block, context{
    4.     for (var key in object{
    5.         if (typeof this.prototype[key== "undefined"{
    6.             block.call(context, object[key], key, object);
    7.         }
    8.     }
    9.  
    10. };
    11.  
    12. alert(Object.forEach);
    13. alert(Function.forEach);
    14. alert(Object.forEach === Function.forEach); // true

    An Object's __proto__ property references the same object as its internal [[Prototype]] (often referred to as "the prototype"), which may be an object or, as in the default case of Object.prototype.__proto__, null . This property is an abstraction error, because a property with the same name, but some other value, could be defined on the object too. If there is a need to reference an object's prototype, the preferred method is to use Object.getPrototypeOf.

    __proto__ pseudo property has been included in §B.3.1 of the draft ECMAScript ed. 6 specification (note that the specification codifies what is already in implementations and what websites may currently rely on).

    var proto = obj.__proto__;

    一个对象的__proto__ 属性和自己的内部属性[[Prototype]]指向一个相同的值 (通常称这个值为原型),原型的值可以是一个对象值也可以是null(比如说Object.prototype.__proto__的值就是null).该属性可能会引发一些错误,因为用户可能会不知道该属性的特殊性,而给它赋值,从而改变了这个对象的原型. 如果需要访问一个对象的原型,应该使用方法Object.getPrototypeOf.

    __proto__ 属性已经被添加在了ES6草案 §B.3.1中.

    不要认为__proto__和prototype相等。

    Description

    When an object is created, its __proto__ property is set to reference the same object as its internal [[Prototype]] (i.e. its constructor's prototype object).  Assigning a new value to __proto__ also changes the value of the internal [[Prototype]] property, except where the object is non–extensible.

    To understand how prototypes are used for inheritance, see the MDN article Inheritance and the prototype chain.

    一个对象被创建时,它的 __proto__ 属性和内部属性[[Prototype]]指向了相同的对象 (也就是它的构造函数的prototype属性).改变__proto__ 属性的值同时也会改变内部属性[[Prototype]]的值,除非该对象是不可扩展的.

    想要知道如何使用原型来实现继承,查看MDN文章继承和原型链.

    Example

    In the following, a new instance of Employee is created, then tested to show that its __proto__ is the same object as its constructor's prototype.

    复制代码
    // 声明一个函数作为构造函数function Employee() {
      /* 初始化实例 */
    }
    
    // 创建一个Employee实例
    var fred = new Employee();
    
    // 测试相等性
    fred.__proto__ === Employee.prototype; // true
    复制代码

    这是, fred 继承了 Employee, 但是如果给fred.__proto__ 赋另外一个对象值,则会改变它的继承对象:

    // Assign a new object to __proto__
    fred.__proto__ = Object.prototype;

    现在,fred不在继承于Employee.prototype, 而是直接继承了Object.prototype, 也就丢失了所有从Employee.prototype继承来的属性.

    可是,这只适用于可扩展的 对象,一个不可扩展的对象的 __proto__ 属性是不可变的:

    1
    <code class=" language-js"><span class="token keyword"> </span></code>
    var obj = {};
    Object.preventExtensions(obj);
    
    obj.__proto__ = {}; // throws a TypeError

    Note that even Object.prototype's __proto__ property can be redefined as long as the chain leads to null:

    复制代码
    var b = {};
    
    Object.prototype.__proto__ = {
        hi: function () {alert('hi');},
        __proto__: null
    };
    
     
    b.hi();
    复制代码

    If Object.prototype's __proto__ had not been set to null, or had not been set to another object whose prototype chain did not eventually lead explicitly to null, a "cyclic __proto__ value" TypeError would result since the chain must eventually lead to null (as it normally does on Object.prototype).

    参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

    上面的

    bject.prototype.__proto__不能改成
    bject.prototype.prototype.
    为什么?
    我们可以:

    function func(){};
    alert(typeof Object.prototype);//Object,不是Function
    alert(typeof func); //Function。

    可以看出Object.prototype是一个object,没有prototype属性.alert( Object.prototype.prototype);显示undefined。

     (从这里我们可以看出Object 是 一个function,typeof Object 为function。)

    prototype is a property of a Function object. It is the prototype of objects constructed by that function.

    只有函数才有prototype属性,对象没有。

    我们可以看stackoverflow上的一个问题:

    This figure again shows that every object has a prototype. Constructor function Foo also has its own__proto__ which is Function.prototype, and which in turn also references via its __proto__property again to the Object.prototype. Thus, repeat, Foo.prototype is just an explicit property of Foo which refers to the prototype of b and c objects.

    var b =new Foo(20);var c =new Foo(30);

    What are the __proto__ and the prototype properties?

    要仔细理解这幅图 typeof Object=='function' 为true,说明Object类型为function。

    Foo.prototype.__proto__ === Object.prototype 为true。

    个人理解:上面的b->Foo.prototype-->Object.prototype组成了一条链,但b没有在自己中找到相应的属性和方法时,就会向上去寻找 。我们可以这么理解,继承与prototype无关,而与__proto__有关。?我们在这里简单地说下。每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。

    参考:http://www.cnblogs.com/youxin/archive/2013/03/08/2950751.html

    可以看到,Function.prototype是函数Foo的__proto__。我们只要在Function.prototype增加了一个方法,所有的函数都可以调用这个方法,如《javascript精粹》中的一个例子:

    Function.prototype.method=function(name,func){
        if(!this.prototype[name]){
            this.prototype[name]=func;
        }
        return this;
    };
     Foo.method("say2",function(){alert("say2");});
    //和上面的话作用一样:Foo.prototype.say2=function(){alert("say2");};
    
    var c=new Foo();
    c.say2();

    下面的这段代码是我编的。

    function Foo(){}
    var b=new Foo();
    alert(Foo.prototype==Foo.__proto__); //false
    alert( Foo.__proto__); //function Empty(){}
    alert(Foo.prototype); //[object object]

    alert(Foo.prototype.constructor); //function Foo(){}

    与上图对应的代码:

    // a constructor function
    function Foo(y) {
      // which may create objects
      // by specified pattern: they have after
      // creation own "y" property
      this.y = y;
    }
     
    // also "Foo.prototype" stores reference
    // to the prototype of newly created objects,
    // so we may use it to define shared/inherited
    // properties or methods, so the same as in
    // previous example we have:
     
    // inherited property "x"
    Foo.prototype.x = 10;
     
    // and inherited method "calculate"
    Foo.prototype.calculate = function (z) {
      return this.x + this.y + z;
    };
     
    // now create our "b" and "c"
    // objects using "pattern" Foo
    var b = new Foo(20);
    var c = new Foo(30);
     
    // call the inherited method
    b.calculate(30); // 60
    c.calculate(40); // 80
     
    // let's show that we reference
    // properties we expect
     
    console.log(
     
      b.__proto__ === Foo.prototype, // true
      c.__proto__ === Foo.prototype, // true
     
      // also "Foo.prototype" automatically creates
      // a special property "constructor", which is a
      // reference to the constructor function itself;
      // instances "b" and "c" may found it via
      // delegation and use to check their constructor
     
      b.constructor === Foo, // true
      c.constructor === Foo, // true
      Foo.prototype.constructor === Foo // true
     
      b.calculate === b.__proto__.calculate, // true
      b.__proto__.calculate === Foo.prototype.calculate // true
     
    );

    具体参考:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

    答案1:

    __proto__ is internal property of an object, pointing to its prototype. Current standards provide an equivalent Object.getPrototypeOf(O) method, though de facto standard __proto__ is quicker.

    You can find instanceof relationships by comparing a function's prototype to an object's__proto__ chain, and you can break these relationships by changing prototype.

    
    
    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    
    var myPoint = new Point();
    
    // the following are all true
    myPoint.__proto__ == Point.prototype
    myPoint.__proto__.__proto__ == Object.prototype
    myPoint instanceof Point;
    myPoint instanceof Object;
    
    

    Here Point is a constructor function, it builds an object (data structure) procedurally. myPoint is an object constructed by Point() so Point.prototype gets saved to myPoint.__proto__ at that time.

    答案2:

    __proto__ is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build __proto__ when you create an object with new:

    (new Foo).__proto__ ===Foo.prototype
    (newFoo).prototype ===undefined
    转自:http://stackoverflow.com/questions/9959727/what-is-the-difference-between-proto-and-prototype-in-javascript

    js中__proto__和prototype的区别和关系?

    几乎任何对象有一个[[prototype]]属性,在标准中,这是一个隐藏属性。该属性指向的是这个对象的原型。

    那么一个对象的[[prototype]]属性究竟怎么决定呢?这是由构造该对象的方法决定的。据我所知有三种构造一个对象的方法:
    1. 这个对象是通过对象字面量构造出来的。
      var person1 = {
          name: 'cyl',
          sex: 'male'
      };
      
      形如这个形式的叫做对象字面量。这样子构造出的对象,其[[prototype]]指向Object.prototype
    2. 这个对象是由构造函数构造出来的。
      function Person(){}
      var person1 = new Person();
      
      通过new操作符调用的函数就是构造函数。由构造函数构造的对象,其[[prototype]]指向其构造函数的prototype属性指向的对象。每个函数都有一个prototype属性,其所指向的对象带有constructor属性,这一属性指向函数自身。(在本例中,person1的[[prototype]]指向Person.prototype)
    3. 这个对象是由函数Object.create构造的。
      var person1 = {
          name: 'cyl',
          sex: 'male'
      };
      
      var person2 = Object.create(person1);
      
      本例中,对象person2的[[prototype]]指向对象person1。在没有Object.create函数的日子里,人们是这样做的:
      Object.create = function(p) {
          function f(){}
          f.prototype = p;
          return new f();
      }
      


    然而虽然说[[prototype]]是一个隐藏属性,但很多浏览器都给每一个对象提供.__proto__这一属性,这个属性就是上文反复提到的该对象的[[prototype]]。由于这个属性不标准,因此一般不提倡使用。ES5中用Object.getPrototypeOf函数获得一个对象的[[prototype]]。ES6中,使用Object.setPrototypeOf可以直接修改一个对象的[[prototype]]


    --------------------------------
    至于什么原型链之类的,都很好理解,这里就不说了。


    ------------------------------------
    某答案说.__proto__ === .constructor.prototype是不对的,如果一个对象是通过Object.create函数构造出来的,.那其__proto__就不一定是.constructor.prototype了

    __proto__(隐式原型)与prototype(显式原型)

    1.是什么
    • 显式原型 explicit prototype property:
    每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。
    Note:通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性。(感谢 同学的答案让我知道这一点)
    NOTE Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties. ----- ECMAScript Language Specification
    • 隐式原型 implicit prototype link:
    JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf().
    Note: Object.prototype 这个对象是个例外,它的__proto__值为null
    • 二者的关系:

    隐式原型指向创建这个对象的函数(constructor)的prototype

    2. 作用是什么
    • 显式原型的作用:用来实现基于原型的继承与属性的共享。
    ECMAScript does not use classes such as those in C++, Smalltalk, or Java. Instead objects may be created in various ways including via a literal notation or via constructors which create objects and then execute code that initialises all or part of them by assigning initial values to their properties. Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties.Objects are created by using constructors in new expressions; for example, new Date(2009,11) creates a new Date object. ----ECMAScript Language Specification
    • 隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。
    Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s “prototype” ----ECMAScript Language Specification

    3. __proto__的指向
    __proto__的指向到底如何判断呢?根据ECMA定义 'to the value of its constructor’s "prototype" ' ----指向创建这个对象的函数的显式原型。所以关键的点在于找到创建这个对象的构造函数,接下来就来看一下JS中对象被创建的方式,一眼看过去似乎有三种方式:(1)对象字面量的方式 (2)new 的方式 (3)ES5中的Object.create() 但是我认为本质上只有一种方式,也就是通过new来创建。为什么这么说呢,首先字面量的方式是一种为了开发人员更方便创建对象的一个语法糖,本质就是 var o = new Object(); o.xx = xx;o.yy=yy; 再来看看Object.create(),这是ES5中新增的方法,在这之前这被称为原型式继承,
    道格拉斯在2006年写了一篇文章,题为 Prototypal Inheritance In JavaScript。在这篇文章中,他介绍了一种实现继承的方法,这种方法并没有使用严格意义上的构造函数。他的想法是借助原型可以基于已有的对象创建新对象,同时还不比因此创建自定义类型,为了达到这个目的,他给出了如下函数:
    function object(o){
        function F(){}
        F.prototype = o;
        return new F()
    }
    
    ----- 《JavaScript高级程序设计》P169

    所以从实现代码 return new F() 中我们可以看到,这依然是通过new来创建的。不同之处在于由 Object.create() 创建出来的对象没有构造函数,看到这里你是不是要问,没有构造函数我怎么知道它的__proto__指向哪里呢,其实这里说它没有构造函数是指在 Object.create() 函数外部我们不能访问到它的构造函数,然而在函数内部实现中是有的,它短暂地存在了那么一会儿。假设我们现在就在函数内部,可以看到对象的构造函数是F, 现在
    //以下是用于验证的伪代码
    var f = new F(); 
    //于是有
    f.__proto__ === F.prototype //true
    //又因为
    F.prototype === o;//true
    //所以
    f.__proto__ === o;
    

    因此由Object.create(o)创建出来的对象它的隐式原型指向o。好了,对象的创建方式分析完了,现在你应该能够判断一个对象的__proto__指向谁了。

    好吧,还是举一些一眼看过去比较疑惑的例子来巩固一下。

    • 构造函数的显示原型的隐式原型:
    1. 内建对象(built-in object):比如Array(),Array.prototype.__proto__指向什么?Array.prototype也是一个对象,对象就是由 Object() 这个构造函数创建的,因此Array.prototype.__proto__ === Object.prototype //true,或者也可以这么理解,所有的内建对象都是由Object()创建而来。
    • 自定义对象
    1. 默认情况下:
    function Foo(){}
    var foo = new Foo()
    Foo.prototype.__proto__ === Object.prototype //true 理由同上
    
    2. 其他情况:
    (1)
     function Bar(){}
    //这时我们想让Foo继承Bar
    Foo.prototype = new Bar()
     Foo.prototype.__proto__ === Bar.prototype //true
    
    (2)
    //我们不想让Foo继承谁,但是我们要自己重新定义Foo.prototype
    Foo.prototype = {
      a:10,
      b:-10
    }
    //这种方式就是用了对象字面量的方式来创建一个对象,根据前文所述 
    Foo.prototype.__proto__ === Object.prototype
    

    : 以上两种情况都等于完全重写了Foo.prototype,所以Foo.prototype.constructor也跟着改变了,于是乎constructor这个属性和原来的构造函数Foo()也就切断了联系。

    • 构造函数的隐式原型

    既然是构造函数那么它就是Function()的实例,因此也就指向Function.prototype,比如 Object.__proto__ === Function.prototype

    4. instanceof
    instanceof 操作符的内部实现机制和隐式原型、显式原型有直接的关系。instanceof的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例。它的内部实现原理是这样的:
    //设 L instanceof R 
    //通过判断
     L.__proto__.__proto__ ..... === R.prototype ?
    //最终返回true or false
    
    也就是沿着L的__proto__一直寻找到原型链末端,直到等于R.prototype为止。知道了这个也就知道为什么以下这些奇怪的表达式为什么会得到相应的值了
     Function instanceof Object // true 
     Object instanceof Function // true 
     Function instanceof Function //true
     Object instanceof Object // true
     Number instanceof Number //false

    首先,要明确几个点:
    1.在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。
    即:对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。

    2.方法(Function)
    方法这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

    好啦,知道了这两个基本点,我们来看看上面这副图。
    1.构造函数Foo()
    构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。

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

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

    另外:
    构造函数Foo()除了是方法,也是对象啊,它也有__proto__属性,指向谁呢?
    指向它的构造函数的原型对象呗。函数的构造函数不就是Function嘛,因此这里的__proto__指向了Function.prototype。
    其实除了Foo(),Function(), Object()也是一样的道理。

    原型对象也是对象啊,它的__proto__属性,又指向谁呢?
    同理,指向它的构造函数的原型对象呗。这里是Object.prototype.

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


    总结:
    1.对象有属性__proto__,指向该对象的构造函数的原型对象。
    2.方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。

  • 相关阅读:
    代码命名,代码里的命名规则:错误的和正确的对比 命名方法总结 “自我描述的源代码”用代码表达出你的思想,让其他人通过代码能明白你的意图。
    201508整理:前端工程师的(现在主要的问题、工作选择、技术前景未来)
    java小程序,用java在指定目录或以及子目录中找出同名文件,java File过滤文件名后找同名文件
    excel中的数据粘贴不全到plsql中,excel 粘贴后空白,Excel复制粘贴内容不全
    漫画的由来,卡通漫画、暴走漫画、先锋漫画、漫画与前卫艺术之间的边缘艺术形式、实用漫画、讽刺漫画、幽默漫画的定义
    人性漫画:一个人成功前和成功后赤裸裸的区别 人成功前后对比 成功人发展由来前后结果
    解决IE弹框提示“是否停止运行此脚本”问题
    用UltraEdit判断打开文件的编码类型 用UltraEdit或notepad记事本查看文件编码格式 用UltraEdit查看当前文件编码
    Oracle pl/sql导入sql文件,插入更新数据,中文乱码问题解决方案
    毕胜自述:我是如何把奢侈品打到白菜价的 制造业的电子商务中国零售 小米启示录
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/7985846.html
Copyright © 2020-2023  润新知