DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> 全面了解JavaScript對象進階
全面了解JavaScript對象進階
編輯:關於JavaScript     

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

1.創建對象

1.1 對象直接量

對象直接量是創建對象最簡單的方式,由若干名/值對組成映射表:

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

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

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"
}
};

對象直接量創建對象十分簡單,但一般都不會這樣使用。代碼可復用性低,如果想要在其他地方使用該對象並且屬性值不一樣,那這麼辦?是不是又得重新創建一份代碼?

1.2 通過new創建對象

通過new創建對象之前,先要創建一個函數,new把這個函數當做構造函數(constructor)。例如通過new創建一個Person對象:

function Person(){
//構造函數
}

var person = new Person();

Javscript語言核心中的原始類型都包含內置構造函數:

var a = new Array();
var d = new Date();
var r = new RegExp(“js”);

1.3 Object.create()

在了解Object的create方法之前,我們想看看什麼是原型。每一個Javascript對象(null除外)都和另一個對象相關聯。“另一個”對象就是我們所說的原型。每一個對象都從原型繼承屬性。

所有通過對象直接量創建的對象都具有同一個原型對象Object.prototype。關鍵字new和構造函數創建的對象原型就是構造函數的prototype屬性的值。通過new Array()創建對象的原型為Array.prototype,通過new Date()創建的對象原型為Date.prototype。原型暫介紹到這裡。

Object.create方法包含兩個參數,第一個參數是對象的原型,第二個參數可選,用於描述對象屬性。使用很簡單,只需傳入所需的原型對象即可:

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

如果想創建一個沒有原型的對象,可通過傳入null作為參數。這樣創建的對象不會繼承任何屬性,也沒有像toString這樣的方法:

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

如果想創建一個普通的空對象,直接傳入Object.prototype:

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

如果是自定義的對象,和創建空對象一樣。直接傳入對象名.prototype:

function Person(){
}
var o4 = Object.create(Person.prototype);

2.屬性管理

2.1 屬性查詢和設置

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

ar author = book.author; //正確
var name = author.surname; //正確
var title = book[“main title”]; //正確
var className = book.class; //錯誤

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

既然對象是關聯數組,那麼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),也有一些屬性是從原型對象繼承而來。我們先看看一個實現繼承功能的函數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; //將其原型屬性設置為p
  return new f();
}

假設要查詢對象o的屬性x,如果o中不存在x,將會繼續在o的原型對象中查詢屬性x。如果原型對象中也沒有x,但這個原型對象也有原型,那麼繼續在這個原型對象的原型上執行查詢,直到找到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繼承自Object.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已經不存在了,什麼都沒做,返回true。
delete o.toString; //什麼都沒做,返回true。
delete不能刪除可配置型為false的屬性。某些內置對象的屬性是不可配置的,比如通過變量聲明和函數聲明創建的全局對象的屬性:
delete Object.prototype //不能刪除,屬性是不可配置的
var x = 1;
delete this.x; //不能刪除這個屬性
function f() {}
delete this.f; //不能刪除全局函數

2.3 檢測屬性

判斷某個屬性是否存在於某個對象中,可通過in運算符、hasOwnProperty()和propetyIsEnumerable()方法來檢測。

in運算符:運算符左側是屬性名,右側是對象。如果對象的自有屬性或者繼承屬性包含屬性則返回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循環遍歷對象屬性,遍歷的屬性包括自有屬性和繼承屬性。對象繼承的內置方法是不可枚舉的,但在代碼中給對象添加的屬性都是可枚舉的。例如:

var o = {x: 1, y: 2, z: 3}; //三個可枚舉的自有屬性
o.propertyIsEnumeable("toString"); //false,不可枚舉
for (p in o) //遍歷屬性
  console.log(p); //輸出x、y和z,不會輸出toString

有時候我們只想遍歷自有屬性,並且屬性不為函數:

for(p in o){
  if(!o.hasOwnProperty(p)) continue;
  if(typeof o[p] === "function") continue;
}

我們可通過枚舉遍歷功能實現可枚舉屬性的復制:

/*
* 把p中的可枚舉屬性復制到o中,並返回o
* 如果o和p含同名屬性,則覆蓋o中的屬性
* 這個函數並不處理getter和setter以及復制屬性
*/
function extend(o, p){
  for(prop in p){ //遍歷p中的所有屬性
    o[prop] = p[prop]; //將屬性添加到o中
  }
  return o;
}

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

