Object.freeze()
该方法可以冻结一个对象,冻结对象指的是不能向这个对象,添加属性、删除属性、修改属性、以及不能修改该对象已有属性的可枚举型、可写性、可配置性,该方法返回被冻结的对象。
演示:
var json = {
name:'秦司令'
};
var obj = Object.freeze(json);
obj.age = 20; // 不生效
obj.name = 'abc'; //不生效
delete obj.name // 不生效
console.log(obj.name); //秦司令
console.log(obj.age) // undefined
上面代码给大家展示了一下操作 就是防止对象的属性被修改 , 对象属性的可枚举性、可写性、可配置性、下面我们会说到。
Object.is()
Es5比较两个值是否相等,只有两个运算符 ,一个等于运算符( == ) , 一个严格等于运算符( === ) 。它们都有缺点,前者会自动转换类型 , 后者的NaN不等于自身,以及+0 等于 -0。javascript缺乏一种运算,在所有环境中,你们的值是一样的就应该相等。
Es6提出同值相等算法,用来解决这个问题。
Object.is就是部署这个算法的新方法。它用来比较俩个值是否严格相等,与严格运算符(===) 的行为基本一致。
演示:
Object.is('foo','foo') // true
Object.is({},{}) // false
上面代码中两个空对象不相等,因为它们的引用地址不同。
不同之处只有两个: 一是 +0 不等于 -0 , 二是 NaN 等于自身
// Es5写法
+0 === -0 // true
NaN === NaN //false
//Es6提出的新算法方法
Object.is(+0,-0) //false
Object.is(NaN,NaN) // true
上面代码Object.is Es6新提出的算法 让 NaN 等于自身
Object.assign()
该方法用于对象合并,将源对象(source)的所有可枚举属性,复制到target目标对象里。
演示:
var json = {a:1};
var source = {b:2};
Object.assign(json,source) // {a:1,b:2}
Object.assign方法的第一个参数是目标对象,其它的都是源对象,该方法返回值返回第一个参数。
注意,如果目标对象和源对象的属性有重复的话,那么源对象的属性覆盖目标对象的属性。
var json = {a:1,b:1}
var source = {b:2,c:3}
Object.assign(json,source) // {a:1,b:2,c:3}
如果只有一个参数,Object.assign会直接返回该参数
Object.assign({name:'秦司令'}) // {name:'秦司令'}
如果该参数不是对象,而先会转换成对象。
Object.assign(2) // Number({})
由于undefined和null无法转换成对象,所以如果它们作为参数,就会报错。
Object.assign(undefined) //报错
Object.assign(null) //报错
当然,如果undefined和null不在首参数就不会报错,因为它们不在首参数,处理方法就不同,如果无法转换成对象就会跳过。
Object.assign({},123,{name:'秦司令'}) // {name:'秦司令'}
其它类型的值 数字,布尔,字符串,不在首参数内也不会报错 。但是,除了字符串会以数组的形式,拷贝到目标对象里,其它值都不行,只有字符串可以。
Object.assign({},'str') // {0:'s',1:'t',2:'r'}
Object.assign({},123) // {}
Object.assign({},true) // {}
上面代码中,数值,布尔值,字符串 结果只有字符串被拷贝进去,这是因为只有字符串有包装类对象,会产生可枚举属性。
请看下个列子
Object(true) //{ [[PrimitiveValue]]:true }
Object(123) // { [[PrimitiveValue]]:123 }
Object('str') // {0:'s',1:'t',2:'r',length:3, [[PrimitiveValue]]:'str' }
上面代码看对象的返回值 [[PrimitiveValue]] 这个是原始属性 assign 是无法拿到的,只有字符串有可枚举属性,所以上面只能拷贝字符串。
Object.assign拷贝属性也是有限制的,只拷贝源对象的自身属性(不包含继承属性),也不拷贝不可枚举属性(enumerable:false)。
Object.assign({name:'秦司令'},
Object.defineProperty({},'age',{
enumerable:false,
value:20
})
)
上面代码中,Object.assign要拷贝这个对象属性,但是没有被拷贝进去,因为这个属性里面设置了不可枚举属性,enumerable:false。
属性名为Symbol值的也会被拷贝进去
Object.assign({a:'b'},{[Symbol()]:'c'}) // {a:'b',[Symbol()]:'c'}
注意点
浅拷贝
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性发生改变,那么Object.assign的目标对象也跟着改变。
var json = {a:{b:1}}
var obj = Object.assign({},json)
json.a.b = 2
obj.a.b // 2
上面代码发生引用的原因是,json.a的属性值是一个对象,Object.assign拷贝到的是这个对象的引用,这个对象的任何变化,都会映射到目标对象上
数组的处理
Object.assign用来处理数组,会把数组视为对象处理。
Object.assign([1,2,3],[4,5,6]) // [4,5,6]
上面代码中,Object.assign把数组视为属性名为 0 1 2的对象,因此源数组的0号属性 4 覆盖了 目标属性的 1 值 , 以此类推 最后全部覆盖。
取值函数的处理
Object.assign只能进行值的复制,如果是要复制一个取值函数,那么将求值后再复制
const source = {
get foo(){
return 1
}
}
const target = {};
Object.assign(target,source) // {foo:1}
上面代码中,source对象的foo属性是一个取值函数,Object.assign不会复制这个取值函数,只会拿到值以后,将这个值复制过去
常见用途
Object.assign方法有很多好处。
一、为对象添加属性
class Point{
constructor(x,y){
Object.assign(this,{x,y})
}
}
new Point(1,2)
上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例。
二、给对象添加方法
function Fn(){}
Object.assign(Fn.Prototype,{
add(){
}
})
new Fn()
上面代码使用Object.assign方法给对象添加方法,这样写法简洁表达式
三、克隆对象
function clone(obj){
return Object.assign({},obj)
}
上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。
不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值,如果想要克隆它的原型,请看下列代码。
function clone(){
var proto = Object.getPrototypeOf(obj)
return Object.assign(Object.create(proto),obj)
}
// Object.create方法是操作__proto__
四、合并多个对象
将多个对象合并到某个对象
const mer = (target,...source) => Object.assign(target,...source)
//上面的写法 相当于下面
function Demo(target,...source){
return Object.assign(target,...source)
}
Object.getOwnPropertyDescriptors()
Es5的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor) , Es2017引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
foo:123,
get bar(){
return 'aaa'
}
}
Object.getOwnPropertyDescriptors(obj)
//{ foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
上面代码中,Object.getOwnPropertyDescriptors()方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该对象的描述对象。
该方法的引入,主要是为了解决Object.assign不能正常拷贝 set属性 和 get 属性的取值函数。
const source = {
set foo(value){
console.log(value)
}
}
var obj = Object.assign({},source);
Object.getOwnPropertyDescriptor(obj,'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
上面代码中,source对象的foo属性的值是一个赋值函数,Object.assign方法将这个属性拷贝给target1对象,结果该属性的值变成了undefined,这是因为Object.assign方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
这时,Object.getOwnPropertyDescriptors()方法配合Object.defineProperties方法,就可以实现正确拷贝。
const source = {
set foo(value){
console.log(value)
}
}
var target = {}
Object.defineProperties(target,Object.getOwnPropertyDescriptors(source))
Object.getOwnPropertyDescriptor(target,'foo')
上面的代码,将属性描述对象添加到目标对象里面。
下面讲解一下Object.defineProperties该方法
var json = {
get foo(){
return 123
}
};
Object.defineProperties({},Object.getOwnPropertyDescriptors(json)) ;
//上面的写法,等同于下面的写法
Object.defineProperties({},{
foo:{
get(){
return 123;
},
set(value){
return value;
}
}
});
上面代码, json是一个对象,里面是一个取值函数 ,然后下面通过对象的属性描述方法给它添加到Object.defineProperties该方法里面。
另外,Object.getOwnPropertyDescriptors()方法的另一个用处,是配合Object.create() 方法,将对象属性克隆到一个新对象,这是浅拷贝。
var obj = {
name:'秦司令'
};
Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj));
__ proto __ 属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前,所有浏览器(包括IE11) 都部署了这个属性。
演示:
//Es5的写法
function demo(){}
var obj = {
method:function(){}
}
obj.__ proto __ = demo
new demo()
// Es6的写法
var obj = Object.create(demo)
上面代码中,Es6的写法Object.create该方法直接是设置__ proto __ 属性的
该属性没有写入Es6的正文,而是写入了附录,原因是__ proto __前后的双下划线,说明它本质上是一个内部属性,而不是一个正式对外的API,只是由于浏览器广泛支持,才被加入Es6。标准明确规定,只有浏览器必须部署这个属性,其它运行环境不一定要部署。
Object.getPrototypeOf()
该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
演示:
var obj = {}
Object.getPrototypeOf(obj)
下面是一个列子。
function Fn(){}
var f = new Fn()
Object.getPrototypeOf(f) === Fn.prototype //true
Object.setPrototypeOf(f,Object.prototype)
Object.getPrototypeOf(f) === Fn.prototype //false
如果参数不是对象,会自动转换为对象
Object.getPrototypeOf(1)
// 转换过程 Object.getPrototypeOf(Number(1))
// Number{ [[PrimitiveValue]]: 0 }
Object.getPrototypeOf('foo')
//转换过程 Object.getPrototypeOf(String('foo'))
// String{length:0,[[PrimitiveValue]]: ' ' }
Object.getPrototypeOf(1) == Number.prototype // true
Object.getPrototypeOf('foo') == String.prototype // true
如果参数是undefined和null,它们无法转换为对象,会直接报错
Object.getPrototypeOf(null) //报错
// typeError: Cannot convert null or undefined to Object
Object.getPrototypeOf(undefined) //报错
// typeError: Cannot convert null or undefined to Object
如果一个对象本身部署了__ proto __属性,该属性的值就是对象的值。
Object.getPrototypeOf({__proto__:null}) // null
Object.setPrototypeOf()
Object.setPrototypeOf方法的作用与__ proto __ 相同, 用来设置一个对象的prototype对象,返回参数对象本身。它是Es6正式推荐设置原型的对象的方法。
演示:
//格式
Object.setPrototypeOf(object,prototype)
//用法
const obj = Object.setPrototypeOf({},null) //设置这对象没有原型
//该方法等同于下面的函数
function setPrototypeOf(obj,proto1){
obj.__proto__ = proto1
return obj
}
下面是一个例子
let proto = {}
let obj = {name:'秦司令'}
Object.setPrototypeOf(obj,proto)
proto.x = 10;
proto.y = 20;
obj.name // 秦司令
obj.x // 10
obj.y // 20
上面代码将proto对象设为obj的原型,所以从obj对象可以读取proto对象的原型属性。
如果第一个参数不是对象,会自动转换为对象,但是由于返回的还是第一个参数,这个操作不会产生任何效果。
Object.setPrototypeOf(1,{}) === 1
Object.setPrototyoeOf('name' , {}) == 'name'
Object.setPrototypeOf(true,{}) == true
由于undefined和null无法转换为对象,所以第一个参数是undefined或null的话,会直接报错。
Object.setPrototypeOf(undefined,{}) //报错
Object.setPrototypeOf(null,{}) //报错
Object.keys()
Es5引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不包含继承的) 所有可遍历(enumerable)的属性键名。
var obj = {name:'秦司令',age:20};
Object.keys(obj) //["name","age"]
Es2017引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for....of循环使用
var obj = {a:1,b:2,c:3};
for(let key of Object.keys(obj)){
console.log(key) // a b c
}
for(let values of Object.values(obj)){
console.log(values) // 1 2 3
}
for(let [k,v] of Object.entries(obj)){
console.log([k,v]) // [name,'秦司令',age:20]
}
Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
var obj = {name:'秦司令',age:20};
Object.values(obj) // ["秦司令",20]
返回数组的成员顺序
var obj = {100:'a',2:'b',18:'c'}
Object.values(obj) // [b,c,d]
上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b,c,d
Object.values只返回自身可遍历属性
var json = Object.create({},{p:{value:123}})
Object.values(json) //[]
上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性,只要把enumerable改成true,Object.values就返回这个属性p值
var obj = Object.create({},{p:{
value:2,
enumerable:true
}})
Object.values(obj) //[2]
Object.values会过滤属性名为Symbol值的属性
Object.values({[Symbol()]:123,name:'秦司令'})
// ['秦司令']
如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('fooa')
// ['f','0','0','a']
上面代码中,字符串会先转换成一个类似数组的对象,字符串的每个字符,就是该对象的一个属性,因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组
如果参数不是对象,Object.values会先将其转换为对象。由于数值和布尔值的包装类对象,都不会为实例添加非继承的属性,所以,Object.valus会返回空数组
Object.values(43) //[]
Object.values(true) // []
Object.entries()
Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
var obj = {foo:'bar',baz:43}
Object.entries(obj)
// [["foo","bar"],["baz",43]]
如果原对象的属性名是一个Symbol值,该属性会被忽略
Object.entries({[Symbol()]:123,foo:'abc'})
//[["foo","abc"]]
Object.entries的基本用途是遍历对象的属性
let obj = {one:1,tow:2}
for(let [k,v] of Object.entries(obj)){
console.log(`${JSON.stringify(k)}:${JSON.stringify(v)}`)
}
// "one":1
// "tow":2
Object.entries另一用处是将对象转换成Map结构
var obj = {foo:'bar',name:'秦司令'}
var map = new Map(Object.entries(obj))
// Map{foo:'bar',name:'秦司令'}