w3schools JavaScript.prototype如何工作?




mdn prototype (17)

它只是你已經有了Object.new的對象,但是在使用構造器語法時你還沒有一個對象。

我不是那麼喜歡動態編程語言,但是我已經寫了我的JavaScript代碼。 我從來沒有真正理解這個基於原型的編程,有沒有人知道這是如何工作的?

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

我記得曾經和人們討論過很多次(我不確定自己在做什麼),但據我了解,沒有一個班級的概念。 它只是一個對象,這些對象的實例是原始的克隆,對嗎?

但在JavaScript中這個.prototype屬性的確切目的是什麼? 它與實例化對像有什麼關係?

編輯

這些slides對於理解這個主題非常有幫助。


Let me tell you my understanding of prototypes. I am not going to compare the inheritance here with other languages. I wish people would stop comparing languages, and just understand the language as itself. Understanding prototypes and prototypal inheritance is so simple, as I will show you below.

Prototype is like a model, based on which you create a product. The crucial point to understand is that when you create an object using another object as it's prototype, the link between the prototype and the product is ever-lasting. 例如:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Every object contains an internal property called the [[prototype]], which can be accessed by the Object.getPrototypeOf() function. Object.create(model) creates a new object and sets it's [[prototype]] property to the object model . Hence when you do Object.getPrototypeOf(product) , you will get the object model .

Properties in the product are handled in the following way:

  • When a property is accessed to just read it's value, its looked up in the scope chain. The search for the variable starts from the product upwards to it's prototype. If such a variable is found in the search, the search is stopped right there, and the value is returned. If such a variable cannot be found in the scope chain, undefined is returned.
  • When a property is written(altered), then the property is always written on the product object. If the product does not have such a property already, it is implicitly created and written.

Such a linking of objects using the prototype property is called prototypal inheritance. There, it is so simple, agree?


prototype允許您創建課程。 如果你不使用prototype那麼它就會變成靜態的。

這是一個簡短的例子。

var obj = new Object();
obj.test = function() { alert('Hello?'); };

在上述情況下,您有靜態函數調用測試。 這個函數只能通過obj.test來訪問,你可以想像obj是一個類。

在下面的代碼中

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

obj已經成為一個現在可以實例化的類。 可以存在多個obj實例,它們都具有test功能。

以上是我的理解。 我把它變成一個社區wiki,所以如果我錯了,人們可以糾正我。


The concept of prototypal inheritance is one of the most complicated for many developers. Let's try to understand the root of problem to understand prototypal inheritance better. Let's start with a plain function.

If we use a new operator on the Tree function , we call it as a constructor function.

Every JavaScript function has a prototype . When you log the Tree.prototype , you get...

If you look at the above console.log() output, you could a see a constructor property on Tree.prototype and a __proto__ property too. The __proto__ represents the prototype that this function is based off, and since this is just a plain JavaScript function with no inheritance set up yet, it refers to the Object prototype which is something just built in to JavaScript...

Object.prototype

This has things like .toString, .toValue, .hasOwnProperty etc...

__proto__ which was brought my mozilla is deprecated and is replaced by Object.getPrototypeOf method to get the object's prototype .

Object.getPrototypeOf(Tree.prototype); // Object {} 

Let's add a method to our Tree prototype .

We have modified the Root and added a function branch to it.

That means when you create an instance of Tree , you can call it's branch method.

We can also add primitives or objects to our Prototype .

Let's add a child-tree to our Tree .

Here the Child inherits its prototype from Tree, what we are doing here is using Object.create() method to create a new object based off what you pass, here it is Tree.prototype . In this case what we're doing is setting the prototype of Child to a new object that looks identical to the Tree prototype. Next we are setting the Child's constructor to Child , if we don't it would point to Tree() .

Child now has its own prototype , its __proto__ points to Tree and Tree's prototype points to base Object .

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Now you create an instance of Child and call branch which is originally available in Tree . We haven't actually defined our branch on the Child prototype . BUT, in the Root prototype which Child inherits from.

In JS everything is not an object, everything can act like an object.

Javascript has primitives like strings, number, booleans, undefined, null. They are not object(ie reference types) , but certainly can act like an object . Let's look at an example here.