3.屬性封裝

3.1 屬性getter和setter 

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

和數據屬性不同,存取器屬性不具有可寫性(writeable atribute)。如果屬性同時具有getter和setter方法,那麼它是一個讀/寫屬性。如果它只有getter方法,那麼它是一個只讀屬性,如果它只有setter方法,那麼它是一個只寫屬性。讀取只寫屬性總是返回undefined。

存取器屬性定義語法也比較簡單,函數定義沒有使用function關鍵字,而是使用get或set:

var o = {
  //普通的數據屬性
  data_prop: 1,
  //存取器屬性都是成對定義的函數
  get accessor_prop(){/* 這裡是函數體 */},
  set accessor_prop(value){}
};

思考下面這個表示2D笛卡爾點坐標的對象。它有兩個普通屬性x和y分別表示x坐標和y坐標,它還有兩個等價的存取器屬性用來表示點的極坐標:

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);
  var ratio = newValue / oldValue;
  this.x *= ratio;
  this.y *= ratio;
  },
  //theta是只讀存取器屬性,只有getter方法
  get theta() { return Math.atan2(this.y, this.x); }
};

和數據屬性一樣,存取器屬性是可以繼承的,因此可以將上述代碼中的p對象當做另一個“點”的原型。可以給性對象定義它的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個特性。

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

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

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

通過調用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");

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

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

// 屬性是存在的,但不可枚舉
o.x; //=> 1
Object.keys(o) //=> []
//現在對屬性x做修改,讓它變成只讀
Object.defineProperty(o, "x", {writable: true });
//視圖更改這個屬性的值
o.x = 2; //操作失敗但不報錯,而在嚴格模式中拋出類型錯誤異常
//屬性依然是可配置的,因此可通過這種方式對它進行修改:
Object.defineProperty(o, "x", {value: 2 });
o.x //=> 2
//現在將x從數據屬性修改為存取器屬性
Object.defineProperty(o, "x", { get: function() {return 0;} });
o.x // => 0

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

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采納之前,大多數Javascript的實現已經可以支持對象直接量語法中get和set寫法。這些實現提供了非標准的老式API用來查詢和設置getter和setter。這些API由四個方法組成,所有對象都擁有這些方法。

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

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

var o = {};
o.__defineGetter__("x", function(){return 0;});
o.__defineSetter__("y", function(value){console.log("set value:" + value);});

4.對象的三個屬性

每一個對象都有與之相關的原型(prototype)、類(class)、可擴展性(extensible attribute)。接下來講述這些屬性有什麼作用。

4.1 原型屬性

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

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

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

var p = {x: 1}; //定義一個原型對象
var o = Object.create(p); //使用這個原型創建一個對象
p.isPrototypeOf(o); //=> true,o繼承自p
Object.prototype.isPrototypeOf(o) //=> true, p繼承自Object.prototype

Mozilla實現的Javascript對外暴露了一個專門命名為__proto__屬性,用以直接查詢/設置對象原型。但IE和Opera不支持__proto__屬性,所以不建議直接使用__proto__屬性。

4.2 類屬性

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

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

function classof(o){
  if(o === null) return "Null";
  if(o === undefined) return "Undefined";
  return Object.prototype.toString.call(o).slice(8, -1);
}

4.3 可擴展性

對象的可擴展性用以表示是否可以給對象添加新屬性。所有內置對象和自定義對象都是顯式可擴展的。在ES5中,可將對象轉換為不可擴展的。

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

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

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

Object.isFrozen()用來檢測對象是否凍結。

5.序列化對象

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

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裡的所有值。支持對象、數組、字符串、無窮大數字、true、false和null,並且它們可以序列化和還原。NaN、Inifinity和-Inifinity序列化結果都是null。函數、RegExp、Error對象和undefined值不能序列化和還原。

這裡在附加說一下對象的方法:

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

toJSON()方法:Object.prototype實際上沒有定義toJSON()方法,但由於需要執行序列化的對象來說,JSON.stringify()方法會調用toJSON()方法。如果在帶序列化的對象中存在這個方法,則調用它。

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

以上這篇全面了解JavaScript對象進階就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved