javascript prototype原型鏈的原理

javascript prototype原型鏈的原理

 

說到prototype,就不得不先說下new的過程。javascript

咱們先看看這樣一段代碼:html

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

 

很簡單的一段代碼,咱們來看看這個new究竟作了什麼?咱們能夠把new的過程拆分紅如下三步:java

<1> var p={}; 也就是說,初始化一個對象p。react

<2> p.__proto__=Person.prototype; 2個下劃線+proto+2個下劃線git

<3> Person.call(p);也就是說構造p,也能夠稱之爲初始化p。github

關鍵在於第二步,咱們來證實一下:web

 

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

這段代碼會返回true。說明咱們步驟2的正確。express

那麼__proto__是什麼?咱們在這裏簡單地說下。每一個對象都會在其內部初始化一個屬性,就是__proto__,當咱們訪問一個對象的屬性時,若是這個對象內部不存在這個屬性,那麼他就會去__proto__裏找這個屬性,這個__proto__又會有本身的__proto__,因而就這樣一直找下去,也就是咱們平時所說的原型鏈的概念。瀏覽器

按照標準,__proto__是不對外公開的,也就是說是個私有屬性,可是Firefox的引擎將他暴露了出來成爲了一個共有的屬性,咱們能夠對外訪問和設置。cookie

好,概念說清了,讓咱們看一下下面這些代碼:

 

 <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指向該方法的原型對象。

相關文章
相關標籤/搜索