屬性的getter和setter
JavaScript的屬性值可以用getter和setter方法代替,由getter和setter定義的屬性稱為存取器屬性。存取器屬性是可以繼承的。
用例子說明:
var p = {
//x和y是普通的可讀寫的數據屬性
x: 1.0,
y: 1.0,
// r是可讀寫的存取器屬性,它有getter和setter
// 函數體結束后不要忘記帶上逗號
get r(){ return Math.sqrt(this.x*this.x + this.y*this.y) },
set r(newValue){
var oldValue = Math.sqrt(this.x*this.x + this.y*this.y);
var ratio = newValue/oldValue;
this.x *= ratio;
this.y *= ratio;
},
// theta是只讀存取器屬性,它只有getter方法
get theta(){return Math.atan2(this.y,this.x)},
};
console.log(p); // ==> (1,1) r = 1.41421356 theta = 0.785398
p.r = 2;
console.log(p); // ==> (1.41421356,1.41421356) r = 2 theta = 0.785398
屬性特性
數據屬性特性分為:值(value),可寫性(writable),可枚舉性,可配置性。
存取器屬性特性分為:讀取(get),寫入(set),可枚舉性,可配置性。
我們創建的屬性默認都是可寫、可枚舉、可配置的。
我們通過例子來介紹一個方法(Object.definePeoperty()
):
// 我們可以調用Object.definePeoperty()來設置對象屬性的特性
// 我們可以調用Object.definePeoperty()來設置對象屬性的特性
/*
*參數1:傳入的對象 (Object)
*參數2:要創建或者修改的屬性的名稱 (String)
*參數3:屬性的特性(對于新創建的自有屬性四個屬性默認都為false或者undefined,對于已有屬性來說,默認的特性值是沒有做任何修改的)
*(注意:此方法 只能修改自有屬性或者是創建自有屬性,但是不能修改繼承屬性)。
*/
//----看點1
// 簡單介紹一下 方法的使用
var obj = {};
Object.defineProperty(obj,"x",{value:2017,
writable:true,
enumerable:true,
configurable:true});
document.write(obj.x+"<br/>"); // ==> 2017
// 這個方法同樣是有返回值的,是將修改的對象返回
var obj1 = Object.defineProperty({},"x",{value:2017,
writable:true,
enumerable:true,
configurable:true});
document.write(obj1.x+"<br/>"); // ==> 2017
//-----------------------------------------------------------
//----看點2
// 對于新創建的自有屬性,四個屬性的默認值都為false或者undefined
var obj2 = Object.defineProperty({},"x",{});
document.write(obj2.x+"<br/>"); // ==> undefined
obj2.x = 2017;
document.write(obj2.x+"<br/>"); // ==> undefined
//-----------------------------------------------------------
//----看點3
// 對于已有屬性來說,默認的特性值是沒有做任何修改的
var obj3 = {x:2017}; // x 屬性是可寫、可枚舉、可配置的
Object.defineProperty(obj3,"x",{}); // 屬性描述中沒有做任何修改
obj3.x = 2000;
document.write(obj3.x+"<br/>"); // ==> 2000 說明x屬性依然是可寫的
//-----------------------------------------------------------
//----看點4
// 此方法 只能修改自有屬性或者是創建自有屬性,但是不能修改繼承屬性
var obj4 = Object.create(obj3);
var obj4_x = obj4.x; // 用一個對象來引用obj4.x屬性
document.write(obj4.hasOwnProperty("x")+ "<br/>"); //==> false x不是自有屬性
Object.defineProperty(obj4,"x",{}); // 創建x屬性并且覆蓋繼承的屬性
document.write(obj4.hasOwnProperty("x")+ "<br/>"); //==> ture
// 從上面可以看出,通過此方法,obj4 創建了x屬性,并且覆蓋了繼承的x屬性
document.write(obj4.x+"---"+obj4_x+ "<br/>");//undefined---2000
obj4.x = 123123; // 嘗試修改x自有屬性,發現無效,原因是writable 為false
document.write(obj4.x+"---"+obj4_x+ "<br/>");//undefined---2000
// 從上面可以看出,新創建的自有屬性x,屬性描述都是默認值,所以value為undefined
//-----------------------------------------------------------
//----看點5
// 看看 數據類型屬性,是可以修改成存取器屬性的
var obj5 = {x:888};
Object.defineProperty(obj5,"x",{get:function(){return 2017}});
document.write(obj5.x + "<br/>"); // ==> 2017
obj5.x = 123; // 由于存儲器屬性沒有set方法,所以是只讀屬性,不能進行修改
document.write(obj5.x + "<br/>"); // ==> 2017
//-----------------------------------------------------------
//----看點6
/*
*Object.defineProperties() 可以修改一個對象的多個屬性描述
*參數1:對象
*參數2:一個映射列表(也成對象,字典),包括屬性名,屬性描述
*/
var obj6 = Object.defineProperties({},{x:{value:100,writable:true,enumerable:true,configurable:true},
"y":{value:200}});
document.write(obj6.x +"---"+ obj6.y + "<br/>"); // ==> 2017
我們來看看如果給Object復制屬性,而且這些屬性的特性也一并復制。
// 復制屬性的特性
/*
*給Object.prototype(原型)添加一個不可枚舉的extend()方法。
*這個方法繼承自調用它的對象,將作為參數傳入的對象的屬性一一復制,
*除了值之外,也要復制屬性的所有特性,除非在目標對象中存在同名的屬性,
*參數對象的所有自有對象(包括不可枚舉的屬性)也要意義復制。
*/
Object.defineProperty(Object.prototype,
"extend",
{
writable:true,
enumerable:false, // 不可枚舉
configurable:true,
value:function(obj){ // 值為一個函數
//獲取所有的自有屬性,包括不可枚舉的
var names = Object.getOwnPropertyNames(obj);
// 遍歷
for(var i = 0; i < names.length; i++){
// 如果屬性中已經存在,則跳過
if(names[i] in this) continue;
// 獲取obj中的屬性的描述符
var desc = Object.getOwnPropertyDescriptor(obj,names[i]);
// 用它給this創建一個屬性
Object.defineProperty(this,names[i],desc);
}
}
});
對象的三個屬性
對象的三個屬性是原型(prototype)、類(class)和可擴展性(extensible)。
-
原型屬性
原型屬性是在實例對象創建之初就設置好的,之前我們提到的,通過對象直接量創建的對象,原型是Object.prototype
。通過new
創建的對象,原型是構造函數的prototype
。通過Object.create()
創建的對象,原型是第一個參數。可以通過Object.getPrototypeOf()
來查詢它的原型。也可以通過isPrototypeOf()
方法來檢測一個對象是否是另一個對象的原型(或處于原型鏈中),例如p.isPrototypeOf(o)來檢測p是否是o的原型。 -
類屬性
對象的類屬性(class)是一個字符串,用來表示對象的類型信息。
因為JS沒有提供設置這個屬性的方法,我們只能通過間接的方法來查詢它,默認的toString()
方法(繼承自Object.prototype)返回[object class]
這種格式的字符串,所以我們需要提取返回來的字符串的第8個位置到倒數第二個位置之間的字符串。(有個棘手的問題是,很多對象重寫了toString()方法,為了能夠調用正確toString()版本,必須簡介地調用Function.call()方法)。看例子:
//這個函數用來獲取對象的class屬性
function classof(obj){
if(obj === null) return "Null";
if(obj ===undefined) return "Undefined";
return Object.prototype.toString.call(obj).slice(8,-1);
}
// 簡單的輸出函數
function printClassName(obj){
document.write(classof(obj)+"<br/>");
}
printClassName(null); //==>Null
printClassName(1); //==>Number
printClassName(""); //==>String
printClassName(false); //==>Boolean
printClassName({}); //==>Object
printClassName([]); //==>Array
printClassName(/./); //==>RegExp
printClassName(new Date());//==>Date
printClassName(window); //==>Window
function f(){} // 定義一個自定義構造函數
printClassName(new f()); //==>Object
-
可擴展性
對象的可擴展性用以表示是否可以給對象添加新屬性。所有的內置對象和自定義對象都是現實可擴展的。我們可以通過(Object.esExtensible()
)來判斷該對象是否是可擴展的。如果我們想將一個對象轉為不可擴展的,需要調用Object.preventExtensions()
。需要注意的是,一旦對象轉成不可擴展的,就無法再將其轉化回可擴展的了,而且這個方法只影響對象本身的可擴展性。
Object.seal()
方法是將對象設置成不可擴展的,同時還將對象的所有自有屬性設置成不可配置的,但是不更改對象屬性的可寫屬性,也就是將對象封閉。
Object.isSeal()
方法是來檢測對象是否封閉。Object.freeze()`是嚴格鎖定對象,不僅將對象設置為不可擴展的和將其屬性設置成不可配置的之外,還可以將它自有的所有數據屬性設置成只讀的(讀取器屬性的不受影響)。
序列化對象
對象序列化是指將對象的狀態轉換為字符串,也可以將字符串還原為對象。
Json.stringify()
用來序列化JS對象的。
Json.parse()
用來還原JS對象。
注意:JSON的語法是JavaScript語法的子集,它并不能代表JavaScript里的所有值。支持對象、數組、字符串、無窮大數字、true、false、null,并且它們可以序列化和還原。NaN、Infinity和- Infinity序列化的結果是null。而函數、RegExp、Error對象和undefined值不能序列化和還原。序列化只能序列化對象可枚舉的自有屬性,對于不能序列化的屬性會將屬性省略。這兩個方法接受第二個參數和第三個參數,大家可以看看文檔。
var mObj = {
x:1,
y:{z:[false,null,"string"]},
func:function(){document.write("這是一個函數func")}
};
var sObj = JSON.stringify(mObj);
document.write(sObj +"<br/>"); // ==> {"x":1,"y":{"z":[false,null,"string"]}} 對象中的方法給省略掉了
var oObj = JSON.parse(sObj); // ==> {x:1,y:{z:[false,null,"string"]}}
對象方法
所有JS對象都從Object.prototype
繼承屬性,這些繼承屬性主要是方法,因為我們對方法更加感興趣。這些方法也可以被重寫。
之前提到過很多對象方法了。這里就不具體講解了。