javascript創建對象有三種方法:
1)對象直接量
例:var empty = {};
var point = {x:1,y:4};
var book = {
"main title":"JavaScript",
'sub-title':"The Definitive Guide",
"for":"audiences",
author:{
firstname:"David",
surname:"Flanagan"
}
}
2)通過new
例:var o - new Object();
var a = new Array();
var d = new Date();
var r = new RegExp("js");
3)利用ECMAScript5中的Object.create()
例:var o1 = Object.create({x:1,y:2}); //o1繼承了屬性x和y
var o2 = Object.create(null); //o2不繼承任何屬性和方法
var o3 = Object.create(Object.prototype); //o3和{}和new Object()一樣
可以通過任意原型創建新對象(換句話說,可以使任意對象可繼承)
下面的代碼通過原型繼承創建一個新的對象
function inherit(p){
if(p == null) throw TypeError();
if(Object.create){
return Object.create(p);
}
var t = typeof p;
if(t != "Object" && t != "function")throw TypeError();
function f(){};
f.prototype = p;
return new f();
}
利用上面的代碼,可以實現以下繼承
var o = {};
o.x = 1;
var p = inherit(o);
p.y = 2;
var q = inherit(p);
q.z = 3;
console.log(q.x + q.y); //輸出3
另外,假設現在給對象o的屬性x賦值,如果o中已經有了屬性x(不是繼承來的),那麼這個賦值操作值改變這個已有屬性x的值,如果o中不存在x屬性,那麼賦值操作給o添加一個x屬性。如果之前o繼承自屬性x,那麼這個繼承屬性就會被新創建的同名屬性覆蓋了。
所以在javascript中,只有查詢屬性時,才會體會到繼承的存在,而設置屬性則與繼承無關。(正是體現出了繼承)
屬性賦值操作首先檢查原型鏈,一次判斷是否允許賦值操作。如果o繼承自一個只讀屬性,那麼賦值操作就是不允許的。
var unitcircle = {r:1};
var c = inherit(unitcircle);
c.x = 1;c.y = 1;
c.r = 2;
console.log(unitcircle.r); //輸出1
console.log(c.r); //輸出2
屬性訪問錯誤的問題
當一個對象中某個屬性不存在時,訪問這個屬性不會報錯,只會報undefined。
但是如果訪問一個不存在的對象的屬性,則會報一個錯誤 ReferenceError
一種簡練的常用方法,獲取subtitle的length屬性或者undefined
var len = book && book.subtitle && book.subtitle.length;
刪除屬性
delete 運算符只能刪除自有屬性,不能刪除繼承屬性。如果要刪除繼承屬性,必須從定義這個屬性的原型對象上刪除它,這會影響到所有繼承自這個原型的對象。
另外delete只是斷開屬性和宿主對象的聯系,而不會去操作屬性中的屬性。
比如
var a = {p:{x:1}};
var b = a.p;
delete a.p;
console.log(b.x); //輸出1
屬性getter和setter
和數據屬性不同,存取器屬性不具有可讀寫性。如果屬性同時具有getter和setter方法,那麼它是一個讀寫屬性。如果它只有getter方法,那麼它是一個只讀屬性。如果它只有setter方法,那麼它是一個只寫屬性(數據屬性中有一些例外),讀取只寫屬性總是返回undefined。
下面是定義存取器屬性最簡單的方法:
var o = {
//普通數據屬性
data_prop:value,
//存取器屬性都是成對定義的函數
get accessor_prop(){ /* 這裡是函數體 */},
set accessor_prop(value){/* 這裡是函數體 */}
};
下面是一個關於私有屬性的設定(帶$符號的就是私有屬性)
var serialnum = {
//這個數據屬性包含下一個序列號
//$符號暗示這個屬性是一個私有屬性
$n:0,
//返回當前值,然後自增
get next(){ return this.$n++;},
//給n設置新的值,但只有當它比當前值大時才設置成功
set next(n){
if(n >= this.$n)this.$n = n;
else throw "序列號的值不能比當前值小";
}
};
下面是利用getter來設置一個對象,用於返回0~255之間的隨機數
var random = {
get octet(){return Math.floor(Math.random() * 255);},
get unit16(){return Math.floor(Math.random() * 65536);},
get int16(){return Math.floor(Math.random() * 65536) - 32768;}
};
屬性的特性
數據屬性包含下面四個特性:值(value),可寫性(writable),可枚舉性(enumerable)和可配置性(configurable)
存取器屬性包含下面四個特性:讀取(get),寫入(set),可枚舉性(enumerable)和可配置性(configurable)。
下面是存取器屬性的描述符對象:
//返回{value:1, writable:true, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor({x:1},"x");
這些特性是可以設置的:
var o = {};
Object.defineProperty(o, "x", {
value:1,
writable:true,
enumerable:false,
configurable:true
});
o.x; //返回1,不可枚舉
Object.keys(o) //返回[]
//現在對屬性x做出修改,讓它變成只讀
Object.defineProperty(o, "x", {writable:false});
//嘗試改變屬性
o.x = 2; //操作失敗但不報錯,而在嚴格模式中拋出類型錯誤異常
o.x;
//屬性依然可配置,所以可以通過下面的方式進行修改
Object.defineProperty(o, "x", {value:2});
o.x;
//現在將x從數據屬性修改為存儲器屬性
Object.defineProperty(o, "x", {get:function(){return 0;}});
o.x; //返回0
對象的三個屬性
1)原型屬性
原型屬性是對象創建之初就設置好的
創建對象的三種方法,對應原型屬性的來源:
①通過對象直接量創建
原型屬性就是Object.prototype
②通過new創建的對象
原型屬性是使用構造函數的prototype屬性
③通過Object.create()創建
原型屬性來自第一個參數(可以是null)
在ECMAScript5中使用Object.getPrototypeOf來查詢原型
而在ECMAScript3中,則使用obj.constructor.prototype來檢測一個對象的原型
要檢測一個對象是否是另一個對象的原型,可以使用isPrototypeOf()方法
var p = {x:1};
var o = Object.create(p);
p.isPrototypeOf(o); //返回true
Object.prototype.isPrototypeOf(o); //返回true
2)類屬性
可以通過下面的代碼來獲取類屬性
function classof(o){
if(o===null)return "Null";
if(