### 一、對象
~~~
var obj = {};
obj[true] = "foo";
obj[3] = "bar";
obj[obj] = "baz";
obj["true"];
obj["3"];
obj["[object Object]"];
~~~
### 二、數組也是對象
~~~
var ary = ["foo", 42, "bar"];
ary.baz = "baz";
ary.length; //3
ary.baz;
/* 如果試圖想數組添加一個屬性,但是屬性名“看起來”像一個數字,那么它會變成一個數值下標 */
ary["4"] = "baz";
ary.length; //5
ary[4];
~~~
### 三、對象屬性
writable:可寫(修改)
enumerable:可枚舉(for ... in)
configurable:可配置(配置、刪除)
**注意:delete只能刪除對象(可刪除)屬性,即configurable為true的屬性。**
~~~
var obj = {};
Object.defineProperty(obj, "a", {enumerable:true, value:2});
Object.defineProperty(obj, "b", {enumerable:false, value:3});
/* 檢查給定屬性名是否直接存在于對象中(而不是在原型鏈上)并滿足enumerable:true */
obj.propertyIsEnumerable("a"); //true
obj.propertyIsEnumerable("b"); //false
/* 返回一個數組,包含所有(自身)可枚舉屬性 */
Object.keys(obj); //["a"]
/* 返回一個數組,包含所有(自身)屬性 */
Object.getOwnPropertyNames(obj); //["a", "b"]
~~~
注意:in和hasOwnProperty(..)的區別在于是否查找[[Prototype]]鏈,然而,Object.keys(..)和Object.getOwnPropertyNames(..)都只會查找對象直接包含的屬性。
### 四、對象原型
獲取對象原型:Object.getPropertyOf(..)
~~~
function Foo(){}
var foo = new Foo();
Foo.prototype === Object.getPrototypeOf(foo); //true
~~~
注意:在面向類的語言中,類可以復制(實例化)多次,就像用模具制作東西一樣。在JavaScript中,并沒有類似的復制機制。不能創建一個類的多個實例,只能創建多個對象,它們[[property]]關聯的是同一個對象。這樣就可用通過委托訪問對象的屬性和方法了。
**類理論:**在構造完成后,通常只需要操作這些實例(而不是類),因為每個實例都有你需要完成任務的所有行為。
### 五、原型鏈[[prototype]]
[[prototype]]機制就是存在于對象中的一個內部鏈接,它會引用其他對象。
~~~
var foo = {
something:function(){
// ....
}
};
var bar = Object.create(foo); // 創建新對象bar,并將其關聯到對象foo上。
~~~
Object.create(null)會創建一個擁有空鏈接的對象,這個對象無法進行委托,其不存在原型鏈,所以instanceof總是返回false。其不受原型鏈干擾,非常適合用來存儲數據!
**對象之間的關系不是復制而是委托!!!**
談及原型鏈不得不提及我們經常在JavaScript中的類型檢查!即內省:檢查實例的類型;主要目的是通過創建方式來判斷對象的結構和功能。
方式一:a instanceof Foo
方式二:**“鴨子類型”**:如果看起來像鴨子,叫起來像鴨子,那就一定是鴨子。
~~~
if(a.something) {
a.something(); // something()并不一定在a自身上
}
~~~
方式三:isPrototypeOf()、getPrototypeOf()
**建議使用方式三!!!**
### 六、函數
JavaScript中的函數無法(用標準、可靠的方法)真正地復制,所以只能共享函數對象的引用。這意味著,如果修改共享函數,比如增加一個屬性,所有引用地方都會隨之修改!
### 七、構造函數
~~~
function Foo(){}
Foo.prototype = {};
var a1 = new Foo();
a1.constructor === Foo; //false
a1.constructor === Object; //true
~~~
**詳解:**
創建一個新對象并替換了函數默認的.prototype對象引用,那么新對象并不會自動獲得.constructor屬性。
a1并沒有.constructor屬性,所以它會委托[[Prototype]]鏈上的Foo.prototype。但是這個對象也沒有.constructor屬性,所以它會繼續委托給鏈頂端的Object.protoype。
實際上,對象的.constructor會默認指向一個函數,這個函數可以通過對象的.prototype引用!
**總之,constructor并不表示被構造!!!改變prototype并不能徹底改變繼承關系!!!**
~~~
function Foo(name){
this.name = "FenFei"
}
function Bar(){}
~~~
**錯誤理解一:**
~~~
Bar.prototype = Foo.prototype;
Bar.prototype.myTest = 123;
new Foo().myTest; //123
~~~
并不會創建一個關聯到Bar.prototype的新對象,它只是讓Bar.prototype直接引用Foo.prototype對象。當執行類似Bar.prototype.myTest = 123的賦值語句時會直接修改Foo.prototype對象本身。
**錯誤理解二:**
~~~
Bar.prototype = new Foo();
new Bar("Camile").name; //FenFei
~~~
的確會創建一個關聯到Bar.prototype的新對象。但是它使用了Foo()的“構造函數調用”,如果Foo有副作用(比如注冊到其他對象、給this添加數據屬性等等),就會影響Bar()的后代。
**正確處理方式一[ES6之前]:**
~~~
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.myTest = 123;
new Foo().myTest; //undefined
new Bar("Camile").name; //undefined
~~~
**正確處理方式二[ES6之后]:**
~~~
Object.setPrototypeOf(Bar.prototype, Foo.prototype);
Bar.prototype.myTest = 123;
new Foo().myTest; //undefined
new Bar("Camile").name; //undefined
~~~
### 八、函數關系
(1)在a的整條[[prototype]]鏈中是否有指向Foo.prototype的對象?
~~~
a instanceof Foo;
~~~
(2)在a的整條[[prototype]]鏈中是否出現過Foo.prototype?
~~~
Foo.prototype.isPrototypeOf(a); // 不需要間接引用函數Foo,它的prototype屬性會自動訪問。
~~~
(3)區別
isPrototypeOf()方法可以判斷對象間的關系。
b是否出現在c的[[prototype]]鏈中?
~~~
b.isPrototypeOf(c);
~~~
**示例:**
~~~
function Foo(){}
var a = new Foo();
console.log(a instanceof Foo); // true
console.log(Foo.prototype.isPrototypeOf(a)); // true
var b = {};
var c = Object.create(b);
console.log(b.isPrototypeOf(c)); // true
console.log(b instanceof c); // TypeError
~~~
在傳統面向類的語言中,類定義之后就不會進行修改,所以類的設計模式不支持修改。但是JavaScript最強大的特性之一就是它的動態性,任何對象的定義都可以修改(除非你把它設置成不可變)!
[轉載請標明出處:[http://blog.csdn.net/ligang2585116](http://blog.csdn.net/ligang2585116)]