一、對象字面量語法
var person={ name:'小王', age:18, _pri:233 }
- 成員名稱的單引號不是必須的
- 最后一個成員結尾不要用逗號,不然在某些瀏覽器中會拋出錯誤
- 成員名相同會發生什么?
es5普通模式下后定義的會覆蓋前面定義的,嚴格模式則會報錯
es6則不管什么模式都采用后面的覆蓋前面的
- 成員名可以是動態變量嗎?
es5只能在對象字面量表達式申明以后再添加
原文鏈接:https://www.cnblogs.com/94pm/p/9179231.html
var dynamicVar="dyna"; var person={ } person[dynamicVar]='123'; console.log(person[dynamicVar])
es6則更符合使用場景,可在表達式內創建動態的成員名
var dynamicVar="dyna"; var person={ [dynamicVar]:'test' } console.log(person[dynamicVar])
es6中如果想使用表達式外面的變量名作為成員名,變量的值作為成員值,可進一步簡寫為
var dynamicVar="dyna"; var person={ dynamicVar, //這是一個語法糖,js引擎會解釋為dynamicVar:'dyna' age:15 } console.log(person.dynamicVar)
注意:此時不能采用person[dynamicVar]方式訪問,因為這句話js引擎會解釋為person['dyna'],對象中沒有dyna屬性,肯定就是undefined了
- 可以采用new person()的方式使用嗎?
肯定是不可以,new關鍵字后面必須是一個構造函數才行,對象字面量哪來的構造函數
二、對象屬性描述符
對象屬性描述符有兩種主要形式:
數據描述符和存取描述符。數據描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。
存取描述符是由getter-setter函數對描述的屬性。
Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()-讀取屬性的描述
Object.definePropertype或Object.defineProperties----設置屬性的描述
當屬性是采用Object.definePropertype創建時,省略的描述符會擁有默認值,布爾值的字段的默認值都是false
。value
,get
和set
字段的默認值為undefined
。
var parent={} Object.defineProperty(parent,'name',{}) console.log(Object.getOwnPropertyDescriptor(parent,'name')) //{value: undefined, writable: false, enumerable: false, configurable: false}
當屬性是直接是直接在對象中創建時,布爾值的字段默認都是true
var parent={ name:'parent' } console.log(Object.getOwnPropertyDescriptor(parent,'name')) //{value: "parent", writable: true, enumerable: true, configurable: true}
數據描述符:
configurable-是否可以刪除某個屬性或修改屬性的描述,為true時可進行操作,如果該屬性先定義為false,后續又定義為true的話會報錯
Object.defineProperty(person,'name',{ configurable:false }) Object.defineProperty(person,'name',{ configurable:true })
writable-屬性是否可寫
Object.defineProperty(person,'name',{ writable:false }) person.name='小李'; //屬性不可寫,嚴格模式下會報錯 console.log(person.name); //輸出小王,說明上面一句無效
enumerable-屬性是否可被枚舉,默認為false,該屬性主要是用來防范Object.keys()和for in的,也就是說該屬性設置對於Object.getOwnPropertyNames()方法是無效的。
使用相應的枚舉方法,輸出的結果是一個數組,那么數組中元素的順序是按什么規則組織的呢?
在es5中並沒有明確這一點,各個瀏覽器有自己的實現,es6中采用Object.keys()和for in方法時還是沒有明確,但采用Object.getOwnPropertyNames()方法枚舉時,有了相應的標准:
最先放入數組中的是數值型的成員名,按升序排列;
其次是其它類型的,按添加的先后順序排列
var obj={ 3:'我是1', 1:'我是1', b:'我是b', a:'我是a' } var names=Object.getOwnPropertyNames(obj); console.log(names) //["1","3","b","a"]
value
-
該屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。默認為 undefined
。
存取描述符:
get與set-讀寫成員時調用的函數,默認為undefined,如果只有get則表示屬性值是只讀的,只有set表示只能寫。屬性讀寫器最常用的場景就是在讀取或寫入屬性前可以附帶的做一些操作,達到更好的封裝性。
Object.defineProperty(person,'pri',{ get:function(){ //做一些其它操作 console.log('准備獲取_pri的值') return _pri; }, set:function(newValue){ _pri =newValue } }) person.pri='456'; console.log(person.pri);
Object.preventExtensions(person); //添加成員無效,非嚴格模式下什么都不會發生,嚴格模式下會報錯 person.bankAccount='中國農業銀行' //依然可以刪除成員,證明了preventExtensions方法只能阻止添加方法 delete person.age; console.log(person.age) //undefined,表明刪除成功了
- 我不想讓別人添加、刪除成員
Object.seal()用來阻止添加或刪除成員,判斷對象是否是密封的可采用Object.isSealed()
- 我不想讓別人添加、刪除成員,也不想讓別人對里面的成員進行賦值操作
使用Object.freeze()方法后,除了不能添加刪除成員,連成員的賦值都會失效,但是寫入屬性(上面set定義的)依然是有效
四、其它技巧
- 實現繼承
Object.create(person)可產生一個具有繼承特性的新對象,但是需要注意的是,父對象的引用類型成員是和子對象共享的,當子對象修改引用類型的成員時,父對象的該成員也會同步發生變化
var person={ name:'小王', age:18, _pri:233, gf:['豆得兒','張G','TFb'] } var child=Object.create(person); child.gf.splice(0,1); //跟豆得兒分手了 console.log(person.gf.length) //父類的gf也變成2了,父子共享女友,尼瑪,太亂了
es6中的Object.setPrototypeOf(obj, prototype)方法可將已有的對象變成繼承關系,其內部原理也跟Object.create一樣,都是將子對象的prototype指向父對象,該方法實現的繼承依然有父子對象共享了引用類型成員的問題
var person={ age:15 } var man={ } Object.setPrototypeOf(man,person) console.log(Object.getPrototypeOf(man)===person) //true console.log(man.age); //15
- 繼承對象的屬性賦值問題
向一個子對象的屬性賦值時,假如這個屬性是從父對象繼承下並且父對象中把該屬性設置為不可寫時,在嚴格模式下會報錯,非嚴格模式下賦值不生效
var parent={ name:'parent' } Object.defineProperty(parent,'name',{ writable:false }) var child=Object.create(parent) child.name='child' //嚴格模式下報錯,非嚴格模式下默認失敗
向一個子對象的屬性賦值時,假如這個屬性是從父對象繼承下來的並且父對象中設置了set描述符,則賦值時會觸發set,如果未定義get,則無法獲取屬性值
'use strict' var parent={ name:'parent' } Object.defineProperty(parent,'name',{ set(val){ console.log('父元素的set被調用了') this._name = val } }) var child=Object.create(parent) child.name='child' //會觸發父對象中的set console.log(child.name) //undefined,只有父對象的name屬性描述符設置了get才能獲取到值
- 如何重寫父對象的成員?
直接在子對象中定義一個同名的成員即可
- 如何實現在子對象中訪問父對象的成員?
super關鍵字是es6新增的,它是一個指針,指向當前對象的原型,也就是父對象
var person={ age:15, testMethod(){ console.log('我是父類方法') } } var man={ //重寫父類方法 testMethod(){ console.log('我是子類方法') super.testMethod(); } } Object.setPrototypeOf(man,person) man.testMethod();
需要注意的是,如果兩個對象不是繼承關系,使用super關鍵字會報錯
- 一句話實現jquery.extend
jquery.extend是一個典行的對象混入,所謂對象混入,就是將n個對象(為了便於表述,直接叫做輸入對象)組合成一個新對象,新對象具有各個輸入對象的特征,這在軟件設計模式中叫做裝飾器模式,在es6以前需要自己實現,核心代碼如下:
function mixins(target,sourceArr){ sourceArr.forEach(function(source){ Object.keys(source).forEach(function(item){ target[item] = source[item] }) }) return target } var obj1={ name:'123' } var obj2={ id:100 } var obj3={ meth(){ console.log('haha') } } var target=mixins(obj1,[obj2,obj3]) target.meth()
上面的代碼實現了一個簡易版的jquery.extend的淺拷貝模式(也就是deep參數為false時的功能),如果多個對象成員同名,則后面的會覆蓋前面的,該代碼如果要在正式環境使用,還需要加不少的判斷代碼,但是在es6中一句話就可以實現mixins()函數的功能。
var target=Object.assign(obj1,obj2,obj3)
需要注意的一點就是輸入對象中使用了get修飾符,這時后會有一個轉換,方法名變成了新對象的屬性名,其值為get修飾符方法中的返回值
var obj1={ name:'123' } var obj2={ id:100 } var obj3={ get getMethod(){ return '123' }, meth(){ console.log('haha') } } var target=Object.assign(obj1,obj2,obj3) console.log(target.getMethod) //target.getMethod()會報錯,原因是發生了轉換