在JavaScript中,万物皆来自对象,可是对Object()这个对象的构造函数却只知其一;不知其二,因此借这个机会来缕清楚Object的方法,并提供低版本的浏览器中兼容方法。react
//从chrome浏览器中的控制台输入Object.getOwnPropertyNames(Object),就可以获得Object对象全部自身属性和方法。
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"]
//从输出台中咱们能够知道Object共有25个属性和方法,固然因为浏览器的兼容性问题,在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版本获得的数据为基准,对Object的方法进行解析,一些还处于试验
//状态也会提到。复制代码
Object.assign(target,source)
中将sources对象中全部可枚举的属性的值复制到目标的对象中,其会返回目标对象。该方法的兼容性不是很好,IE全面沦陷,在移动端方面,仅有少数的浏览器才兼容该方法。幸亏的是在MDN上提供了兼容的方法。git
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复制代码
其实若是咱们仔细观看源码的时候就会发现一个事实,那就是Object.assign()
只是对一级属性进行复制,而不会对对象里面的对象进行深度拷贝,若是出现同名属性的key值,那么后者会覆盖前者,并不能作到完整的融合,若是要进行融合的话,能够前往depp-assign中阅读。github
Object.create(__proto__,[properties])
该方法将__proto__
做为原型对象,并将[properties]
做为新对象的属性。web
Object.defineProperty()
Object.defineProperty(obj,prop,descriptor)
方法在obj对象上对prop属性进行定义或修改,其中descriptor为被定义或修改的属性符。其中对于descriptor属性符能够设置的值以下显示:chrome
在MVVM框架中的双向数据绑定是经过 Object.defineProperty()
来实现的,其核心的代码以下所示:数组
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)
目前兼容性作的不错,移动端上兼容绝大部分的浏览器,PC上也可以兼容到IE9及以上。注意,该方法在IE8上也是可以使用的,可是其只可以传入DOM对象,若是传入其余对象会报错。可能有人会对该方法的做用产生疑问,其实该方法极大的优化了对象的获取和修改属性方式,如下是来自腾讯AlloyTeam的一个示例:浏览器
//当作动画效果的时候,经常这样作,很是的繁琐
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;
}
}
//这样再后面调用的时候, 十分简单
dom.translateX = 10;
dom.translateX = -10;复制代码
可能有人以为这样作仍是有点繁琐,可是咱们能够封装一个函数库专门作动画效果。
具体的GitHub地址能够前往这里观看Object.defineProperty动画应用。框架
Object.defineProperties()
Object.defineProperties(obj,props)
方法直接在一个对象上修改或建立属性,并返回修改后的对象,其与上面那个方法的区别在于前者能够修改或定义多个属性,可是后者能够定义或修改多个,同时两者的兼容性同样。对于前者而言,有相应的polyfill函数。以下显示:dom
function defineProperties(obj, properties)
{
function convertToDescriptor(desc){
function hasProperty(obj, prop){
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function isCallable(v){
// 若是除函数之外,还有其余类型的值也能够被调用,则能够修改下面的语句
return typeof v === "function";
}
if (typeof desc !== "object" || desc === null)
throw new TypeError("不是正规的对象");
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("不是正规的对象");
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属性存在对象obj上,则返回其属性描述符,若是不存在就返回undefined。该属性描述符由下面的属性所组成。ide
Object.getOwnPropertyNames(obj)
该方法返回obj上全部自身可枚举和不可枚举的属性(不包括原型链上的属性),若是传入的obj不是数组,则会报错。该方法的兼容IE9及以上的浏览器。
Object.getPrototypeOf(obj)
该方法返回对象的原型对象,若是没有的话,则返回null。须要指出的是,对于函数对象,其返回的并非显式原型(prototype),而是隐式原型(__proto__
),该方法兼容IE9及以上的浏览器。
Object.is(val1,val2)
该方法是肯定两个值是不是相同的值,这个方法与===
相比,其会将-0和+0当作不等,而且对于两个NaN的比较,Object.is()会当作是相等的,而===会将0、-0、+0当作相等的,两个NaN当作不等。如下是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提供了相应的解决方案。
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()
该方法可让一个对象永远不能添加新的属性,在严格模式下,若是强行为对象添加属性,会报错,如下是Object.isExtensible()的注意事项:
"use strict";
var obj = {name:"zhang"};
obj.name = "li"//能够进行修改
Object.preventExtensions(obj);
//obj.age = 14;严格模式下会报错
obj.__proto__.age = 13;
console.log(obj);//可以在原型对象上添加属性
obj.__proto__ = {}//不能直接重定义原型,会报错。复制代码
Object.seal(obj)
其对一个对象进行密封,并返回被密封的对象,这些对象都是不可以添加属性,不能删除已有属性,以及不可以修改已有属性的可枚举型、可配置型、可写性。
Object.freeze(obj)
该方法将obj对象冻结,其任何属性都是不能够被修改的。如今咱们演示下这个用法。
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"}
//这个对象竟然改变了,明明已经冻结了,为何起属性仍是能够发生变化复制代码
这就要说到Object.freeze(obj)
的特性了,其只是一个浅冻结。何为浅冻结?浅冻结仅仅是对对象的一级属性进行冻结,像上面代码中所演示的那样,若是直接修改其name和prop属性是不能被修改的。若是属性也是一个对象的话,那将不同了,直接对属性中的属性就行修改,如Object.prop.age = 25;
同样,是能够修改的。既然有浅冻结,就必定有深冻结了,那怎么才能实现深冻结呢?
//咱们能够配合递归实现
Object.prototype.deepFreeze = Object.prototype.deepFreeze || function (o){
var prop, propKey;
Object.freeze(o); // 首先冻结第一层对象
for (propKey in o){
prop = o[propKey];
if(!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)){
continue;
}
deepFreeze(prop); // 递归
}
}复制代码
能够有人会对preventExtensions,seal,freeze这三个方法产生疑问,这三个方法对从扩展、密封和冻结三个方面对对象进行读写状态的控制,防止对象被改变。其中最弱的一层是preventExtensions,只能让对象没法添加新的属性,其次是seal,该方法没法添加属性,也没法删除属性,最后是freeze。固然,上面这三种方法仍是能够经过改变对象的原型对象,来增长原型链上的属性,而且都使浅冻结。有对对象进行控制的方法,就确定有判断其是否被控制的方法,Object.isExtensible()
、Object.isSealed()
和Object.isfreeze(obj)
。这三个是判断是否被控制,在此就再也不赘述。
Object.keys(obj)
该方法会返回obj上全部能够进行枚举的属性的字符串数组,以下所示:
//数组对象
var arr =[3,4,5];
console.log(Object.keys(obj))
//[0,1,2]
var obj = {}
console.log(Object.keys(obj))
//[],其不会遍历原型链上的属性。复制代码
该方法兼容IE9及以上的浏览器,可是有相应的解决方法。
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对象上自身的(非继承的)全部Symbol属性键。
Object.entries()、Object.getOwnPropertyDescriptors()、Object.values()
这些方法还只是处于试验阶段的,因此不在这里进行展开。