全面了解JavaScript對(duì)象進(jìn)階
來源:易賢網(wǎng) 閱讀:687 次 日期:2016-07-26 15:00:13
溫馨提示:易賢網(wǎng)小編為您整理了“全面了解JavaScript對(duì)象進(jìn)階”,方便廣大網(wǎng)友查閱!

下面小編就為大家?guī)硪黄媪私釰avaScript對(duì)象進(jìn)階。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。

要了解JavaScript對(duì)象,我們可以從對(duì)象創(chuàng)建、屬性操作、對(duì)象方法這幾個(gè)方面入手。概括起來,包括以下幾模塊:  

1.創(chuàng)建對(duì)象

1.1 對(duì)象直接量

對(duì)象直接量是創(chuàng)建對(duì)象最簡(jiǎn)單的方式,由若干名/值對(duì)組成映射表:

var point = {x: 0, y: 0 };

屬性名也沒有什么限制,可以是js的關(guān)鍵字或者任意字符串,如果是這兩種情況,屬性需要用雙引號(hào)引起來:

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"

}

};

對(duì)象直接量創(chuàng)建對(duì)象十分簡(jiǎn)單,但一般都不會(huì)這樣使用。代碼可復(fù)用性低,如果想要在其他地方使用該對(duì)象并且屬性值不一樣,那這么辦?是不是又得重新創(chuàng)建一份代碼?

1.2 通過new創(chuàng)建對(duì)象

通過new創(chuàng)建對(duì)象之前,先要?jiǎng)?chuàng)建一個(gè)函數(shù),new把這個(gè)函數(shù)當(dāng)做構(gòu)造函數(shù)(constructor)。例如通過new創(chuàng)建一個(gè)Person對(duì)象:

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方法之前,我們想看看什么是原型。每一個(gè)Javascript對(duì)象(null除外)都和另一個(gè)對(duì)象相關(guān)聯(lián)。“另一個(gè)”對(duì)象就是我們所說的原型。每一個(gè)對(duì)象都從原型繼承屬性。

所有通過對(duì)象直接量創(chuàng)建的對(duì)象都具有同一個(gè)原型對(duì)象Object.prototype。關(guān)鍵字new和構(gòu)造函數(shù)創(chuàng)建的對(duì)象原型就是構(gòu)造函數(shù)的prototype屬性的值。通過new Array()創(chuàng)建對(duì)象的原型為Array.prototype,通過new Date()創(chuàng)建的對(duì)象原型為Date.prototype。原型暫介紹到這里。

Object.create方法包含兩個(gè)參數(shù),第一個(gè)參數(shù)是對(duì)象的原型,第二個(gè)參數(shù)可選,用于描述對(duì)象屬性。使用很簡(jiǎn)單,只需傳入所需的原型對(duì)象即可:

var o1 = Object.create({x: 1, y: 2 }); //原型為Object.prototype

如果想創(chuàng)建一個(gè)沒有原型的對(duì)象,可通過傳入null作為參數(shù)。這樣創(chuàng)建的對(duì)象不會(huì)繼承任何屬性,也沒有像toString這樣的方法:

var o2 = Object.create(null); //沒有原型

如果想創(chuàng)建一個(gè)普通的空對(duì)象,直接傳入Object.prototype:

var o3 = Object.create(Object.prototype);

如果是自定義的對(duì)象,和創(chuàng)建空對(duì)象一樣。直接傳入對(duì)象名.prototype:

function Person(){

}

var o4 = Object.create(Person.prototype);

2.屬性管理

2.1 屬性查詢和設(shè)置

對(duì)象的屬性可通過點(diǎn)(.)或方括號(hào)([])運(yùn)算符獲取。如果使用點(diǎn)獲取屬性,屬性名必須是簡(jiǎn)單的表示符。不能是保留字,比如,o.for或者o.class。

ar author = book.author; //正確

var name = author.surname; //正確

var title = book[“main title”]; //正確

var className = book.class; //錯(cuò)誤

object[“property”]這種語法看起來更像數(shù)組,只是這個(gè)數(shù)組的元素是通過字符串索引而不是數(shù)字索引。這種數(shù)組就是我們所說的關(guān)聯(lián)數(shù)組,也稱為散列、映射或字典。Javascript對(duì)象都是關(guān)聯(lián)數(shù)組。

