詳解Javascript中的Object物件
Object是在javascript中一個被我們經常使用的型別,而且JS中的所有物件都是繼承自Object物件的。雖說我們平時只是簡單地使用了Object物件來儲存資料,並沒有使用到太多其他功能,但是Object物件其實包含了很多很有用的屬性和方法,尤其是ES5增加的方法,因此,本文將從最基本的介紹開始,詳細說明了Object的常用方法和應用。
基礎介紹
建立物件
首先我們都知道,物件就是一組相似資料和功能的集合,我們就是用它來模擬我們現實世界中的物件的。那在Javascript中,建立物件的方式通常有兩種方式:建構函式和物件字面量。
new建構函式法
var person = new Object(); person.name = "狼狼的藍胖子"; person.age = 25;
這種方式使用new關鍵字,接著跟上Object建構函式,再來給物件例項動態新增上不同的屬性。這種方式相對來說比較繁瑣,一般推薦使用物件字面量來建立物件。
物件字面量
物件字面量很好理解,使用key/value的形式直接建立物件,簡潔方便。
var person = { name: “狼狼的藍胖子”, age: 25 };
這種方式直接通過花括號將物件的屬性包起來,使用key/value的方式建立物件屬性,每個屬性之間用逗號隔開。
注意:如果是最後一個屬性,後面就不要加逗號,因為在一些舊的瀏覽器下會報錯。
物件例項的屬性和方法
不管通過哪種方式建立了物件例項後,該例項都會擁有下面的屬性和方法,下面將會一一說明。
constructor屬性
constructor屬性是儲存當前物件的建構函式,前面的例子中,constructor儲存的就是Object方法。
var obj1 = new Object(); obj1.id = "obj1"; var obj2 = { "id": "obj2" }; console.log(obj1.constructor);//function Object(){} console.log(obj2.constructor);//function Object(){}
hasOwnProperty(propertyName)方法
hasOwnProperty方法接收一個字串引數,該參數列示屬性名稱,用來判斷該屬性是否在當前物件例項中,而不是在物件的原型鏈中。我們來看看下面這個例子:
var arr = []; console.log(arr.hasOwnProperty("length"));//true console.log(arr.hasOwnProperty("hasOwnProperty"));//false
在這個例子中,首先通過定義了一個陣列物件的例項arr,我們知道陣列物件實際是通過原型鏈繼承了Object物件,然後擁有自己的一些屬性,我們通過hasOwnProperty方法判斷length是arr自己的屬性,而hasOwnProperty是在原型鏈上的屬性。
hasOwnProperty方法可以和for..in結合起來獲取物件自己的key。
isPrototypeOf(Object)方法
isPrototype方法接收一個物件,用來判斷當前物件是否在傳入的引數物件的原型鏈上,說起來有點抽象,我們來看看程式碼。
function MyObject() {} var obj = new MyObject(); console.log(Object.prototype.isPrototypeOf(obj));
我們知道MyObject是繼承自Object物件的,而在JS中,繼承是通過prototype來實現的,所以Object的prototype必定在MyObject物件例項的原型鏈上。
propertyIsEnumerable(prototypeName)方法
prototypeIsEnumerable用來判斷給定的屬性是否可以被for..in語句給列舉出來。看下面程式碼:
var obj = { name: "objName" } for (var i in obj) { console.log(i); }
執行這段程式碼輸出字串“name”,這就說明通過for…in語句可以得到obj的name這個屬性,但是我們知道,obj的屬性還有很多,比如constructor,比如hasOwnPrototype等等,但是它們沒有被輸出,說明這些屬性不能被for…in給列舉出來,可以通過propertyIsEnumerable方法來得到。
console.log(obj.propertyIsEnumerable("constructor"));//false
判斷“constructor”是否可以被列舉,輸出false說明無法被列舉出來。
toLocaleString()方法
toLocalString方法返回物件的字串表示,和程式碼的執行環境有關。
var obj = {}; console.log(obj.toLocaleString());//[object Object] var date = new Date(); console.log(date.toLocaleString());//2016/2/28 下午1:39:27
toString()方法
toString用來返回物件的字串表示。
var obj = {}; console.log(obj.toString());//[object Object] var date = new Date(); console.log(date.toString());//Sun Feb 28 2016 13:40:36 GMT+0800 (中國標準時間)
valueOf()方法
valueOf方法返回物件的原始值,可能是字串、數值或bool值等,看具體的物件。
var obj = { name: "obj" }; console.log(obj.valueOf());//Object {name: "obj"} var arr = [1]; console.log(arr.valueOf());//[1] var date = new Date(); console.log(date.valueOf());//1456638436303
如程式碼所示,三個不同的物件例項呼叫valueOf返回不同的資料。
屬性的型別
在Javascript中,屬性有兩種型別,分別是資料屬性和訪問器屬性,我們來看看這兩種屬性具體是什麼東西。
資料屬性
資料屬性我們可以理解為我們平時定義物件時賦予的屬性,它可以進行讀和寫。但是,ES5中定義了一些特性,這些特性是用來描述屬性的各種特徵,特性是內部值,不能直接訪問到。特性通過用兩對方括號表示,比如[[Enumerable]]。屬性的特性會有一些預設值,要修改特性的預設值,必須使用ES5定義的新方法Object.defineProperty方法來修改。
資料屬性有4個描述其特徵的特性,下面將依次說明每一個特性:
(1)[[Configurable]]:該特性表示是否可以通過delete操作符來刪除屬性,預設值是true。
var obj = {}; obj.name = "myname"; delete obj.name; console.log(obj.name);//undefined
這段程式碼很明顯,通過delete刪除了obj的name屬性後,我們再訪問name屬性就訪問不到了。
我們通過Object.defineProperty方法來修改[[Configurable]]特性。
var obj = {}; obj.name = "myname"; Object.defineProperty(obj, "name", { configurable: false }) delete obj.name; console.log(obj.name);//myname
通過將configurable特性設定成false之後,delete就無法刪除name屬性了,如果在嚴格模式下,使用delete去刪除就會報錯。
(2)[[Enumerable]]:表示是否能夠通過for…in語句來列舉出屬性,預設是true
我們來看看前面的例子:
var obj = { name: "objName" } for (var i in obj) { console.log(i);//name }
這段程式碼只輸出了name屬性,我們來將constructor屬性的[[Enumerable]]設定為true試試。
var obj = { name: "objName" } Object.defineProperty(obj, "constructor", { enumerable: true }) for (var i in obj) { console.log(i);//name,constructor } console.log(obj.propertyIsEnumerable("constructor"));//true
這段程式碼中,for…in迴圈得到了name和constructor兩個屬性,而通過propertyIsEnumerable方法來判斷constructor也返回了true。
(3)[[Writable]]:表示屬性值是否可以修改,預設為true
如果[[Writable]]被設定成false,嘗試修改時將沒有效果,在嚴格模式下會報錯
(4)[[Value]]:表示屬性的值,預設為undefined
我們通過一個簡單的例子來看看這兩個特性:
var obj = { name: "name" }; console.log(obj.name);//name Object.defineProperty(obj, "name", { value: "newValue", writable: false }) console.log(obj.name);//newValue obj.name = "oldValue"; console.log(obj.name);//newValue
我們首先定義了obj物件的name屬性值為“name”,然後通過defineProperty方法來修改值,並且將其設定為不可修改的。接著我們再修改name屬性的值,可以發現修改無效。
如果我們通過defineProperty來修改name屬性的值,是否可以修改呢?答案是可以的:
Object.defineProperty(obj, "name", { value: "oldValue" }) console.log(obj.name); //oldValue
訪問器屬性
訪問器屬性有點類似於C#中的屬性,和資料屬性的區別在於,它沒有資料屬性的[[Writable]]和[[Value]]兩個特性,而是擁有一對getter和setter函式。
[[Get]]:讀取屬性時呼叫的函式,預設是undefined
[[Set]]:設定屬性時呼叫的函式,預設是undefined
getter和setter是一個很有用的東西,假設有兩個屬性,其中第二個屬性值會隨著第一個屬性值的變化而變化。這種場景在我們平時的編碼中起始是非常常見的。在之前的做法中,我們往往要去手動修改第二個屬性的值,那現在我們就可以通過get和set函式來解決這個問題。看下面這個例子:
var person = { age: 10 } Object.defineProperty(person, "type", { get: function () { if (person.age > 17) { return "成人"; } return "小孩"; } }) console.log(person.type);//小孩 person.age = 18; console.log(person.type);//成人
通過修改age的值,type的值也會相應的修改,這樣我們就不用再手動的去修改type的值了。
下面這種方式也是可以實現同樣的效果:
var person = { _age: 10, type: "小孩" } Object.defineProperty(person, "age", { get: function () { return this._age; }, set: function (newValue) { this._age = newValue; this.type = newValue > 17 ? "成人" : "小孩"; } }) console.log(person.type); person.age = 18; console.log(person.type);
關於訪問器屬性,有幾點要注意:
1、嚴格模式下,必須同時設定get和set
2、非嚴格模式下,可以只設定其中一個,如果只設定get,則屬性是隻讀的,如果只設定set,屬性則無法讀取
3、Object.defineProperty是ES5中的新方法,IE9(IE8部分實現,只有dom物件才支援)以下瀏覽器不支援,一些舊的瀏覽器可以通過非標準方法defineGetter()和defineSetter()來設定,這裡就不說明了,有興趣的同學可以查詢相關資料。
特性操作的相關方法
ES5提供了一些讀取或操作屬性特性的方法,前面用到的Object.defineProperty就是其中之一。我總結了一些比較常用的方法如下:
(1)Object.defineProperty
定義一個物件的屬性,這個方法前面我們已經用到多次,簡單說說其用法。
Object.defineProperty(obj,propName,descriptor);
defineProperty有點類似於定於在Object上的靜態方法,通過Object直接呼叫,它接收3個引數:
obj:需要定義屬性的物件
propNane:需要被定義的屬性名稱
defineProperty:屬性描述符,包含一些屬性的特性定義
例子如下:
var obj = {}; Object.defineProperty(obj, "name", { value: "name", configurable: true, writable: true, enumerable: true });
(2)Object.defineProperties
和defineProperty類似,是用來定義物件屬性的,不同的是它可以用來同時定義多個屬性,我們通過命名也可以看出來,用法如下:
var obj = {}; Object.defineProperty(obj, { "name": { value: "name", configurable: true, writable: true, enumerable: true }, "age": { value: 20 } });
(3)Object.getOwnPropertyDescriptor
ES5中還提供了一個讀取特性值的方法,該方法接收物件及其屬性名作為兩個引數,返回一個物件,根據屬性型別的不同,返回物件會包含不同的值。
var person = { _age: 10, type: "小孩" } Object.defineProperty(person, "age", { get: function () { return this._age; }, set: function (newValue) { this._age = newValue; this.type = newValue > 17 ? "成人" : "小孩"; } }) console.log(Object.getOwnPropertyDescriptor(person, "type"));//Object {value: "成人", writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(person, "age")); //Object {enumerable: false, configurable: false, get: function(),set: function ()}
Object的方法
在ES5中,Object物件上新增了一批方法,這些方法可以直接通過Object進行訪問,前面用到的defineProperty就是新增的方法之一。除此之外還有很多方法,我將其總結歸納如下:
物件建立型方法
Object.create(proto, [propertiesObject])
在前面我們提到,建立一個物件有兩種方法:建構函式和物件字面量。
這兩種方法有一個缺點就是:如果要建立多個物件,寫起來很繁瑣,所以後來就有了一種建立自定義建構函式的方法來建立物件,如下所示:
function Person(name, age) { this.name = name; this.age = age; } var person = new Person("Jack", 15);
這種方式可以很方便的建立多個同樣的物件,也是目前比較常用的方法。
ES5提供的Object.create方法也是一個建立物件的方法,這個方法允許為建立的物件選擇原型物件,不需要定義一個建構函式。用法如下:
var obj = Object.create(Object.prototype, { name: { value: "Jack" } }) console.log(obj.name);//Jack
這個方法接收的第一個引數作為被建立物件的原型,第二個引數是物件的屬性。注意:在這個例子中,name屬性是無法被修改的,因為它沒有設定writable特性,預設則為false。
個人看法:Object.create這種建立物件的方式略顯繁瑣,除非是需要修改屬性的特性,否則不建議使用這種方式建立物件。
屬性獲取型方法
Object.keys
Object.keys是用來獲取給定物件的所有可列舉的自身屬性的屬性名,它返回一個陣列。
function Parent() { this.lastName = "Black" } function Child(firstName) { this.firstName = firstName; } Child.prototype = new Parent(); var son = new Child("Jack"); console.log(Object.keys(son));//["firstName"]
程式碼中返回了firstName,並沒有返回從prototype繼承而來的lastName和不可列舉的相關屬性。
在一些舊的瀏覽器中,我們可以使用hasOwnProperty和for…in來達到類似的效果。
Object.keys = Object.keys || function (obj) { var keys = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return keys; }
Object.getOwnPropertyNames()
getOwnPropertyNames用來獲取物件自身的所有屬性,包括可列舉和不可列舉的所有屬性,如下所示:
function Parent() { this.lastName = "Black" } function Child(firstName) { this.firstName = firstName; } Child.prototype = new Parent(); var son = new Child("Jack"); Object.defineProperty(son, "age", { enumerable: false }) console.log(Object.keys(son));//["firstName"] console.log(Object.getOwnPropertyNames(son));//["firstName", "age"]
我們定義給son物件定義了一個不可列舉的屬性age,然後通過keys和getOwnPropertyNames兩個方法來獲取屬性列表,能明顯看出了兩者區別。
屬性特性型方法
這個主要是前面提到的三個方法:defineProperty,defineProperties和getOwnPropertyDescriptor三個方法
物件限制型方法
ES5中提供了一系列限制物件被修改的方法,用來防止被某些物件被無意間修改導致的錯誤。每種限制型別包含一個判斷方法和一個設定方法。
阻止物件擴充套件
Object.preventExtensions()用來限制物件的擴充套件,設定之後,物件將無法新增新屬性,用法如下:
Object.preventExtensions(obj);
該方法接收一個要被設定成無法擴充套件的物件作為引數,需要注意兩點:
1、物件的屬性不可用擴充套件,但是已存在的屬性可以被刪除
2、無法新增新屬性指的是無法在自身上新增屬性,如果是在物件的原型上,還是可以新增屬性的。
function Person(name) { this.name = name; } var person = new Person("Jack"); Object.preventExtensions(person); delete person.name; console.log(person.name);//undefined Person.prototype.age = 15; console.log(person.age);//15
Object.isExtensible方法用來判斷一個物件是否可擴充套件,預設情況是true
將物件密封
Object.seal可以密封一個物件並返回被密封的物件。
密封物件無法新增或刪除已有屬性,也無法修改屬性的enumerable,writable,configurable,但是可以修改屬性值。
function Person(name) { this.name = name; } var person = new Person("Jack"); Object.seal(person); delete person.name; console.log(person.name);//Jack
將物件密封后,使用delete刪除物件屬性,還是可以訪問得到屬性。
通過Object.isSealed可以用來判斷一個物件是否被密封了。
凍結物件
Object.freeze方法用來凍結一個物件,被凍結的物件將無法新增,修改,刪除屬性值,也無法修改屬性的特性值,即這個物件無法被修改。
function Person(name) { this.name = name; } var person = new Person("Jack"); Object.freeze(person); delete person.name; console.log(person.name);//Jack Person.prototype.age = 15; console.log(person.age);//15
分析上面的程式碼我們可以發現,被凍結的物件無法刪除自身的屬性,但是通過其原型物件還是可以新增屬性的。
通過Object.isFrozen可以用來判斷一個物件是否被凍結了。
可以發現:這三個限制物件的方法的限制程度是依次上升的。
總結
Object雖說是一個我們平時開發中最經常用到的物件,但是它的很多功能還沒有被我們挖掘出來。本文首先介紹了Object的基本使用,接著介紹了一些比較少使用到的屬性特性,最後分析了一些比較常用的方法,尤其是ES5中提供的新方法。歡迎大家交流!!
相關文章
- JavaScript中 Map 物件詳解JavaScript物件
- 談談JavaScript中建立物件(Object)JavaScript物件Object
- JavaScript Object 物件JavaScriptObject物件
- 詳解JavaScript之神奇的Object.definePropertyJavaScriptObject
- JS中Object的API詳解JSObjectAPI
- 詳解object detection中的mAPObject
- JavaScript arguments物件詳解JavaScript物件
- JavaScript arguments 物件詳解JavaScript物件
- JavaScript中的this詳解JavaScript
- 詳解JavaScript中的thisJavaScript
- JavaScript 檔案物件詳解JavaScript物件
- JavaScript 物件字面量(object literal)JavaScript物件Object
- JavaScript 基礎之物件ObjectJavaScript物件Object
- 深入瞭解JavaScript中的物件JavaScript物件
- JavaScript物件導向詳解(原理)JavaScript物件
- 詳解 Java 中的物件克隆Java物件
- Javascript中的Object.defineProperty()JavaScriptObject
- javascript,檢測object物件中是否存在某個屬性JavaScriptObject物件
- 瞭解 JavaScript 中的內建物件JavaScript物件
- TypeScript 中令人迷惑的物件型別:Object、{} 和 objectTypeScript物件型別Object
- JavaScript物件導向名詞詳解JavaScript物件
- JavaScript建立物件4種方法詳解JavaScript物件
- javascript中的Event Loop詳解JavaScriptOOP
- JavaScript 中的遍歷詳解JavaScript
- 詳解Javascript 中的this指標JavaScript指標
- JavaScript 複習之 Object物件的相關方法JavaScriptObject物件
- JavaScript新的物件建立方式---Object.create()JavaScript物件Object
- javascript如何判斷Object是空物件JavaScriptObject物件
- JavaScript入門④-萬物皆物件:ObjectJavaScript物件Object
- JavaScript學習四(object物件,陣列,)JavaScriptObject物件陣列
- Java中的類與物件詳解Java物件
- JavaScript中的Object相等(譯文)JavaScriptObject
- JavaScript中的Object的引用型別JavaScriptObject型別
- JavaScript內建物件--Error型別詳解JavaScript物件Error型別
- JavaScript 特殊物件 Array-Like Objects 詳解JavaScript物件Object
- JavaScript中的async/await詳解JavaScriptAI
- javascript中的閉包closure詳解JavaScript
- Object.defineProperty的用法詳解Object