下面小編就為大家?guī)硪黄媪私釰avaScript對象進階。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。
要了解JavaScript對象,我們可以從對象創(chuàng)建、屬性操作、對象方法這幾個方面入手。概括起來,包括以下幾模塊:
1.創(chuàng)建對象
1.1 對象直接量
對象直接量是創(chuàng)建對象最簡單的方式,由若干名/值對組成映射表:
var point = {x: 0, y: 0 };
屬性名也沒有什么限制,可以是js的關(guān)鍵字或者任意字符串,如果是這兩種情況,屬性需要用雙引號引起來:
var empty = {};
va point = {x: 0, y: 0 };
var book = {
"main title": "Javascript",
"sub-title": "The definitive Guide",
"for": "all audience",
author: {
firstName: "Davide",
lastName: "Flanagan"
}
};
對象直接量創(chuàng)建對象十分簡單,但一般都不會這樣使用。代碼可復用性低,如果想要在其他地方使用該對象并且屬性值不一樣,那這么辦?是不是又得重新創(chuàng)建一份代碼?
1.2 通過new創(chuàng)建對象
通過new創(chuàng)建對象之前,先要創(chuàng)建一個函數(shù),new把這個函數(shù)當做構(gòu)造函數(shù)(constructor)。例如通過new創(chuàng)建一個Person對象:
function Person(){
//構(gòu)造函數(shù)
}
var person = new Person();
Javscript語言核心中的原始類型都包含內(nèi)置構(gòu)造函數(shù):
var a = new Array();
var d = new Date();
var r = new RegExp(“js”);
1.3 Object.create()
在了解Object的create方法之前,我們想看看什么是原型。每一個Javascript對象(null除外)都和另一個對象相關(guān)聯(lián)?!傲硪粋€”對象就是我們所說的原型。每一個對象都從原型繼承屬性。
所有通過對象直接量創(chuàng)建的對象都具有同一個原型對象Object.prototype。關(guān)鍵字new和構(gòu)造函數(shù)創(chuàng)建的對象原型就是構(gòu)造函數(shù)的prototype屬性的值。通過new Array()創(chuàng)建對象的原型為Array.prototype,通過new Date()創(chuàng)建的對象原型為Date.prototype。原型暫介紹到這里。
Object.create方法包含兩個參數(shù),第一個參數(shù)是對象的原型,第二個參數(shù)可選,用于描述對象屬性。使用很簡單,只需傳入所需的原型對象即可:
var o1 = Object.create({x: 1, y: 2 }); //原型為Object.prototype
如果想創(chuàng)建一個沒有原型的對象,可通過傳入null作為參數(shù)。這樣創(chuàng)建的對象不會繼承任何屬性,也沒有像toString這樣的方法:
var o2 = Object.create(null); //沒有原型
如果想創(chuàng)建一個普通的空對象,直接傳入Object.prototype:
var o3 = Object.create(Object.prototype);
如果是自定義的對象,和創(chuàng)建空對象一樣。直接傳入對象名.prototype:
function Person(){
}
var o4 = Object.create(Person.prototype);
2.屬性管理
2.1 屬性查詢和設(shè)置
對象的屬性可通過點(.)或方括號([])運算符獲取。如果使用點獲取屬性,屬性名必須是簡單的表示符。不能是保留字,比如,o.for或者o.class。
ar author = book.author; //正確
var name = author.surname; //正確
var title = book[“main title”]; //正確
var className = book.class; //錯誤
object[“property”]這種語法看起來更像數(shù)組,只是這個數(shù)組的元素是通過字符串索引而不是數(shù)字索引。這種數(shù)組就是我們所說的關(guān)聯(lián)數(shù)組,也稱為散列、映射或字典。Javascript對象都是關(guān)聯(lián)數(shù)組。
既然對象是關(guān)聯(lián)數(shù)組,那么Javascript也為我們提供了屬性的遍歷方式for/in。下面的例子利用for/in計算portfolio的總計值:
function getvalue(portfolio){
var total = 0.0;
for(stock in portolio){
var shares = portolio[stock];
var price = getquote(stock);
total += shares * price;
}
return total;
}
繼承:Javascript對象具有自有屬性(own property),也有一些屬性是從原型對象繼承而來。我們先看看一個實現(xiàn)繼承功能的函數(shù)inherit:
function inherit(p){
if (p == null) throw TypeError(); //p是一個對象,大不能是null
if(Object.create){
return Object.create(p); //直接使用Object.create方法
}
var t = typeof p;
if(t !== "object" && t !== "function") throw TypeError();
function f() {};
f.prototype = p; //將其原型屬性設(shè)置為p
return new f();
}
假設(shè)要查詢對象o的屬性x,如果o中不存在x,將會繼續(xù)在o的原型對象中查詢屬性x。如果原型對象中也沒有x,但這個原型對象也有原型,那么繼續(xù)在這個原型對象的原型上執(zhí)行查詢,直到找到x或者查詢到一個原型為null的對象為止。
var o = {}; //o從Object.prototype繼承對象屬性
o.x = 1; //給o定義x屬性
var p = inherit(o); //p繼承o和Object.prototype
p.y = 2; //p定義屬性y
var q = inherit(p); //q繼承p、o和Object.prototype
q.z = 3; //給q定義屬性z
var s = q.toString(); //toString繼承自O(shè)bject.prototype
q.x + q.y // => 3:x和y分別繼承自o和p
2.2 刪除屬性
delete運算符可以刪除對象的屬性:
delete book.author;
delete book[“main title”];
delete只能刪除自有屬性,不能刪除繼承屬性。要刪除繼承屬性,必須從定義這個屬性的原型對象上刪除它,而且這會影響到所有的繼承自這個原型的對象。刪除成功會返回true。
ar o = {x: 1};
delete o.x; //刪除x,返回true
delete o.x; //x已經(jīng)不存在了,什么都沒做,返回true。
delete o.toString; //什么都沒做,返回true。
delete不能刪除可配置型為false的屬性。某些內(nèi)置對象的屬性是不可配置的,比如通過變量聲明和函數(shù)聲明創(chuàng)建的全局對象的屬性:
delete Object.prototype //不能刪除,屬性是不可配置的
var x = 1;
delete this.x; //不能刪除這個屬性
function f() {}
delete this.f; //不能刪除全局函數(shù)
2.3 檢測屬性
判斷某個屬性是否存在于某個對象中,可通過in運算符、hasOwnProperty()和propetyIsEnumerable()方法來檢測。
in運算符:運算符左側(cè)是屬性名,右側(cè)是對象。如果對象的自有屬性或者繼承屬性包含屬性則返回true:
var o = {x: 1};
"x" in o; //true:x是o的屬性
"y" in o; //false:y不是o的屬性
"toString" in o; //true:o繼承toString屬性
hasOwnProperty()方法:檢測給定的名字是否是對象的自有屬性。對于繼承屬性它將返回false:
var o = {x: 1};
o.hasOwnProperty("x"); //true:o有一個自由屬性x
o.hasOwnProperty("y"); //false:o中不存在屬性y
o.hasOenProperty("toString"); //false:toString是繼承屬性
propertyIsEnumerable()方法:是hasOwnProperty的增強版,只有檢測到自有屬性并且這個屬性是可枚舉行為true時才返回true:
var o = inherit({y: 2});
o.x = 1;
o.propertyIsEnumerable("x"); //true: o有一個可枚舉屬的自有屬性x
o.propertyIsEnumerable("y"); //false:y是繼承來的
Object.prototype.propertyIsEnumerable("toString"); //false:不可枚舉
2.4 枚舉屬性
通常使用for/in循環(huán)遍歷對象屬性,遍歷的屬性包括自有屬性和繼承屬性。對象繼承的內(nèi)置方法是不可枚舉的,但在代碼中給對象添加的屬性都是可枚舉的。例如:
var o = {x: 1, y: 2, z: 3}; //三個可枚舉的自有屬性
o.propertyIsEnumeable("toString"); //false,不可枚舉
for (p in o) //遍歷屬性
console.log(p); //輸出x、y和z,不會輸出toString
有時候我們只想遍歷自有屬性,并且屬性不為函數(shù):
for(p in o){
if(!o.hasOwnProperty(p)) continue;
if(typeof o[p] === "function") continue;
}
我們可通過枚舉遍歷功能實現(xiàn)可枚舉屬性的復制:
/*
* 把p中的可枚舉屬性復制到o中,并返回o
* 如果o和p含同名屬性,則覆蓋o中的屬性
* 這個函數(shù)并不處理getter和setter以及復制屬性
*/
function extend(o, p){
for(prop in p){ //遍歷p中的所有屬性
o[prop] = p[prop]; //將屬性添加到o中
}
return o;
}
ES5定義了兩個用以枚舉屬性名稱的函數(shù)。第一個是Object.keys(),返回由對象中可枚舉屬自有屬性名稱組成的數(shù)組。第二個枚舉函數(shù)是Object.getOwnPropertyNames(),和Object.keys()類似,它返回對象的所有自有屬性,而不僅僅是可枚舉屬性。
3.屬性封裝
3.1 屬性getter和setter
對象屬性由名字、值和一組特性(attribute)構(gòu)成的。在ES5中,屬性值可以用一個或兩個方法替代,這兩個方法就是getter和setter。由getter和setter定義的屬性稱做“存取器屬性”,它不同于“數(shù)據(jù)屬性”,數(shù)據(jù)屬性只有一個簡單的值。
和數(shù)據(jù)屬性不同,存取器屬性不具有可寫性(writeable atribute)。如果屬性同時具有g(shù)etter和setter方法,那么它是一個讀/寫屬性。如果它只有g(shù)etter方法,那么它是一個只讀屬性,如果它只有setter方法,那么它是一個只寫屬性。讀取只寫屬性總是返回undefined。
存取器屬性定義語法也比較簡單,函數(shù)定義沒有使用function關(guān)鍵字,而是使用get或set:
var o = {
//普通的數(shù)據(jù)屬性
data_prop: 1,
//存取器屬性都是成對定義的函數(shù)
get accessor_prop(){/* 這里是函數(shù)體 */},
set accessor_prop(value){}
};
思考下面這個表示2D笛卡爾點坐標的對象。它有兩個普通屬性x和y分別表示x坐標和y坐標,它還有兩個等價的存取器屬性用來表示點的極坐標:
var p = {
//x和y是普通的可讀寫數(shù)據(jù)屬性
x: 1.0,
y: 1.0,
//r是可讀寫的存取器屬性,它有g(shù)etter和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);
var ratio = newValue / oldValue;
this.x *= ratio;
this.y *= ratio;
},
//theta是只讀存取器屬性,只有g(shù)etter方法
get theta() { return Math.atan2(this.y, this.x); }
};
和數(shù)據(jù)屬性一樣,存取器屬性是可以繼承的,因此可以將上述代碼中的p對象當做另一個“點”的原型??梢越o性對象定義它的x和y屬性,但r和theta屬性繼承而來:
var q = inherit(p);
q.x = 1, q.y = 1;
console.log(q.r);
cosole.log(q.theta);
3.2 屬性特性
我們可以將存取器屬性的getter和setter方法看成屬性的特性。按照這個邏輯,我們也可把屬性的值同樣看著屬性的特性。因此,可以認為一個屬性包含一個名字和4個特性。
數(shù)字屬性的4個特性分別是它的值(value)、可寫性(writeable)、可枚舉性(enumerable)和可配置型(configurable)。
存取器屬性不具有值(value)特性和可寫性,因此包含:讀取(get)、寫入(set)、可枚舉性、可配置性。
ES5定義了一個名為“屬性描述符”的對象,這個對象代表那4個特性。數(shù)據(jù)屬性的描述符對象的屬性有value、writable、enumerable和configurable。存取器屬性的描述符對象則用get屬性和set屬性代替value和writable。其中writable、enumerable、configurable都是布爾值,get屬性和set屬性是函數(shù)值。
通過調(diào)用Object.getOwnPropertyDescriptor()可以獲取某個對象特定屬性的屬性描述符:
//返回{value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnProeprtyDescriptor({x: 1},"x");
//查詢上文中定義的random對象的octet屬性
//返回{get: /*func */, set: undefined, enumerable: true, configurable: true}
Object.getOwnPropertyDesciptor(random, "octet");
//對于繼承屬性和不存在屬性,返回undefined
Object.getOwnPropertyDesciptor({}, "x");
Object.getOwnPropertyDesciptor({}, "toString");
從函數(shù)名就可以看出,Object.getOwnPropertyDesciptor()只能得到自有屬性的描述符。要想獲得繼承屬性的特性,需要遍歷原型鏈(Object.getPrototypeOf())。
想要設(shè)置屬性的特性,或者讓新建屬性具有某些特性,則需要調(diào)用Object.defineProperty(),包含三個參數(shù):對象、屬性名、屬性描述符對象:
// 屬性是存在的,但不可枚舉
o.x; //=> 1
Object.keys(o) //=> []
//現(xiàn)在對屬性x做修改,讓它變成只讀
Object.defineProperty(o, "x", {writable: true });
//視圖更改這個屬性的值
o.x = 2; //操作失敗但不報錯,而在嚴格模式中拋出類型錯誤異常
//屬性依然是可配置的,因此可通過這種方式對它進行修改:
Object.defineProperty(o, "x", {value: 2 });
o.x //=> 2
//現(xiàn)在將x從數(shù)據(jù)屬性修改為存取器屬性
Object.defineProperty(o, "x", { get: function() {return 0;} });
o.x // => 0
如果要同時修改或創(chuàng)建多個屬性,則需要使用Object.defineProperties()。第一個參數(shù)是要修改的對象,第二個參數(shù)㐊一個映射表。例如:
var p = Object.defineProperties({}, {
x: { value: 1, writable: true, enumerable: true, configurable: true},
y: { value: 2, writable: true, enumerable: true, configurable: true},
r: {
get: function(){ return Math.sqrt(this.x * this.x + this.y * this.y); },
enumerable: true,
configurable: true
}
});
getter和setter的老式API: 在ES5采納之前,大多數(shù)Javascript的實現(xiàn)已經(jīng)可以支持對象直接量語法中g(shù)et和set寫法。這些實現(xiàn)提供了非標準的老式API用來查詢和設(shè)置getter和setter。這些API由四個方法組成,所有對象都擁有這些方法。
__lookupGetter__()和__lookupSetter__()用以返回一個命名屬性的getter和setter方法。
__defineGetter__()和__defineSetter__()用以定義getter和setter,第一個參數(shù)是屬性名字,第二個參數(shù)是getter和setter方法。
var o = {};
o.__defineGetter__("x", function(){return 0;});
o.__defineSetter__("y", function(value){console.log("set value:" + value);});
4.對象的三個屬性
每一個對象都有與之相關(guān)的原型(prototype)、類(class)、可擴展性(extensible attribute)。接下來講述這些屬性有什么作用。
4.1 原型屬性
對象的原型屬性是用來繼承屬性的,我們經(jīng)常把“o的原型屬性”直接叫做“o的原型”。在之前“創(chuàng)建對象”介紹了三種方式創(chuàng)建對象。通過對象直接量創(chuàng)建的對象使用Object.prototype作為它們的原型。通過new創(chuàng)建的對象使用構(gòu)造函數(shù)的prototype屬性作為它們的原型。通過Object.create()創(chuàng)建的對象使用第一個參數(shù)作為它們的原型。
在ES5中,可通過Object.getPrototypeOf()查詢對象原型。在ES3中,沒有與之等價的函數(shù),而是使用表達式o.constructor.prototype檢查對象的原型。
要想檢測一個對象是否是另一個對象的原型(或處于原型鏈中),使用isPrototypeOf()方法。例如,可以通過p.isPrototypeOf(o)來檢測p是否是o的原型:
var p = {x: 1}; //定義一個原型對象
var o = Object.create(p); //使用這個原型創(chuàng)建一個對象
p.isPrototypeOf(o); //=> true,o繼承自p
Object.prototype.isPrototypeOf(o) //=> true, p繼承自O(shè)bject.prototype
Mozilla實現(xiàn)的Javascript對外暴露了一個專門命名為__proto__屬性,用以直接查詢/設(shè)置對象原型。但IE和Opera不支持__proto__屬性,所以不建議直接使用__proto__屬性。
4.2 類屬性
對象的類屬性是一個字符串,用以表示對象的類型信息。ES3和ES5都為提供設(shè)置這個屬性的方法,只有一種間接方式查詢它。默認的toString()方法返回這種格式的字符串:[object class]。
可通過調(diào)用toString()方法,然后提取已返回字符串的第八個到倒數(shù)第二個位置之間的字符。但有個麻煩是,很多對象繼承的toString()方法重寫了,為了能夠調(diào)用正確的toString()版本,必須間接調(diào)用Function.call()方法。下面例子的classof函數(shù)可返回任意對象的類:
function classof(o){
if(o === null) return "Null";
if(o === undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8, -1);
}
4.3 可擴展性
對象的可擴展性用以表示是否可以給對象添加新屬性。所有內(nèi)置對象和自定義對象都是顯式可擴展的。在ES5中,可將對象轉(zhuǎn)換為不可擴展的。
Object.seal()方法除了能夠?qū)ο笤O(shè)置為不可擴展的,還可以將對象的所有自有屬性都設(shè)置為不可配置的。也就是說,不能給對象添加新屬性,而且已有屬性也不能刪除和配置。
Object.isSealed()方法用來檢測對象是否封閉。
Object.freeze()方法將更嚴格的鎖定對象,除了擁有Object.seal()方法的功能外,還可以將自有的所有數(shù)據(jù)屬性設(shè)置為只讀(如果對象的存取器屬性有setter方法,存取器屬性不受影響, 仍可以通過給屬性賦值調(diào)用它們)。
Object.isFrozen()用來檢測對象是否凍結(jié)。
5.序列化對象
對象序列化是指將對象的狀態(tài)轉(zhuǎn)換為字符串,也可以將字符串還原為對象。ES5提供了內(nèi)置函數(shù)JSON.stringify()和JSON.parse()用來序列化和還原Javascript對象。這些方法都使用JSON作為數(shù)據(jù)交換格式。例如:
o = {x: 1, y: {z: [false, null, ""]}}; //定義一個測試對象
s = JSON.stringify(o); //{"x":1,"y":{"z":[false,null,""]}}
p = JSON.parse(s); //p是o的深拷貝
JSON的語法是Javscript語法的子集,它并不能表示Javascript里的所有值。支持對象、數(shù)組、字符串、無窮大數(shù)字、true、false和null,并且它們可以序列化和還原。NaN、Inifinity和-Inifinity序列化結(jié)果都是null。函數(shù)、RegExp、Error對象和undefined值不能序列化和還原。
這里在附加說一下對象的方法:
toString()方法:它將返回一個表示調(diào)用這個方法的對象值的字符串。很多對象都重寫了toString()方法,比如Array.toString()、Date.toString()以及Function.toStrring()。
toJSON()方法:Object.prototype實際上沒有定義toJSON()方法,但由于需要執(zhí)行序列化的對象來說,JSON.stringify()方法會調(diào)用toJSON()方法。如果在帶序列化的對象中存在這個方法,則調(diào)用它。
valueOf()方法:valueOf()方法和toString()方法非常相似,但往往Javascript需要將對象轉(zhuǎn)換為某種原始值而非字符串的時候才調(diào)用它,尤其是轉(zhuǎn)換為數(shù)字的時候。有些內(nèi)置類自定義了valueOf()方法,比如,Date.valueOf()。
以上這篇全面了解JavaScript對象進階就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考