既然對(duì)象是關(guān)聯(lián)數(shù)組,那么Javascript也為我們提供了屬性的遍歷方式for/in。下面的例子利用for/in計(jì)算portfolio的總計(jì)值:   

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對(duì)象具有自有屬性(own property),也有一些屬性是從原型對(duì)象繼承而來。我們先看看一個(gè)實(shí)現(xiàn)繼承功能的函數(shù)inherit:

function inherit(p){

  if (p == null) throw TypeError(); //p是一個(gè)對(duì)象,大不能是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è)要查詢對(duì)象o的屬性x,如果o中不存在x,將會(huì)繼續(xù)在o的原型對(duì)象中查詢屬性x。如果原型對(duì)象中也沒有x,但這個(gè)原型對(duì)象也有原型,那么繼續(xù)在這個(gè)原型對(duì)象的原型上執(zhí)行查詢,直到找到x或者查詢到一個(gè)原型為null的對(duì)象為止。

var o = {}; //o從Object.prototype繼承對(duì)象屬性

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運(yùn)算符可以刪除對(duì)象的屬性:

delete book.author;

delete book[“main title”];

delete只能刪除自有屬性,不能刪除繼承屬性。要?jiǎng)h除繼承屬性,必須從定義這個(gè)屬性的原型對(duì)象上刪除它,而且這會(huì)影響到所有的繼承自這個(gè)原型的對(duì)象。刪除成功會(huì)返回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)置對(duì)象的屬性是不可配置的,比如通過變量聲明和函數(shù)聲明創(chuàng)建的全局對(duì)象的屬性:

delete Object.prototype //不能刪除,屬性是不可配置的

var x = 1;

delete this.x; //不能刪除這個(gè)屬性

function f() {}

delete this.f; //不能刪除全局函數(shù)

2.3 檢測(cè)屬性

判斷某個(gè)屬性是否存在于某個(gè)對(duì)象中,可通過in運(yùn)算符、hasOwnProperty()和propetyIsEnumerable()方法來檢測(cè)。

in運(yùn)算符:運(yùn)算符左側(cè)是屬性名,右側(cè)是對(duì)象。如果對(duì)象的自有屬性或者繼承屬性包含屬性則返回true:

var o = {x: 1};

"x" in o; //true:x是o的屬性

"y" in o; //false:y不是o的屬性

"toString" in o; //true:o繼承toString屬性

hasOwnProperty()方法:檢測(cè)給定的名字是否是對(duì)象的自有屬性。對(duì)于繼承屬性它將返回false:

var o = {x: 1};

o.hasOwnProperty("x"); //true:o有一個(gè)自由屬性x

o.hasOwnProperty("y"); //false:o中不存在屬性y

o.hasOenProperty("toString"); //false:toString是繼承屬性

propertyIsEnumerable()方法:是hasOwnProperty的增強(qiáng)版,只有檢測(cè)到自有屬性并且這個(gè)屬性是可枚舉行為true時(shí)才返回true:

var o = inherit({y: 2});

o.x = 1;

o.propertyIsEnumerable("x"); //true: o有一個(gè)可枚舉屬的自有屬性x

o.propertyIsEnumerable("y"); //false:y是繼承來的

Object.prototype.propertyIsEnumerable("toString"); //false:不可枚舉

2.4 枚舉屬性

通常使用for/in循環(huán)遍歷對(duì)象屬性,遍歷的屬性包括自有屬性和繼承屬性。對(duì)象繼承的內(nèi)置方法是不可枚舉的,但在代碼中給對(duì)象添加的屬性都是可枚舉的。例如:

var o = {x: 1, y: 2, z: 3}; //三個(gè)可枚舉的自有屬性

o.propertyIsEnumeable("toString"); //false,不可枚舉

for (p in o) //遍歷屬性

  console.log(p); //輸出x、y和z,不會(huì)輸出toString

有時(shí)候我們只想遍歷自有屬性,并且屬性不為函數(shù):

for(p in o){

  if(!o.hasOwnProperty(p)) continue;

  if(typeof o[p] === "function") continue;

}

