目录
1.1.4Object.fromEntries(iterable);
1.2.5Object.preventExtensions(obj)
1.3.1Object.assign(target,...source)
1.3.2Object.create(prop,propertiesObject
前言:了解对象和数组的一些常用方法和属性,在我们日常开发中显得尤为重要,而且在面试中也会让您与同龄人脱颖而出,所以在了解了数组的属性和方法之后,现在特来此出一篇对象的属性和方法。
对象其实没有什么属性,就是一个constructor,Object.prototype.constructor,返回属性Object的构造函数。所以在这一节我们来探讨一下对象的方法。
1.静态属性
1.1"迭代器"方法
注意对象没有提供可迭代的接口,数组提供了可迭代的接口,所以我们这里类比数组,也将其叫做对象的可迭代方法。
1.1.1Object.keys(obj)
- 参数:要返回其枚举自身属性的对象。
- 返回值:一个表示给定对象自身的可枚举的非symbol属性的字符串数组。
for in也是可以遍历自己的和非自己的(原型链上的)可枚举的非symbol属性的属性
1.1.2Object.values(obj)
- 参数:被返回可枚举属性值的对象。
- 返回值:一个包含对象自身的可枚举的非symbol属性的属性值的数组。
1.1.3Object.entries(obj)
- 参数:可以返回其可枚举属性的键值对的对象。
- 返回值:给定对象自身的可枚举的非symbol属性的键值对数组。
共同点:
参数都接受一个对象,返回值都是数组,只是一个返回对象的属性的字符串,一个返回对象属性的值,一个返回对象键值对。
- 自身的也就是的属性是不会被获取的
- 可枚举的也就是属性enumerable为true的属性
- 非symbol属性也就是属性为Symbol标记的属性名
可以通过map将返回的值转化为键值对数据。所以通过这种方式可以将一个对象转化为一个map类型的数据。现用entries将对象转化为普通键值对列表,再用map将普通键值对转化为map类型的键值对列表。
测试代码如下:
let obj={
name:"cx",
age:18,
gender:"male",
[Symbol()]:"湖北"
}
Object.defineProperty(obj,'gender',{
enumerable:false
})
Object.getOwnPropertyDescriptor(obj,'gender')
console.log(obj)
console.log(Object.keys(obj))
console.log(Object.values(obj))
console.log(Object.entries(obj))
测试代码结果截图如下:
重写entries代码
obj={
a:1,
b:2
}
Object.myEntries=function(o){
let _pool=[]
if(Object.prototype.toString.call(o)==='[object Object]'){
for(let i in o){
if(o.hasOwnProperty(i)){
let _arr=[i,o[i]]
_pool.push(_arr)
}
}
}
return _pool
}
console.log(Object.myEntries(obj))//[['a',1],['b',2]]
只对Object对象起作用,其他的都返回一维空数组。
1.1.4Object.fromEntries(iterable);
- 参数:map或者array或其他键值对列表
- 返回值:返回一个带有这些键值对的新对象。(所以一个对象经过entries和fromEntries回来之后,和原来的对象是不一样的)
let obj={
name:"cx",
age:18,
gender:"male",
[Symbol()]:"湖北"
}
Object.defineProperty(obj,'gender',{
enumerable:false
})
//array转换为object
console.log(Object.fromEntries(Object.entries(obj)))//{name: 'cx', age: 18}
//map转换为object
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
Object.fromEntries()
执行与 Object.entries 互逆的操作。
重写fromEntries代码
Object.myFromEntries=function(o){
let _obj={}
for(let i of o){
_obj[i[0]]=i[1]
}
return _obj
}
console.log(Object.myFromEntries([[1,2]]))
for in可以用来遍历数组下标也可以用来遍历对象属性,但是for of只能用来遍历数组属性值,而不能用来遍历对象
1.2对象的冻结,封闭和不可扩展方法
1.2.1Object.freeze(obj)
参数:要被冻结的对象。
返回值:被冻结的对象。
传入的对象和返回的对象是全等关系,也就是说传入的对象就是返回的对象。
冻结对象的特点:
一个对象是冻结的是指它不可扩展,所有的非writable数据属性都是不可配置的,且不可再变回可配置。
- 对象的数据属性不能修改,包括value,writable,enumerable,configurable。
- 不能增加属性,不能删除已有属性
- 对象的原型也不能修改,但是原型里面的属性可以增删改(浅冻结)。
- 也可以用来冻结数组
- 这里的冻结是浅冻结,也就说保证每个属性的引用数据类型不变就可以了。如果想要实现深冻结,我们可以使用一个递归函数进行深冻结。
一个冻结对象是一个封闭对象更是一个不可扩展对象。
1.2.2Object.isFrozen(obj)
参数:被检测的对象。
返回值:表示给定对象是否被冻结(Boolean值)
1.2.3Object.seal(obj)
参数:将要变得封闭的对象。
返回值:已经封闭的对象。
传入的对象和返回的对象是全等关系,也就是说传入的对象就是返回的对象。
一个对象是冻结的是指它不可扩展,所有的非writable和非value数据属性都是不可配置的,且不可再变回可配置。
- 对象的数据属性不能修改,包括enumerable,configurable。
- 不能增加属性,不能删除已有属性
- 对象的原型可以修改,里面也是任意增删改(浅封闭)
- 也可以用来冻结数组
- 这里的封闭是浅封闭,也就是说保证以前的每个属性都在(不删除任何属性),且不增加任何属性,但是对象数据类型里面的增删改还是照样进行。如果想要实现深封闭,我们可以使用一个递归函数进行深封闭。
一个封闭对象是一个不可扩展对象。
1.2.4Object.isSealed(obj)
参数:要被检查的对象。
返回值:表示给定对象是否被密封的一个Boolean 。
1.2.5Object.preventExtensions(obj)
参数:将要变得封闭的对象。
返回值:已经封闭的对象。
传入的对象和返回的对象是全等关系,也就是说传入的对象就是返回的对象。
让一个对象变的不可扩展,也就是永远不能再添加新的属性。
- 对象的数据属性可以修改
- 不能增加属性,可以删除已有属性
- 对象的原型可以修改,里面也是任意增删改(浅不可扩展)
- 也可以用来不可扩展数组
- 这里的不可扩展是浅不可扩展,不增加任何属性,但是对象数据类型里面的增删改还是照样进行。如果想要实现深封闭,我们可以使用一个递归函数进行深不可扩展。
1.2.6Object.isExtensible(obj)
参数:要被检查的对象。
返回值:表示给定对象是否可以扩展的一个Boolean 。
对象的冻结封闭和不可扩展总结:
冻结(freeze):不可增 不可删 不可改
封闭(seal):不可增 不可删 可改
不可扩展(preventExtensions):不可增 可删 可改
且都是浅冻结,浅封闭,浅不可扩展(对于引用数据类型来说,冻结只要保证不整个修改引用数据类型就好,而封闭和不可扩展则不要求,即使修改也可以,而不可扩展则在上面的情况下,还可以进行删除)
在ES6中接受一个非对象也是可以个,接受什么就返回什么,这在以前的ES5中是要报错的。
1.3对象的创建和合并
1.3.1Object.assign(target,...source)
参数:
target
目标对象,接收源对象属性的对象,也是修改后的返回值。sources
源对象,包含将被合并的属性。
返回值:目标对象。
Object.assign()
方法将所有可枚举(Object.propertyIsEnumerable()
返回真)和自有(Object.hasOwnProperty()
返回真)属性从一个或多个源对象复制到目标对象,返回处理后的目标对象(所以目标对象和返回的对象是一个引用)。
底层就是调用sources的getter,然后调用target的setter。
而且target只会管对象的键值对,不会管对象的属性选择器,也就是后面源对象的属性选择器的内容是分配不到target对象里面的。但是如果前面一个对象属性选择器不会被后面的覆盖,所以后面的属性如果要给前面非可写的属性重新赋值的时候,就会报错。
面试题:如果传入的源对象不是对象的话,则要经过包装类型的包装。
let v1=123
let v2="123"
let v3=true
let v4=function test(){}
//因为source类型是对象,所以在这先要将基本类型转换为对象
let r1=new Number(v1)
let r2=new String(v2)
let r3=new Boolean(v3)
let r4=new Function(v4)
console.log(r1,r2,r3,r4)
// 通过结果可以看出只有r2可以枚举或者是通过for in也可以看出来
console.log(Object.assign({},r1,r2,r3,r4))//{0:"1",1:"2",2:"3"}
那么我们想一下我们怎么讲描述符也拷贝进去呢,看下面的代码,为什么这个getter没有拷贝进去,拷贝进去的仅仅是执行后的值。
let source={
age:15,
get getAge(){
return 89
}
}
let target=Object.assign({},source)
console.log(target)//{age: 15,getAge: 89}
1.3.2Object.create(prop,propertiesObject)
参数:
proto
新创建对象的原型对象。- propertiesObject通过属性描述器对象来描述一个属性,descriptor,value,writable,configurable,enumerable,通过属性描述器来定义属性的时候,其他的三个属性都默认是false,而通过字面量和new的方式创建的对象这三个属性都是true。第二个参数的写法和Object.getOwnPropertyDescriptors(obj)返回的对象是一样的。
返回值:返回一个新对象
Object.create()
方法创建一个新对象,使用现有的对象作为新创建对象的原型(原型)。
const ages = Object.create(null, {
alice: { value: 18, enumerable: true },
bob: { value: 27, enumerable: true },
});
非常感谢您的阅读,欢迎大家提出您的意见,指出相关错误,谢谢!越努力,越幸运!