在JavaScript中,萬物皆來自對(duì)象,但是對(duì)Object()這個(gè)對(duì)象的構(gòu)造函數(shù)卻一知半解掷贾,所以借這個(gè)機(jī)會(huì)來縷清楚Object的方法倦春,并提供低版本的瀏覽器中兼容方法户敬。
//從chrome瀏覽器中的控制臺(tái)輸入Object.getOwnPropertyNames(Object),就能夠得到Object對(duì)象所有自身屬性和方法。
Array ["length", "name", "arguments", "caller", "prototype", "assign", "getOwnPropertyDescriptor",
"getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "is", "preventExtensions", "seal",
"create", "defineProperties", "defineProperty", "freeze", "getPrototypeOf", "setPrototypeOf", "isExtensible",
"isFrozen", "isSealed", "keys", "entries", "values"]
//從輸出臺(tái)中我們可以知道Object共有25個(gè)屬性和方法溅漾,當(dāng)然由于瀏覽器的兼容性問題山叮,在Firefox瀏覽器中輸入,得到如下
Array [ "assign", "getPrototypeOf", "setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
"keys", "values", "entries", "is", "defineProperty","defineProperties","creat","getOwnPropertyNames",
"getOwnPropertySymbols","preventExtensions", "seal","isFrozen", "isSealed","prototype","length","name",
"freeze","isExtensible"]
//所以為了最大限度的得到Object中的方法添履,我將以chrome版本得到的數(shù)據(jù)為基準(zhǔn)屁倔,對(duì)Object的方法進(jìn)行解析,一些還處于試驗(yàn)
//狀態(tài)也會(huì)提到暮胧。
Object.assign()
Object.assign(target,source)
中將sources對(duì)象中所有可枚舉的屬性的值復(fù)制到目標(biāo)的對(duì)象中锐借,其會(huì)返回目標(biāo)對(duì)象。該方法的兼容性不是很好往衷,IE全面淪陷钞翔,在移動(dòng)端方面,僅有少數(shù)的瀏覽器才兼容該方法席舍。幸好的是在MDN上提供了兼容的方法布轿。
if(typeof Object.assign!='function'){
Object.assign = function(target){
'use strict';
if(target = null){
throw new TypeError('Cannot convert undefined or null to object');
}
target = Object(target);
for(var index=0;index<arguments.length;index++){
var source = arguments[index];
if(source!=null){
for(var key in source){
if(Object.prototype.hasOwnProperty.call(source,key)){
target[key] = source[key];
}
}
}
}
return target;
}
}
其實(shí)如果我們仔細(xì)觀看源碼的時(shí)候就會(huì)發(fā)現(xiàn)一個(gè)事實(shí),那就是Object.assign()
只是對(duì)一級(jí)屬性進(jìn)行復(fù)制,而不會(huì)對(duì)對(duì)象里面的對(duì)象進(jìn)行深度拷貝汰扭,如果出現(xiàn)同名屬性的key值稠肘,那么后者會(huì)覆蓋前者,并不能做到完整的融合萝毛,如果要進(jìn)行融合的話项阴,可以前往depp-assign中閱讀。
Object.create()
Object.create(__proto__,[properties])
該方法將__proto__
作為原型對(duì)象笆包,并將[properties]
作為新對(duì)象的屬性环揽。
Object.defineProperty()
Object.defineProperty(obj,prop,descriptor)
方法在obj對(duì)象上對(duì)prop屬性進(jìn)行定義或修改,其中descriptor為被定義或修改的屬性符庵佣。其中對(duì)于descriptor屬性符可以設(shè)置的值如下顯示:
- 【value】表示屬性的值歉胶,默認(rèn)為undefined
- 【writable】該屬性是否為可寫,如果直接在對(duì)象上定義屬性巴粪,則默認(rèn)為true跨扮。如果設(shè)置為false,則屬性僅為可讀验毡。
- 【configurable】 如果為false的話衡创,則不能修改(writabel,configurable,enumerable),如果直接在對(duì)象上定義屬性晶通,則默認(rèn)為true
- 【enumerable】是否能夠被枚舉璃氢,如果直接在對(duì)象上定義屬性,則默認(rèn)為true狮辽。
- 【get】當(dāng)對(duì)象訪問prop屬性的實(shí)話一也,會(huì)調(diào)用這個(gè)方法,并返回結(jié)果喉脖。默認(rèn)為undefined
- 【set】當(dāng)對(duì)象設(shè)置該屬性的時(shí)候椰苟,會(huì)調(diào)用這個(gè)方法,默認(rèn)為undefined树叽。
在MVVM框架中的雙向數(shù)據(jù)綁定是通過 Object.defineProperty()
來實(shí)現(xiàn)的舆蝴,其核心的代碼如下所示:
function defineReactive(obj, key, value) {
var dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if (Dep.target) {
dep.depend()
}
return value
},
set: function reactiveSetter(newVal) {
if (value === newVal) {
return
} else {
value = newVal
dep.notify()
}
}
})
}
Object.defineProperty(obj,prop,descriptor)
目前兼容性做的不錯(cuò),移動(dòng)端上兼容絕大部分的瀏覽器题诵,PC上也能夠兼容到IE9及以上洁仗。注意,該方法在IE8上也是能夠使用的性锭,但是其只能夠傳入DOM對(duì)象赠潦,如果傳入其他對(duì)象會(huì)報(bào)錯(cuò)〔莞裕可能有人會(huì)對(duì)該方法的作用產(chǎn)生疑問她奥,其實(shí)該方法極大的優(yōu)化了對(duì)象的獲取和修改屬性方式瓮增,以下是來自騰訊AlloyTeam的一個(gè)示例:
//當(dāng)做動(dòng)畫效果的時(shí)候,常常這樣做哩俭,非常的繁瑣
var targetDom = document.getElementById('target');
var transformText = 'translateX(' + 10 + 'px)';
targetDom.style.webkitTransform = transformText;
targetDom.style.transform = transformText;
//有了Object.defineProperty()之后就可以這樣做了
Object.defineProperty(targetDom, 'translateX', {
set: function(value) {
var transformText = 'translateX(' + value + 'px)';
dom.style.webkitTransform = transformText;
dom.style.transform = transformText;
}
}
//這樣再后面調(diào)用的時(shí)候, 十分簡(jiǎn)單
dom.translateX = 10;
dom.translateX = -10;
可能有人覺得這樣做還是有點(diǎn)繁瑣钉赁,但是我們可以封裝一個(gè)函數(shù)庫專門做動(dòng)畫效果。
具體的GitHub地址可以前往這里觀看Object.defineProperty動(dòng)畫應(yīng)用携茂。
Object.defineProperties()
Object.defineProperties(obj,props)
方法直接在一個(gè)對(duì)象上修改或創(chuàng)建屬性,并返回修改后的對(duì)象诅岩,其與上面那個(gè)方法的區(qū)別在于前者可以修改或定義多個(gè)屬性讳苦,但是后者可以定義或修改多個(gè),同時(shí)二者的兼容性一樣吩谦。對(duì)于前者而言鸳谜,有相應(yīng)的polyfill函數(shù)。如下顯示:
function defineProperties(obj, properties)
{
function convertToDescriptor(desc){
function hasProperty(obj, prop){
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function isCallable(v){
// 如果除函數(shù)以外,還有其他類型的值也可以被調(diào)用,則可以修改下面的語句
return typeof v === "function";
}
if (typeof desc !== "object" || desc === null)
throw new TypeError("不是正規(guī)的對(duì)象");
var d = {};
if (hasProperty(desc, "enumerable"))
d.enumerable = !!desc.enumerable;
if (hasProperty(desc, "configurable"))
d.configurable = !!desc.configurable;
if (hasProperty(desc, "value"))
d.value = desc.value;
if (hasProperty(desc, "writable"))
d.writable = !!desc.writable;
if (hasProperty(desc, "get")){
var g = desc.get;
if (!isCallable(g) && g !== "undefined")
throw new TypeError("bad get");
d.get = g;
}
if (hasProperty(desc, "set")){
var s = desc.set;
if (!isCallable(s) && s !== "undefined")
throw new TypeError("bad set");
d.set = s;
}
if (("get" in d || "set" in d) && ("value" in d || "writable" in d))
throw new TypeError("identity-confused descriptor");
return d;
}
if (typeof obj !== "object" || obj === null)
throw new TypeError("不是正規(guī)的對(duì)象");
properties = Object(properties);
var keys = Object.keys(properties);
var descs = [];
for (var i = 0; i < keys.length; i++)
descs.push([keys[i], convertToDescriptor(properties[keys[i]])]);
for (var i = 0; i < descs.length; i++)
Object.defineProperty(obj, descs[i][0], descs[i][1]);
return obj;
}
Object.getOwnPropertyDescriptor(obj,prop)
該方法是用于如果prop屬性存在對(duì)象obj上式廷,則返回其屬性描述符咐扭,如果不存在就返回undefined。該屬性描述符由下面的屬性所組成莱没。
- 【value】表示屬性的值刽射,僅針對(duì)數(shù)據(jù)屬性描述符有效
- 【writable】當(dāng)且僅當(dāng)屬性的值可以被改變時(shí)為true词顾。(僅針對(duì)數(shù)據(jù)屬性描述有效)
- 【configurable】 當(dāng)且僅當(dāng)指定對(duì)象的屬性描述可以被改變或者屬性可被刪除時(shí),為true薛闪。
- 【enumerable】當(dāng)且僅當(dāng)指定對(duì)象的屬性可以被枚舉出時(shí),為 true俺陋。
- 【get】獲取該屬性的訪問器函數(shù)(getter)豁延。如果沒有訪問器, 該值為undefined腊状。
- 【set】獲取該屬性的設(shè)置器函數(shù)(setter)诱咏。如果沒有設(shè)置器,該值為undefined缴挖。(僅針對(duì)包含訪問器或設(shè)置器的屬性描述有效)
該方法能夠在IE8及以上的瀏覽器上運(yùn)行袋狞。
Object.getOwnPropertyNames(obj)
該方法返回obj上所有自身可枚舉和不可枚舉的屬性(不包括原型鏈上的屬性),如果傳入的obj不是數(shù)組映屋,則會(huì)報(bào)錯(cuò)硕并。該方法的兼容IE9及以上的瀏覽器。
Object.getPrototypeOf(obj)
該方法返回對(duì)象的原型對(duì)象秧荆,如果沒有的話倔毙,則返回null。需要指出的是乙濒,對(duì)于函數(shù)對(duì)象陕赃,其返回的并不是顯式原型(prototype)卵蛉,而是隱式原型(__proto__
),該方法兼容IE9及以上的瀏覽器么库。
Object.is(val1,val2)
該方法是確定兩個(gè)值是否是相同的值傻丝,這個(gè)方法與===
相比,其會(huì)將-0和+0看成不等诉儒,并且對(duì)于兩個(gè)NaN的比較葡缰,Object.is()也會(huì)看成是不等的。以下是Object.is()的示例:
Object.is(0,-0)//false
Object.is(-0,-0);//true
Object.is(NaN,0/0); //true
Object.is(5,5/1); //true
該方法在微軟公司出的瀏覽器上只支持EDGE瀏覽器忱反。不過泛释,萬幸的是MDN提供了相應(yīng)的解決方案。
if (!Object.is) {
Object.is = function(x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
};
}
Object.preventExtensions()
該方法可以讓一個(gè)對(duì)象永遠(yuǎn)不能添加新的屬性温算,在嚴(yán)格模式下怜校,如果強(qiáng)行為對(duì)象添加屬性,會(huì)報(bào)錯(cuò)注竿,以下是Object.isExtensible()的注意事項(xiàng):
"use strict";
var obj = {name:"zhang"};
obj.name = "li"http://可以進(jìn)行修改
Object.preventExtensions(obj);
//obj.age = 14;嚴(yán)格模式下會(huì)報(bào)錯(cuò)
obj.__proto__.age = 13;
console.log(obj);//能夠在原型對(duì)象上添加屬性
obj.__proto__ = {}//不能直接重定義原型茄茁,會(huì)報(bào)錯(cuò)。
Object.seal(obj)
其對(duì)一個(gè)對(duì)象進(jìn)行密封巩割,并返回被密封的對(duì)象裙顽,這些對(duì)象都是不能夠添加屬性,不能刪除已有屬性宣谈,以及不能夠修改已有屬性的可枚舉型锦庸、可配置型、可寫性蒲祈。
Object.freeze(obj)
該方法將obj對(duì)象凍結(jié)甘萧,其任何屬性都是不可以被修改的。現(xiàn)在我們演示下這個(gè)用法梆掸。
var obj = {name:"zhangsan",prop:{age:23,sex:"man"}};
Object.freeze(obj);
obj.name = "lisi";
console.log(obj.name);//"zhangsan
//我們使用Object.defineProperty()方法來修改屬性
Object.defineProperty(obj,'prop',{"age":32,sex:"female"});
console.log(obj.prop);
//{age: 23, sex: "man"}貌似還是不行扬卷,我們換種方式看看
Object.prop.age = 25;
console.log(Object.prop);
//{age: 25, sex: "man"}
//這個(gè)對(duì)象居然改變了,明明已經(jīng)凍結(jié)了酸钦,為什么起屬性還是可以發(fā)生變化
這就要說到Object.freeze(obj)
的特性了怪得,其只是一個(gè)淺凍結(jié)。何為淺凍結(jié)卑硫?淺凍結(jié)僅僅是對(duì)對(duì)象的一級(jí)屬性進(jìn)行凍結(jié)徒恋,像上面代碼中所演示的那樣,如果直接修改其name和prop屬性是不能被修改的欢伏。如果屬性也是一個(gè)對(duì)象的話入挣,那將不一樣了,直接對(duì)屬性中的屬性就行修改硝拧,如Object.prop.age = 25;
一樣径筏,是可以修改的葛假。既然有淺凍結(jié),就一定有深凍結(jié)了滋恬,那怎么才能實(shí)現(xiàn)深凍結(jié)呢聊训?
//我們可以配合遞歸實(shí)現(xiàn)
Object.prototype.deepFreeze = Object.prototype.deepFreeze || function (o){
var prop, propKey;
Object.freeze(o); // 首先凍結(jié)第一層對(duì)象
for (propKey in o){
prop = o[propKey];
if(!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)){
continue;
}
deepFreeze(prop); // 遞歸
}
}
可以有人會(huì)對(duì)preventExtensions,seal,freeze這三個(gè)方法產(chǎn)生疑問,這三個(gè)方法對(duì)從擴(kuò)展恢氯、密封和凍結(jié)三個(gè)方面對(duì)對(duì)象進(jìn)行讀寫狀態(tài)的控制带斑,防止對(duì)象被改變。其中最弱的一層是preventExtensions勋拟,只能讓對(duì)象無法添加新的屬性勋磕,其次是seal,該方法無法添加屬性指黎,也無法刪除屬性,最后是freeze州丹。當(dāng)然醋安,上面這三種方法還是可以通過改變對(duì)象的原型對(duì)象,來增加原型鏈上的屬性墓毒,并且都使淺凍結(jié)吓揪。有對(duì)對(duì)象進(jìn)行控制的方法,就肯定有判斷其是否被控制的方法所计,Object.isExtensible()
柠辞、Object.isSealed()
和Object.isfreeze(obj)
。這三個(gè)是判斷是否被控制主胧,在此就不再贅述叭首。
Object.keys(obj)
該方法會(huì)返回obj上所有可以進(jìn)行枚舉的屬性的字符串?dāng)?shù)組,如下所示:
//數(shù)組對(duì)象
var arr =[3,4,5];
console.log(Object.keys(obj))
//[0,1,2]
var obj = {}
console.log(Object.keys(obj))
//[],其不會(huì)遍歷原型鏈上的屬性踪栋。
該方法兼容IE9及以上的瀏覽器焙格,但是有相應(yīng)的解決方法。
if (!Object.keys) {
Object.keys = (function () {
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
var result = [];
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i=0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
}
})()
};
getOwnPropertySymbols(obj)
該方法返回obj對(duì)象上自身的(非繼承的)所有Symbol屬性鍵夷都。
Object.entries()眷唉、Object.getOwnPropertyDescriptors()、Object.values()
這些方法還只是處于試驗(yàn)階段的囤官,所以不在這里進(jìn)行展開冬阳。