In the first line of this listing, a primitive string value is assigned to name. The second line treats name like an object and calls charAt(0) using dot notation.

This is what happens behind the scenes: // what the JavaScript engine does

The String object exists only for one statement before it's destroyed (a process called autoboxing ). Let's again get back to our prototypal inheritance .

  • Javascript supports inheritance via delegation based on prototypes .
  • Each Function has a prototype property, which refers to another object.
  • properties/functions are looked from the object itself or via prototype chain if it does not exist

A prototype in JS is an object which yields you to the parent of another object . [ie.. delegation] Delegation means that if you are unable to do something, you'll tell someone else to do it for you.

https://jsfiddle.net/say0tzpL/1/

If you look up the above fiddle, dog has access to toString method, but its not available in it, but available via the prototype chain which delegates to Object.prototype

If you look at the below one, we are trying to access the call method which is available in every function .

https://jsfiddle.net/rknffckc/

If you look up the above fiddle, Profile Function has access to call method, but its not available in it, but available via the prototype chain which delegates to Function.prototype

Note: prototype is a property of the function constructor, whereas __proto__ is a property of the objects constructed from the function constructor. Every function comes with a prototype property whose value is an empty object . When we create an instance of the function, we get an internal property [[Prototype]] or __proto__ whose reference is the prototype of the Function constructor .

The above diagram looks bit complicated, but brings out the whole picture on how prototype chaining works. Let's walk through this slowly:

There are two instance b1 and b2 , whose constructor is Bar and parent is Foo and has two methods from prototype chain identify and speak via Bar and Foo

https://jsfiddle.net/kbp7jr7n/

If you look up the code above, we have Foo constructor who has the method identify() and Bar constructor which has speak method. We create two Bar instance b1 and b2 whose parent type is Foo . Now while calling speak method of Bar , we are able to identify the who is calling the speak via prototype chain.

Bar now has all the methods of Foo which are defined in its prototype . Let's dig further in understanding the Object.prototype and Function.prototype and how they are related. If you look up the constructor of Foo , Bar and Object are Function constructor .

The prototype of Bar is Foo , prototype of Foo is Object and if you look closely the prototype of Foo is related to Object.prototype .

Before we close this down, let's just wrap with a small piece of code here to summarize everything above . We are using instanceof operator here to check whether an object has in its prototype chain the prototype property of a constructor which below summarizes the entire big diagram.

I hope this add's some information, I know this kinda could be big to grasp... in simple words its it's just objects linked to objects!!!!


Javascript通常沒有繼承,但它有原型鏈。

原型鏈

如果在對像中找不到對象的成員,它會在原型鏈中查找它。 該鏈由其他對象組成。 給定實例的原型可以通過__proto__變量訪問。 每個對像都有一個,因為在javascript中類和實例之間沒有區別。

向原型添加函數/變量的好處是它只能在內存中存儲一次,而不是每個實例。

它對於繼承也很有用,因為原型鏈可以由許多其他對象組成。


what is the exact purpose of this ".prototype" property?

The interface to standard classes become extensible. For example, you are using the Array class and you also need to add a custom serializer for all your array objects. Would you spend time coding up a subclass, or use composition or ... The prototype property solves this by letting the users control the exact set of members/methods available to a class.

Think of prototypes as an extra vtable-pointer. When some members are missing from the original class, the prototype is looked up at runtime.


When a constructor creates an object, that object implicitly references the constructor's “prototype” property for the purpose of resolving property references. The constructor's “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object's prototype are shared, through inheritance, by all objects sharing the prototype.


我扮演一位JavaScript老師的角色,原型概念一直是我教授的有爭議的話題。 我花了一段時間想出了一個澄清概念的好方法,現在我將在本文中嘗試解釋JavaScript .prototype如何工作。

這是一個非常簡單的基於原型的對像模型,在解釋過程中會被視為一個樣本,目前還沒有評論:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

在通過原型概念之前,我們必須考慮一些關鍵點。

1- JavaScript功能如何實際工作:

為了邁出第一步,我們必須弄清楚JavaScript函數實際上是如何工作的,就像函數使用this關鍵字一樣,或者只是作為具有參數的常規函數,它做了什麼以及返回什麼。

假設我們要創建一個Person像模型。 但在這一步中,我會試著做同樣的事情,而不使用prototypenew關鍵字

所以在這一步中, functionsobjectsthis關鍵字就是我們所有的。

第一個問題是如何使用this關鍵字而不使用new關鍵字

所以要回答這個問題,假設我們有一個空對象,並且有兩個函數:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

現在不使用new關鍵字我們可以如何使用這些功能。 所以JavaScript有三種不同的方式來做到這一點:

一個。 第一種方法就是將函數作為常規函數調用:

Person("George");
getName();//would print the "George" in the console

在這種情況下,這將是當前的上下文對象,它通常是瀏覽器中的全局window對像或Node.js GLOBAL 。 這意味著我們將在瀏覽器中使用window.name或在Node.js中使用GLOBAL.name,其中“George”作為其值。

灣 我們可以它們附加到一個對像上,作為它的屬性

- 最簡單的方法是修改空的person對象,如:

person.Person = Person;
person.getName = getName;

這樣我們可以稱他們為:

person.Person("George");
person.getName();// -->"George"

現在這個person就像是:

Object {Person: function, getName: function, name: "George"}

- 將屬性附加到對象的另一種方法是使用該對象的prototype ,該對象的prototype可以在名為__proto__任何JavaScript對像中找到,並且我試圖在摘要部分對其進行解釋。 所以我們可以通過這樣做來得到相似的結果:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

但是這樣我們實際上正在修改Object.prototype ,因為每當我們使用文字( { ... } )創建一個JavaScript對象時,它都是基於Object.prototype創建的,這意味著它會被附加到新創建的對像作為名為__proto__的屬性,所以如果我們改變它,就像我們在前面的代碼片段中做的那樣,所有的JavaScript對像都會被改變,這不是一個好習慣。 那麼現在最好的做法是什麼?

person.__proto__ = {
    Person: Person,
    getName: getName
};

現在其他的物體是和平的,但它似乎並不是一個好的做法。 所以我們還有一個解決方案,但是要使用這個解決方案,我們應該回到創建person對象的代碼行( var person = {}; ),然後將其更改為:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

它所做的是創建一個新的JavaScript Object ,並將propertiesObject附加到__proto__屬性。 所以要確保你能做到:

console.log(person.__proto__===propertiesObject); //true

但棘手的是,您可以訪問person對象第一級__proto__定義的所有屬性(請參閱摘要部分以獲取更多詳細信息)。

正如你所看到的使用這兩種方式中的任何一種, this將完全指向person

C。 JavaScript有另一種方法來提供這個函數,它使用callapply來調用函數。

apply()方法使用給定的這個值和作為數組(或類似數組的對象)提供的參數來調用函數。

call()方法使用給定的值和單獨提供的參數來調用函數。

這種方式是我最喜歡的,我們可以輕鬆地調用我們的功能,如:

Person.call(person, "George");

要么

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

這三種方法是找出.prototype功能的重要初始步驟。

2- new關鍵字如何工作?

這是理解.prototype功能的第二步。這是我用來模擬過程的:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

在這部分中,我將嘗試在使用new關鍵字時,不使用new關鍵字和prototype ,而是採取JavaScript所採取的所有步驟。 所以當我們做new Person("George")Person函數作為構造函數,這些就是JavaScript一個接一個地做的事情:

一個。 首先它會創建一個空對象,基本上是一個空的哈希,如:

var newObject = {};

灣 JavaScript採取的下一步是所有原型對象附加到新創建的對象

我們在這裡有my_person_prototype類似於原型對象。

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

這不是JavaScript實際附加原型中定義的屬性的方式。 實際的方式與原型鏈概念有關。

一個。 &b。 而不是這兩個步驟,你可以通過做相同的結果:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

現在我們可以在我們的my_person_prototype調用getName函數:

newObject.getName();

C。 然後它將該對象賦予構造函數,

我們可以用我們的例子來做到這一點:

Person.call(newObject, "George");

要么

Person.apply(newObject, ["George"]);

