JavaScript對象可以看作是屬性的無序集合,每個屬性就是一個鍵值對,可增可刪。
JavaScript中的所有事物都是對象:字符串、數字、數組、日期,等等。
JavaScript對象除了可以保持自有的屬性外,還可以從一個稱為原型的對象繼承屬性。對象的方法通常是繼承的屬性。這種“原型式集成”是JavaScript的的核心特征。
1.創建對象
第一種:對象直接量表示法創建對象。
這是最簡單的對象創建方式,對象直接量由若干key:value鍵值對屬性組成,屬性之間用逗號分隔,整個對象用花括號括起來。
var empty = {}; //不包含任何屬性的對象 var point = { x: 3, y: 5 }; //包含兩個屬性的對象 var point2 = { x: point.x + 1, y: point.y + 1 }; //屬性值可以是表達式 var book = { "main title": "JavaScript", //屬性名有空格,必須用字符串表示 "sub-title": "The Defintive Guide", //屬性名有連字符,必須用字符串表示 "for": "all audiences", //屬性名是保留字,必須用字符串表示 author: { //這個屬性的值是一個對象 firstname: "David", surname: "Flanagan" }
ECMAScript 5版本中,使用保留字屬性名可以不用引號引起來。對象直接量最後一個屬性後的逗號自動忽略。
第二種:通過關鍵字創建對象。
關鍵字new用來創建並初始化對象,後面跟一個構造函數。JavaScript語言核心中原始類型都包含內置構造函數,下面是內置對象創建演示。
var o = new Object(); //創建一個空對象,等價於 0={} var a = new Array(); //創建一個空數組 var d = new Date(); //創建一個代表當前時間的Date對象 var r = new RegExp("js"); //創建一個正則表達式對象
除了這些內置構造函數,使用自定義構造函數來初始化新對象也很常見。
介紹第三種方法之前需要先簡單了解“原型”的概念。每一個JavaScript對象(null除外)都有一個關聯對象,並且可以從關聯對象繼承屬性。這個關聯對象就是所謂的“原型”,類似於C#中的基類。
所有通過對象直接量和構造函數創建的對象都可以通過Object.prototype獲得原型對象的引用。沒有原型的對象為數不多,Object.prototype就是其中之一。
普通對象都有原型,比如Array數組對象的原型是Array.prototype。同時,內置構造函數都具有一個繼承Object.prototype的原型。因此,通過new Array()創建的數組對象的屬性同時繼承至Array.prototype和Object.prototype,當對象出現多繼承關系時,那麼這一系列鏈接的原型對象就被稱作“原型鏈”。
第三種:使用Object.create()函數創建對象。
Object.create(Object[,Properties])是ECMAScript 5版本出現的一個靜態函數,用來創建對象。它接收兩個參數:第一個是要創建對象的原型;第二個是可選參數,用來描述對象屬性。
使用它創建對象,只需傳入所需原型對象即可:
var a = Object.create({ 'isLock': true }); //為對象a指定一個原型 console.log(a.isLock); //=> true o繼承原型對象屬性isLock console.log(a.hasOwnProperty('isLock')); //=> false 驗證isLock並非o的自有屬性
創建一個普通的空對象,需要傳入參數Object.prototype:
var b = Object.create(Object.prototype);
可以通過傳入參數null來創建沒有原型的對象,該類對象不會繼承任何東西:
var b = Object.create(null); //該對象不包括任何對象的基礎方法
通過原型創建對象,可以使任意對象可繼承,這是一個強大的特性。比如可以防止程序無意修改不受控制的對象。程序不直接操作對象,而是操作通過Object.create()創建的繼承對象。
2.查詢和設置屬性
對象屬性值可以通過點.和方括號[]運算符來查詢或設置。
var book = { 'author': 'Tom', 'main title': 'Hello JavaScript' }; var author = book.author; //1.獲取book的“author”屬性值 var title = book["main title"]; //2.獲取book的“main title”屬性值 book.edition = 6; //3.給book創建一個“edition”屬性 book["main title"] = "ECMAScript"; //4.修改"main title"屬性值
ES3版本中,如果屬性名是關鍵字必須通過方括號的形式訪問。ES5版本放寬了要求,可以直接在點運算符後面直接使用保留字。
關聯數組對象
上面提到可以通過object["property"]操作對象屬性,這種語法看起來更像數組,只是這個數組元素是通過字符串索引而不是數字索引,這類數組被稱為關聯數組。JavaScript對象都是關聯數組,通過[]訪問對象屬性時,在程序運行時可以創建或修改它們,更有靈活性。
繼承
JavaScript對象的屬性分兩種,一種是自己定義的,被稱為“自有屬性”。也有一些屬性是從原型對象繼承過來的。對象屬性的多繼承關系構成了原型鏈。
對象屬性在賦值前會先檢查原型鏈,以此判斷是否允許賦值操作。例如,如果對象o繼承自一個只讀屬性x,那麼對x屬性賦值是不允許的。如果允許屬性賦值,也只是在原始對象上創建或對已有的屬性賦值,而不會修改原型鏈。
JavaScript中,一般只有在查詢屬性的時候才能體會到繼承的存在,而設置屬性和繼承無關。通過這個特性可以有選擇的覆蓋繼承的屬性。
屬性訪問錯誤
查詢一個不存在的屬性不會報錯。如果在對象自身屬性和繼承的屬性中沒有找到指定屬性,則返回undefined。通過下面一小段代碼驗證下:
var a = { name: 'admin' }; //定義一個原型對象a var b = Object.create(a); //定義一個對象b繼承至對象a console.log(b.name); //=> admin b繼承a的name屬性,正常輸出 console.log(b.age); //=>undefined b本身和繼承對象都沒有age屬性,故輸出undefined
但有一種情況:假如對象不存在,試圖訪問這個不存在對象的屬性時則會拋異常。例如:
console.log(c.name); //Uncaught ReferenceError: c is not defined var d = null; console.log(d.name); //Uncaught TypeError: Cannot read property 'name' of null
所以,這就要求我們在訪問不確定對象屬性時需要驗證一下。
var book = { "length": 21 }; var len = book && book.length; //這裡用&&的第三種用法代替if。 console.log(len); //=>21
3.刪除屬性
delete運算符可以刪除對象的屬性,刪除成功返回true。但是delete不能刪除那些可配置型為false的屬性。只能刪除自身屬性,不能刪除繼承屬性。
delete book.author // 返回true
刪除全局屬性時,可以直接省略全局對象,delete後面跟上要刪除的屬性即可。
this.x=1; //創建一個全局屬性 console.log(delete x); //=>true
4.檢測屬性
所謂檢測屬性就是判斷某個屬性時候存在與某個對象中。一般可以通過in運算符、hasOwnProperty()和propertyIsEnumerable()方法來完成驗證工作。
in運算符判斷,如果對象自有屬性或繼承屬性包含這個屬性則返回true。
var o = { "x": 5 }; console.log("x" in o); //=>true 對象o有屬性x console.log("y" in o); //=>false 對象o沒有屬性x console.log("toString" in o); //=>true 對象o繼承屬性toString hasOwnProperty()方法用來檢測給定屬性是否為對象的自有屬性,對於繼承屬性返回false。 var o = { "x": 5 }; console.log(o.hasOwnProperty("x")); //=>true console.log(o.hasOwnProperty("toString")); //=>false propertyIsEnumerable()方法是hasOwnProperty()的增強版。只有檢測到屬性為對象的自有屬性並且這個屬性可枚舉性時才返回true。 var o = Object.create({ "y": 5 }); o.x = 6; console.log(o.propertyIsEnumerable("x")); //=>true x為自有屬性 console.log(o.propertyIsEnumerable("y")); //=>false y是繼承屬性 console.log(Object.prototype.propertyIsEnumerable("toString")); //=>false toString不可枚舉
5.屬性存取器
ECMAScript 5版本中,對象可以用get和set關鍵字定義像C#、Java等高級語言一樣的保護屬性。這種屬性被稱為“存取器屬性”,它是可以繼承的。
var obj = { //數據屬性(可看成字段) data: null, //存取器屬性(保護屬性) get Data() { return this.data; }, set Data(value) { this.data = value; } }; obj.Data = "admin"; console.log(obj.data); //=>admin
怎麼樣,有沒有感覺和JAVA中的保護屬性寫法很像。因為JavaScript本身就是一種面向對象的編程語言。
如果對象屬性同時具有get和set方法,那麼它是一個可讀/寫的屬性。如果屬性只有一個get方法,那麼它是一個只讀屬性。如果屬性只有一個set方法,那麼它是一個只寫屬性,讀取只寫屬性總是返回undefined。
6.屬性的特性
ECMAScript 3版本下對象的屬性都是否可寫、可配置和可枚舉的,但是到ECMAScript 5版本下是屬性是可以通過一些API來標識是否為可寫、可配置和可枚舉的。這API也就是所謂的屬性的特性。
•普通數據屬性的4個特性:value(值)、writable(可寫性)、enumerable(可枚舉性)、configurable(可配置性)。
•存儲器屬性的4個特性:get(讀取)、set(寫入)、enumerable(可枚舉性)、configurable(可配置性)。
ECMAScript 5中定義了一個Object.getOwnPropertyDescriptor()方法用來查詢對象特定屬性的特性,返回一個“屬性描述符”對象,該對象就代表對象屬性的4個特性。
var descriptor = Object.getOwnPropertyDescriptor({ length: 50 }, "length"); console.log(descriptor); //=> descriptor = { value: 50, writable: true, enumerable: true, configurable: true } //------------------------------------------------------------------ var random = { //只讀屬性:返回一個0-255之間的隨機數 get octet() { return Math.floor(Math.random() * 256); } }; var descriptor1= Object.getOwnPropertyDescriptor(random,"octet"); console.log(descriptor1); //=> descriptor1 = Object {set: undefined, enumerable: true, configurable: true}
從名字可以看出該方法只能得到對象自有屬性的描述符,所以對於繼承屬性和不存在的屬性,返回undefined。要獲得繼承屬性的特性,需要遍歷原型鏈。
要想設置屬性或讓新創建屬性具有某種特性,則需要調用Object.defineProperty()方法,第一個參數是要修改的對象;第二個參數是要修改的屬性;第三個是屬性描述符對象。返回值為修改後的對象副本。
var o = {}; //創建一個空對象 Object.defineProperty(o, "x", { value: 1, //定義一個x屬性,賦值為1 writable: true, //可寫 enumerable: false, //不可枚舉 configurable: true //可配置 }); if (o.x) console.log(Object.keys(o)); //=> props = [] 屬性存在,但是不能枚舉 Object.defineProperty(o, "x", { writable: false }); //讓屬性x變為只讀 o.x = 2; //試圖修改屬性x的值失敗,但不報錯 console.log(o.x); //=>1 Object.defineProperty(o, "x", { value: 2 }); //但屬性x依然為可配置,可以直接修改value值特性。 console.log(o.x); //=>2 Object.defineProperty(o, "x", { //將數據屬性修改為存取器屬性 get: function () { return 0; } }); console.log(o.x); //=>0
該方法同樣不能設置繼承屬性的特性。如果需要同時修改多個自有屬性的特性可以使用Object.defineProperties()方法。第一個參數是要修改的對象;第二參數是一個映射表對象,它包含屬性名稱和對應屬性的描述符對象。
var p = Object.defineProperties({}, { x: { value: 3, writable: true, enumerable: true, configurable: true }, y: { value: 4, writable: true, enumerable: true, configurable: true }, r: { get: function () { return Math.sqrt(this.x * this.x + this.y * this.y); }, enumerable: true, configurable: true } }); console.log(p.r); //=>5
7.對象的三個屬性
原型屬性
對象的原型是用來繼承屬性的,這個屬性非常重要,以至於經常把“o的原型屬性”直接叫做“o的原型”。
原型屬性是在對象創建之初就設置好的。前面已對原型做過介紹,但這裡還是要補充補充。
•通過對象直接量創建的對象使用Object.prototype作為原型;
•通過new關鍵字創建的對象使用構造函數的prototype作為原型;
•通過Object.create()創建的對象使用第一個參數作為原型。
在ES5版本中,將對象傳入Object.getPrototypeOf()方法可以查詢它的原型對象。
想要檢測一個對象是否是另一個對象的原型可以使用isPrototypeOf()方法。
var a = { x: 2 }; var b = Object.create(a); console.log(a.isPrototypeOf(b)); //=> true console.log(Object.prototype.isPrototypeOf(b));//=> true
類屬性
對象的類屬性是一個字符串,用來表示對象的類型信息。但是JS中沒有提供直接查詢方法,只能用一種間接的方法查詢,可以調用對象的toString()方法,然後提取返回字符串的第8個字符至倒數第二個位置之間的字符。如果對象繼承的toString()方法重寫了,這時必須間接通過Function.call()方法調用。
function classof(o) { if (o === null) return "Null"; if (o === undefined) return "Undefined"; return Object.prototype.toString.call(o).slice(8,-1); }
classof()可以接收任何類型的參數,並且該函數包含了對null和undefined的特殊處理。
console.log(classof(null)); //=> "Null" console.log(classof(1)); //=> "Number" console.log(classof("")); //=> "String" console.log(classof(false)); //=> "Boolen" console.log(classof({})); //=> "Object" console.log(classof(/./)); //=> "Regexp" console.log(classof(window)); //=> "Window"(浏覽器宿主對象類)
可擴展性
對象的可擴展行用來表示是否可以給對象添加新屬性。ECMAScript 5版本中,所有自定義對象、內置對象和宿主對象默認支持可擴展性。下面介紹幾個檢測和設置對象可擴展性的方法以及它們之間的區別。
Object.preventExtensions()方法能將傳入對象設置為不可擴展的。需要注意的兩點是:1.一旦對象轉為不可擴展的,就無法再將其轉換成可擴展的;2.如果給一個不可擴展的對象的原型添加屬性,這個不可擴展的對象同樣會繼承這些新屬性。
Object.isExtensible()方法可以檢測傳入對象的可擴展性。
Object.seal()方法能將傳入對象設置為不可擴展的,並且將對象所有自有屬性都設置為不可配置的。也就是說不能給這個對象添加新屬性,而且也不能刪除或配置已有屬性。對於已經密封的對象同樣不能解封,可以使用Object.isSealed()方法檢測對象是否封閉。
Object.freeze()方法更“狠”,它會直接將對象凍結。除了將對象設置為不可擴展和其屬性設置為不可配置之外,還將對象自有屬性的所有數據屬性設置為只讀屬性。可以使用Object.isFrozen()方法檢測對象是否被凍結。
Object.preventExtensions()、Object.seal()和Object.freeze()三個方法都返回傳入的對象。
8.序列化對象
相信大家對JSON都不陌生,其實該小節就是介紹JSON序列化。所謂序列化就是JS對象和字符串之間的互相轉換,JSON作為數據交換格式。ECMAScript 5 中提供兩個內置函數JSON.stringify()和JSON.parse()用來序列化和還原JS對象。
var obj = { x: 3, y: 5 }; //定義一個測試對象 var str = JSON.stringify(obj); //str = "{"x":3,"y":5}" obj = JSON.parse(str); //obj = Object {x: 3, y: 5}
JSON的全稱是“JavaScript Object Notation”---JavaScript對象表示法。JSON的語法並不能表示JavaScript裡所有的所有值。支持序列化和還原的有對象、NaN、數組、字符串、無窮大數字、true\false和null。函數、RegExp、Error對象和undefined值不能序列化和還原。JSON.stringify()函數只能序列化對象可枚舉的自有屬性。日期對象序列化的結果是ISO格式的日期字符串。
以上所述是小編給大家介紹的JavaScript權威指南之對象,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對網站的支持!