我們可通過枚舉遍歷功能實(shí)現(xiàn)可枚舉屬性的復(fù)制:

/*

* 把p中的可枚舉屬性復(fù)制到o中,并返回o

* 如果o和p含同名屬性,則覆蓋o中的屬性

* 這個(gè)函數(shù)并不處理getter和setter以及復(fù)制屬性

*/

function extend(o, p){

  for(prop in p){ //遍歷p中的所有屬性

    o[prop] = p[prop]; //將屬性添加到o中

  }

  return o;

}

ES5定義了兩個(gè)用以枚舉屬性名稱的函數(shù)。第一個(gè)是Object.keys(),返回由對(duì)象中可枚舉屬自有屬性名稱組成的數(shù)組。第二個(gè)枚舉函數(shù)是Object.getOwnPropertyNames(),和Object.keys()類似,它返回對(duì)象的所有自有屬性,而不僅僅是可枚舉屬性。

3.屬性封裝

3.1 屬性getter和setter 

對(duì)象屬性由名字、值和一組特性(attribute)構(gòu)成的。在ES5中,屬性值可以用一個(gè)或兩個(gè)方法替代,這兩個(gè)方法就是getter和setter。由getter和setter定義的屬性稱做“存取器屬性”,它不同于“數(shù)據(jù)屬性”,數(shù)據(jù)屬性只有一個(gè)簡(jiǎn)單的值。

和數(shù)據(jù)屬性不同,存取器屬性不具有可寫性(writeable atribute)。如果屬性同時(shí)具有g(shù)etter和setter方法,那么它是一個(gè)讀/寫屬性。如果它只有g(shù)etter方法,那么它是一個(gè)只讀屬性,如果它只有setter方法,那么它是一個(gè)只寫屬性。讀取只寫屬性總是返回undefined。

存取器屬性定義語法也比較簡(jiǎn)單,函數(shù)定義沒有使用function關(guān)鍵字,而是使用get或set:

var o = {

  //普通的數(shù)據(jù)屬性

  data_prop: 1,

  //存取器屬性都是成對(duì)定義的函數(shù)

  get accessor_prop(){/* 這里是函數(shù)體 */},

  set accessor_prop(value){}

};

思考下面這個(gè)表示2D笛卡爾點(diǎn)坐標(biāo)的對(duì)象。它有兩個(gè)普通屬性x和y分別表示x坐標(biāo)和y坐標(biāo),它還有兩個(gè)等價(jià)的存取器屬性用來表示點(diǎn)的極坐標(biāo):

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對(duì)象當(dāng)做另一個(gè)“點(diǎn)”的原型??梢越o性對(duì)象定義它的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方法看成屬性的特性。按照這個(gè)邏輯,我們也可把屬性的值同樣看著屬性的特性。因此,可以認(rèn)為一個(gè)屬性包含一個(gè)名字和4個(gè)特性。

數(shù)字屬性的4個(gè)特性分別是它的值(value)、可寫性(writeable)、可枚舉性(enumerable)和可配置型(configurable)。

存取器屬性不具有值(value)特性和可寫性,因此包含:讀?。╣et)、寫入(set)、可枚舉性、可配置性。

ES5定義了一個(gè)名為“屬性描述符”的對(duì)象,這個(gè)對(duì)象代表那4個(gè)特性。數(shù)據(jù)屬性的描述符對(duì)象的屬性有value、writable、enumerable和configurable。存取器屬性的描述符對(duì)象則用get屬性和set屬性代替value和writable。其中writable、enumerable、configurable都是布爾值,get屬性和set屬性是函數(shù)值。

通過調(diào)用Object.getOwnPropertyDescriptor()可以獲取某個(gè)對(duì)象特定屬性的屬性描述符:

//返回{value: 1, writable: true, enumerable: true, configurable: true}

Object.getOwnProeprtyDescriptor({x: 1},"x");

//查詢上文中定義的random對(duì)象的octet屬性

//返回{get: /*func */, set: undefined, enumerable: true, configurable: true}

Object.getOwnPropertyDesciptor(random, "octet");

//對(duì)于繼承屬性和不存在屬性,返回undefined

Object.getOwnPropertyDesciptor({}, "x");

Object.getOwnPropertyDesciptor({}, "toString");

從函數(shù)名就可以看出,Object.getOwnPropertyDesciptor()只能得到自有屬性的描述符。要想獲得繼承屬性的特性,需要遍歷原型鏈(Object.getPrototypeOf())。

想要設(shè)置屬性的特性,或者讓新建屬性具有某些特性,則需要調(diào)用Object.defineProperty(),包含三個(gè)參數(shù):對(duì)象、屬性名、屬性描述符對(duì)象:

// 屬性是存在的,但不可枚舉

o.x; //=> 1

Object.keys(o) //=> []

//現(xiàn)在對(duì)屬性x做修改,讓它變成只讀

Object.defineProperty(o, "x", {writable: true });

//視圖更改這個(gè)屬性的值

o.x = 2; //操作失敗但不報(bào)錯(cuò),而在嚴(yán)格模式中拋出類型錯(cuò)誤異常

//屬性依然是可配置的,因此可通過這種方式對(duì)它進(jìn)行修改:

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

如果要同時(shí)修改或創(chuàng)建多個(gè)屬性,則需要使用Object.defineProperties()。第一個(gè)參數(shù)是要修改的對(duì)象,第二個(gè)參數(shù)㐊一個(gè)映射表。例如:

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的實(shí)現(xiàn)已經(jīng)可以支持對(duì)象直接量語法中g(shù)et和set寫法。這些實(shí)現(xiàn)提供了非標(biāo)準(zhǔn)的老式API用來查詢和設(shè)置getter和setter。這些API由四個(gè)方法組成,所有對(duì)象都擁有這些方法。

__lookupGetter__()和__lookupSetter__()用以返回一個(gè)命名屬性的getter和setter方法。

__defineGetter__()和__defineSetter__()用以定義getter和setter,第一個(gè)參數(shù)是屬性名字,第二個(gè)參數(shù)是getter和setter方法。  

var o = {};

o.__defineGetter__("x", function(){return 0;});

o.__defineSetter__("y", function(value){console.log("set value:" + value);});

4.對(duì)象的三個(gè)屬性

每一個(gè)對(duì)象都有與之相關(guān)的原型(prototype)、類(class)、可擴(kuò)展性(extensible attribute)。接下來講述這些屬性有什么作用。

4.1 原型屬性

對(duì)象的原型屬性是用來繼承屬性的,我們經(jīng)常把“o的原型屬性”直接叫做“o的原型”。在之前“創(chuàng)建對(duì)象”介紹了三種方式創(chuàng)建對(duì)象。通過對(duì)象直接量創(chuàng)建的對(duì)象使用Object.prototype作為它們的原型。通過new創(chuàng)建的對(duì)象使用構(gòu)造函數(shù)的prototype屬性作為它們的原型。通過Object.create()創(chuàng)建的對(duì)象使用第一個(gè)參數(shù)作為它們的原型。

在ES5中,可通過Object.getPrototypeOf()查詢對(duì)象原型。在ES3中,沒有與之等價(jià)的函數(shù),而是使用表達(dá)式o.constructor.prototype檢查對(duì)象的原型。

要想檢測(cè)一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型(或處于原型鏈中),使用isPrototypeOf()方法。例如,可以通過p.isPrototypeOf(o)來檢測(cè)p是否是o的原型:

var p = {x: 1}; //定義一個(gè)原型對(duì)象

var o = Object.create(p); //使用這個(gè)原型創(chuàng)建一個(gè)對(duì)象

p.isPrototypeOf(o); //=> true,o繼承自p

Object.prototype.isPrototypeOf(o) //=> true, p繼承自O(shè)bject.prototype

Mozilla實(shí)現(xiàn)的Javascript對(duì)外暴露了一個(gè)專門命名為__proto__屬性,用以直接查詢/設(shè)置對(duì)象原型。但I(xiàn)E和Opera不支持__proto__屬性,所以不建議直接使用__proto__屬性。

4.2 類屬性

對(duì)象的類屬性是一個(gè)字符串,用以表示對(duì)象的類型信息。ES3和ES5都為提供設(shè)置這個(gè)屬性的方法,只有一種間接方式查詢它。默認(rèn)的toString()方法返回這種格式的字符串:[object class]。

可通過調(diào)用toString()方法,然后提取已返回字符串的第八個(gè)到倒數(shù)第二個(gè)位置之間的字符。但有個(gè)麻煩是,很多對(duì)象繼承的toString()方法重寫了,為了能夠調(diào)用正確的toString()版本,必須間接調(diào)用Function.call()方法。下面例子的classof函數(shù)可返回任意對(duì)象的類:

function classof(o){

  if(o === null) return "Null";

  if(o === undefined) return "Undefined";

  return Object.prototype.toString.call(o).slice(8, -1);

}

4.3 可擴(kuò)展性

對(duì)象的可擴(kuò)展性用以表示是否可以給對(duì)象添加新屬性。所有內(nèi)置對(duì)象和自定義對(duì)象都是顯式可擴(kuò)展的。在ES5中,可將對(duì)象轉(zhuǎn)換為不可擴(kuò)展的。

Object.seal()方法除了能夠?qū)?duì)象設(shè)置為不可擴(kuò)展的,還可以將對(duì)象的所有自有屬性都設(shè)置為不可配置的。也就是說,不能給對(duì)象添加新屬性,而且已有屬性也不能刪除和配置。

Object.isSealed()方法用來檢測(cè)對(duì)象是否封閉。

Object.freeze()方法將更嚴(yán)格的鎖定對(duì)象,除了擁有Object.seal()方法的功能外,還可以將自有的所有數(shù)據(jù)屬性設(shè)置為只讀(如果對(duì)象的存取器屬性有setter方法,存取器屬性不受影響, 仍可以通過給屬性賦值調(diào)用它們)。

Object.isFrozen()用來檢測(cè)對(duì)象是否凍結(jié)。

5.序列化對(duì)象

對(duì)象序列化是指將對(duì)象的狀態(tài)轉(zhuǎn)換為字符串,也可以將字符串還原為對(duì)象。ES5提供了內(nèi)置函數(shù)JSON.stringify()和JSON.parse()用來序列化和還原Javascript對(duì)象。這些方法都使用JSON作為數(shù)據(jù)交換格式。例如:

o = {x: 1, y: {z: [false, null, ""]}}; //定義一個(gè)測(cè)試對(duì)象

s = JSON.stringify(o); //{"x":1,"y":{"z":[false,null,""]}}

p = JSON.parse(s); //p是o的深拷貝

JSON的語法是Javscript語法的子集,它并不能表示Javascript里的所有值。支持對(duì)象、數(shù)組、字符串、無窮大數(shù)字、true、false和null,并且它們可以序列化和還原。NaN、Inifinity和-Inifinity序列化結(jié)果都是null。函數(shù)、RegExp、Error對(duì)象和undefined值不能序列化和還原。

這里在附加說一下對(duì)象的方法:

toString()方法:它將返回一個(gè)表示調(diào)用這個(gè)方法的對(duì)象值的字符串。很多對(duì)象都重寫了toString()方法,比如Array.toString()、Date.toString()以及Function.toStrring()。

toJSON()方法:Object.prototype實(shí)際上沒有定義toJSON()方法,但由于需要執(zhí)行序列化的對(duì)象來說,JSON.stringify()方法會(huì)調(diào)用toJSON()方法。如果在帶序列化的對(duì)象中存在這個(gè)方法,則調(diào)用它。

valueOf()方法:valueOf()方法和toString()方法非常相似,但往往Javascript需要將對(duì)象轉(zhuǎn)換為某種原始值而非字符串的時(shí)候才調(diào)用它,尤其是轉(zhuǎn)換為數(shù)字的時(shí)候。有些內(nèi)置類自定義了valueOf()方法,比如,Date.valueOf()。

以上這篇全面了解JavaScript對(duì)象進(jìn)階就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考

更多信息請(qǐng)查看網(wǎng)絡(luò)編程
易賢網(wǎng)手機(jī)網(wǎng)站地址:全面了解JavaScript對(duì)象進(jìn)階
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 加入群交流 | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65317125(9:00—18:00) 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:526150442(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)