那麼構造函數可以做任何想做的事情,因為這個構造函數的內部是剛創建的對象。

現在是模擬其他步驟之前的最終結果:Object {name:“George”}

概要:

基本上,當你在一個函數上使用new關鍵字時,你需要調用它並且該函數作為構造函數,所以當你說:

new FunctionName()

JavaScript在內部創建一個對象,一個空的散列,然後將該對象賦予構造函數,然後構造函數可以做任何想要的事情,因為這個構造函數的內部是剛剛創建的對象,然後當然會給你那個對象如果你沒有在你的函數中使用return語句,或者你已經把return undefined;return undefined; 在函數體的末尾。

所以當JavaScript去查找一個對象的屬性時,它首先要做的事情就是查看對象。 然後有一個秘密屬性[[prototype]] ,我們通常將它放在__proto__這個屬性中,這個屬性就是JavaScript接下來要看的東西。 並且,當它通過__proto__查看時,只要它是另一個JavaScript對象,它就有它自己的__proto__屬性,它會一直向上,直到它到達下一個__proto__為空的點。 該點是JavaScript中唯一的對象,其__proto__屬性為null是Object.prototype對象:

console.log(Object.prototype.__proto__===null);//true

這就是繼承在JavaScript中的作用。

換句話說,當你在一個函數上有一個prototype屬性並且你調用了一個新的屬性時,在JavaScript完成查看這個新創建的對象的屬性之後,它會查看函數的.prototype ,並且這個對像也是可能的有它自己的內部原型。 等等。


閱讀完本文後,我對JavaScript Prototype Chain感到困惑,然後找到了這些圖表

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance

這是一個清晰的圖表,顯示了原型鏈的JavaScript繼承

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

這個包含一個代碼和幾個漂亮圖表的例子。

原型鏈最終回落到Object.prototype。

原型鏈可以在技術上進行擴展,只要您想要,每次通過將子類的原型設置為等於父類的對象。

希望這對理解JavaScript Prototype Chain也很有幫助。


I found it helpful to explain the "prototype chain" as recursive convention when obj_n.prop_X is being referenced:

if obj_n.prop_X doesn't exist, check obj_n+1.prop_X where obj_n+1 = obj_n.[[prototype]]

If the prop_X is finally found in the k-th prototype object then

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

You can find a graph of the relation of Javascript objects by their properties here:

http://jsobjects.org


There's two distinct but related entities here that need explaining:

  • The .prototype property of functions.
  • The [[Prototype]] [1] property of all objects [2] .

These are two different things.

The [[Prototype]] property:

This is a property that exists on all [2] objects.

What's stored here is another object, which, as an object itself, has a [[Prototype]] of its own that points to another object. That other object has a [[Prototype]] of its own. This story continues until you reach the prototypical object that provides methods that are accessible on all objects (like .toString ).

The [[Prototype]] property is part of what forms the [[Prototype]] chain. This chain of [[Prototype]] objects is what is examined when, for example, [[Get]] or [[Set]] operations are performed on an object:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

The .prototype property:

This is a property that is only found on functions. Using a very simple function:

function Bar(){};

The .prototype property holds an object that will be assigned to b.[[Prototype]] when you do var b = new Bar . You can easily examine this:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

One of the most important .prototype s is that Object.prototype . This prototype holds the prototypical object that all [[Prototype]] chains contain. On it, all the available methods for new objects are defined:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Now, since .prototype is an object, it has a [[Prototype]] property. When you don't make any assignments to Function.prototype , the .prototype 's [[Prototype]] points to the prototypical object ( Object.prototype ). This is automatically performed anytime you create a new function.

This way, any time you do new Bar; the prototype chain is set up for you, you get everything defined on Bar.prototype and everything defined on Object.prototype :

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

When you do make assignments to Function.prototype all you are doing is extending the prototype chain to include another object. It's like an insertion in a singly linked list.

This basically alters the [[Prototype]] chain allowing properties that are defined on the object assigned to Function.prototype to be seen by any object created by the function.

