zoukankan      html  css  js  c++  java
  • 转: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 print. In 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/

  • 相关阅读:
    MT【139】公比为有理数
    鼠标右键怎么清除Catalyst Control Center
    人脸识别技术框架
    Qt生成灰度图(转载)
    人脸识别必读的N篇文章
    QT+vs2010下改变可执行程序的图标
    内部排序技术
    ubuntu下配置qt+opengl+opencv
    HOG算子
    基于HOG特征的Adaboost行人检测
  • 原文地址:https://www.cnblogs.com/youxin/p/2950751.html
Copyright © 2011-2022 走看看