[1:這不會混淆任何人; 通過提供__proto__性能在許多實現。
[2]:除了null


在使用Java,C#或C ++等經典繼承的語言中,首先創建一個類 - 您的對象的藍圖 - 然後您可以從該類創建新對象,或者可以擴展該類,定義一個新的類來增強原來的班級。

在JavaScript中,你首先創建一個對象(沒有類的概念),那麼你可以擴充自己的對像或者從中創建新的對象。 這並不困難,但有點過於陌生,很難代謝習慣於傳統方式的人。

例:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

到現在為止,我一直在擴展基礎對象,現在我創建了另一個對象,然後從Person繼承。

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

儘管如我所說,我無法在Person上調用setAmountDue(),getAmountDue()。

//The following statement generates an error.
john.setAmountDue(1000);

I always like analogies when it comes to understand this type of stuff. 'Prototypical inheritance' is pretty confusing in comparison to class bass inheritance in my opinion, even though prototypes are much simpler paradigm. In fact with prototypes, there really is no inheritance, so the name in and of itself misleading, it's more a type of 'delegation'.

Imagine this ....

You're in high-school, and you're in class and have a quiz that's due today, but you don't have a pen to fill out your answers. Doh!

You're sitting next to your friend Finnius, who might have a pen. You ask, and he looks around his desk unsuccessfully, but instead of saying "I don't have a pen", he's a nice friend he checks with his other friend Derp if he has a pen. Derp does indeed have a spare pen and passes it back to Finnius, who passes it over to you to complete your quiz. Derp has entrusted the pen to Finnius, who has delegated the pen to you for use.

What is important here is that Derp does not give the pen to you, as you don't have a direct relationship with him.

This, is a simplified example of how prototypes work, where a tree of data is searched for the thing you're looking for.


Consider the following keyValueStore object :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

I can create a new instance of this object by doing this :

kvs = keyValueStore.create();

Each instance of this object would have the following public properties :

  • data
  • get
  • set
  • delete
  • getLength

Now, suppose we create 100 instances of this keyValueStore object. Even though get , set , delete , getLength will do the exact same thing for each of these 100 instances, every instance has its own copy of this function.

Now, imagine if you could have just a single get , set , delete and getLength copy, and each instance would reference that same function. This would be better for performance and require less memory.

That's where prototypes come in. A prototype is a "blueprint" of properties that is inherited but not copied by instances. So this means that it exists only once in memory for all instances of an object and is shared by all of those instances.

Now, consider the keyValueStore object again. I could rewrite it like this :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

This does EXACTLY the same as the previous version of the keyValueStore object, except that all of its methods are now put in a prototype. What this means, is that all of the 100 instances now share these four methods instead of each having their own copy.



這篇文章很長。 但是我相信它會清除你關於JavaScript繼承的“原型”本質的大部分查詢。 甚至更多。 請閱讀完整的文章。

JavaScript基本上有兩種數據類型

  • 非對象
  • 對象

非對象

以下是非對像數據類型

  • 數量(包括NaN和Infinity)
  • 布爾值(true,false)
  • 未定義

當您使用typeof運算符時,這些數據類型會返回以下內容

typeof “string literal” (或者一個包含字符串文字的變量)=== 'string'

typeof 5 (或任何數字文字或包含數字文字或NaN或Infynity的變量)=== 'number'

typeof true (或false或包含truefalse的變量)=== 'boolean'

typeof undefined (或未定義的變量或包含未定義的變量)=== 'undefined'

字符串數字布爾數據類型可以表示為對象非對象 。當它們被表示為對象時,它們的typeof總是==='object'。 一旦我們理解了對像數據類型,我們就會回到這個。

對象

對像數據類型可以進一步分為兩種類型

  1. 函數類型對象
  2. 非函數類型的對象

函數類型對 像是typeof運算符返回字符串'function'的 對象 。 All the user defined functions and all the JavaScript built in objects that can create new objects by using new operator fall into this category. For eg.

  • Object
  • String
  • Boolean
  • Array
  • Typed Arrays
  • RegExp
  • 功能
  • All the other built in objects that can create new objects by using new operator
  • function UserDefinedFunction (){ /*user defined code */ }

So, typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) === typeof(UserDefinedFunction) === 'function'

All the Function type objects are actually instances of the built in JavaScript object Function (including the Function object ie it is recursively defined). It is as if the these objects have been defined in the following way

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

As mentioned, the Function type objects can further create new objects using the new operator . For eg an object of type Object , String , Number , Boolean , Array , RegExp Or UserDefinedFunction can be created by using

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

The objects thus created are all Non Function type objects and return their typeof === 'object' . In all these cases the object "a" cannot further create objects using operator new. So the following is wrong

var b=new a() //error. a is not typeof==='function'

The built in object Math is typeof === 'object' . Hence a new object of type Math cannot be created by new operator.

var b=new Math() //error. Math is not typeof==='function'

Also notice that Object , Array and RegExp functions can create a new object without even using operator new . However the follwing ones don't.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

The user defined functions are special case.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Since the Function type objects can create new objects they are also called Constructors .

Every Constructor/Function (whether built in or user defined) when defined automatically has a property called "prototype" whose value by default is set as an object. This object itself has a property called "constructor" which by default references back the Constructor/Function .

For example when we define a function

function UserDefinedFunction()
{
}

following automatically happens

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

This "prototype" property is only present in the Function type objects (and never in Non Function type objects ).

This is because when a new object is created (using new operator)it inherits all properties and methods from Constructor function's current prototype object ie an internal reference is created in the newly created object that references the object referenced by Constructor function's current prototype object.

This "internal reference" that is created in the object for referencing inherited properties is known as the object's prototype (which references the object referenced by Constructor's "prototype" property but is different from it). For any object (Function or Non Function) this can be retrieved using Object.getPrototypeOf() method. Using this method one can trace the prototype chain of an object.

Also, every object that is created ( Function type or Non Function type ) has a "constructor" property which is inherited from the object referenced by prototype property of the Constructor function. By default this "constructor" property references the Constructor function that created it (if the Constructor Function's default "prototype" is not changed).

For all Function type objects the constructor function is always function Function(){}

For Non Function type objects (eg Javascript Built in Math object) the constructor function is the function that created it. For Math object it is function Object(){} .

All the concept explained above can be a little daunting to understand without any supporting code. Please go through the following code line by line to understand the concept. Try to execute it to have a better understanding.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

The prototype chain of every object ultimately traces back to Object.prototype (which itself does not have any prototype object) . Following code can be used for tracing the prototype chain of an object

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

The prototype chain for various objects work out as follows.

  • Every Function object (including built in Function object)-> Function.prototype -> Object.prototype -> null
  • Simple Objects (created By new Object() or {} including built in Math object)-> Object.prototype -> null
  • Object created with new or Object.create -> One or More prototype chains -> Object.prototype -> null

For creating an object without any prototype use the following:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

One might think that setting the prototype property of the Constructor to null shall create an object with a null prototype. However in such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object. This is demonstrated by the following code

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Following in the summary of this article

  • There are two types of objects Function types and Non Function types
  • Only Function type objects can create a new object using the operator new . The objects thus created are Non Function type objects. The Non Function type objects cannot further create an object using operator new .

  • All Function type objects by default have a "prototype" property. This "prototype" property references an object that has a "constructor" property that by default references the Function type object itself.

  • All objects ( Function type and Non Function type ) have a "constructor" property that by default references the Function type object / Constructor that created it.

  • Every object that gets created internally references the object referenced by "prototype" property of the Constructor that created it. This object is known as the created object's prototype (which is different from Function type objects "prototype" property which it references) . This way the created object can directly access the methods and properties defined in object referenced by the Constructor's "prototype" property (at the time of object creation).

  • An object's prototype (and hence its inherited property names) can be retrieved using the Object.getPrototypeOf() method. In fact this method can be used for navigating the entire prototype chain of the object.

  • The prototype chain of every object ultimately traces back to Object.prototype (Unless the object is created using Object.create(null) in which case the object has no prototype).

  • typeof(new Array())==='object' is by design of language and not a mistake as pointed by Douglas Crockford

  • Setting the prototype property of the Constructor to null(or undefined,number,true,false,string) shall not create an object with a null prototype. In such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object.

希望這可以幫助。






prototype-oriented