我是靠谱客的博主 爱笑母鸡,最近开发中收集的这篇文章主要介绍重学ES6 对象的扩展(2),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

__proto__属性、 Object.setPrototypeOf()

__proto__属性

proto 用来设置或者读取当前对象的prototype对象。

该属性没有写入ES6正文,代码最好认为这个属性是不存在的。无论从语义还是兼容性,都不要使用这个属性。而是使用Object.setPrototypeOf()(写操作),Object.getPrototypeOf()(读操作)或者Object.create()(生成操作)代替。

Object.setPrototypeOf()

Object.setPrototypeOf() 用来设置一个对象的 prototype 对象,返回参数对象本身。

// 格式
Object.setPrototypeOf(object,prototype)
复制代码

例子

let proto = {};
let obj = {x:10}
Object.setPrototypeOf(obj,proto);
proto.y = 20;
proto.z = 40;
obj.x //10
obj.y //20
obj.z //40
复制代码

上述代码将 proto 设置为 obj 的原型,所以,obj可以读取 proto的属性。

如果第一个参数不是对象,那么会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何作用。

Object.setPrototypeOf(1,{}) === 1 //true
Object.setPrototypeOf('foo',{}) === 'foo' //true
Object.setPrototypeOf(true,{}) === true //true
复制代码

由于 undefined 和 null 无法转化为对象,所以第一个参数是 undefined 或者 null 就会报错。

Object.getPrototypeOf()

读取一个对象的 prototype 对象。

例子

function Rectangle(){
...
}
var rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype //true
复制代码

Object.keys()、Object.values()、Object.entries()

Object.keys()

返回一个数组,成员时参数对象自身的(不含继承的)所有可遍历属性的键名。

var obj = {
foo:'bar',
baz:10
}
Object.keys(obj); //["foo","baz"]
复制代码

ES2017引入了与Object.keys配套的Object.values,Object.entries

let {keys,values,entries}
= Object;
let obj = {
a:1,
b:2,
c:3
}
for(let key of keys(obj)){
console.log(key); // 'a','b','c'
}
for(let value of values(obj)){
console.log(key); // 1,2,3
}
for(let [key,value] of entries(obj)){
console.log([key,value]); // ['a':1],['b':2],['c':3]
}
复制代码

Object.values()

方法返回一个数组,成员是参数自身的(不含继承的)所有可遍历属性的键值。

var obj = {
foo:'bar',
baz:10
}
Object.values(obj);// ["bar",10]
复制代码

返回数组的成员顺序

var obj = {
100:'a',
2:'b',
7:'c'
}
Object.values(obj); // ["b","c","a"]
复制代码

上述代码,属性名为数值的属性,按照数字从小到大遍历。

Object.entries()

方法返回一个数组,成员是参数自身的(不含继承的)所有可遍历属性的键值对数组。

var obj = {
foo:'bar',
baz:10
}
Object.entries(obj); //[["foo","bar"],["baz",42]]
复制代码

如果源对象属性名是一个Symbol值,该属性被忽略。

Object.entries另外一个用处是将对象转化成真正的Map结构。

var obj = {
foo:'bar',
baz:10
}
var map = new Map(Object.entries(obj));
map //Map {foo:"bar",baz:42}
复制代码

自己实现Object.entries()

// Generator
function* entries(obj){
for(let key of Object.keys(obj)){
yield [key,obj[key]];
}
}
//普通
function entries(obj){
let arr = [];
for(let key of Object.keys(obj)){
arr.push([key,obj[key]])
}
return arr
}
复制代码

对象的扩展运算符

数组扩展运算符

const [a,...b] = [1,2,3];
a // 1
b // [2,3]
复制代码

ES2017将这个运算符引入了对象。

解构赋值

对象解构赋值相当于 从一个对象取值,相当于将 所有可遍历的,但尚未被读取的属性分配到指定对象上面。所有的键和值都会被复制到新的对象上面。

let {x,y,...z} = {x:1,y:2,a:3,b:4};
x //1
y //2
z // {a:3,b:4}
复制代码

上述代码,变量z是解构赋值所在的对象。它获取等号右边所有尚未读取的键,将他们的值一起复制过来。

由于解构赋值要求等号右边是一个对象,所以右边是undefined或者null就会报错。因为它们无法转为对象。

解构赋值必须是最后一个参数,否则会报错。

解构赋值是浅复制。

let obj = {
a: {
b:1
}
}
let {...x} = obj;
obj.a.b = 2;
x.a.b //2
复制代码

上述代码,x是解构赋值所在对象,复制了 obj 的 a 属性,a属性引用了一个对象,修改这个对象会影响解构赋值对它的引用。

另外,解构赋值不会复制继承自原型对象的属性。

let o1 = {
a:1
}
let o2 = {
b:2
}
o2._proto_ = o1
let {...o3} = o2
o3 //{b:2}
o3.a // undefined
复制代码
var o = Object.create({x:1,y:2})
o.z = 3
let {x,...{y,z}} = o
x // 1
y // undefined
z // 3
复制代码

x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y是双重解构赋值,只能读取对象o 自身属性,只有变量z可以赋值成功!

扩展运算符

(...)用于取出参数对象的所有可遍历属性,将其复制到当前对象之中。

let z = {
a:3,
b:4
}
let n = {...z}
n // {a:3,b:4}
//等同于使用
let aclone = {...a}
//等同
let aclone = Object.assign({},a)
复制代码

上述方法只是复制了对象实例属性,如果想完整克隆一个对象,还要复制对象原型的属性。

// 方法1 兼容性差
const clone1 = {
__proto__:Object.getPrototypeOf(obj),
...obj
}
//方法2
推荐
const clone2 = Object.assign(
Object.create(Object.getPropertyOf(obj)),
obj
)
复制代码

扩展运算符可用于合并两个对象

let ab = {...a,...b}
//等同
let ab = Object.assign({},a,b)
复制代码

修改现有对象部分属性

let newVersion = {
...previousVersion,
name: 'New Name'
}
复制代码

如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。

let aWidthDefaults = {x:1,y:2,...a}
// equals
let aWtithDefaults = Object.assign({},{x:1,y:2},a);
// equals
let aWtithDefaults = Object.assign({x:1,y:2},a);
复制代码

对象扩展运算符可以带有表达式

const obj = {
...(x > 1 ? {a:1} : {}),
b:2
}
复制代码

Object.getOwnPropertyDescriptors()

用于返回对象属性的描述对象。

var obj = {p:'a'};
Object.getOwnPropertyDescriptors(obj,'p');
//Object {
//
value:"a",
//
writable:true,
//
enumerable:true,
//
configurable:true
//}
Object.getOwnPropertyDescriptors(obj);
//Object {
//
p:{
//
value:"a",
//
writable:true,
//
enumerable:true,
//
configurable:true
//
}
//}
复制代码

该方法的实现

function getOwnPropertyDescriptors(obj){
const result = {}
for(let key of Reflect.ownKeys(obj)){
result[key] = Object.getOwnPropertyDescriptor(obj,key)
}
return result
}
复制代码

Object.getOwnPropertyDescriptors 另一个用处是,配合 Object.create 方法将对象属性克隆到一个新的对象,属于浅复制

// Object.create
第一个参数 是 继承原型 ,第二个参数 是 对象属性描述
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj))
复制代码

Object.getOwnPropertyDescriptors还可以实现一个对象继承另一个对象。

以前,通常是这么写:

const obj = {
__proto__:prot,
foo:123
}
复制代码

__prot__只需要部署浏览器,无需部署其他环境。

如果去掉__proto__,代码就这样写

const obj = Object.create(prot);
obj.foo = 123;
// or
const obj = Object.assign(
Object.create(prot),
{
foo:123
}
)
复制代码

有了 Object.getOwnPropertyDescriptors 就可以这样写

const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({
foo:123
})
)
复制代码

Null 传导运算符

我们在读取对象内部某个属性,往往需要判断该对象是否存在。 比如要读取 message.body.user.firstname

const firstname = (message && message.body &&
message.body.user && message.body.user.firstname) || 'default'
复制代码

这样判断实在是太麻烦了 简化写法:

const firstname = message?.body?.user?.firstname || 'default'
复制代码

3个 ?.运算符,只要其中一个返回null或undefined,就不再继续运算,而是返回 undefined。

转载于:https://juejin.im/post/5cdac0c3e51d4547587bf6d1

最后

以上就是爱笑母鸡为你收集整理的重学ES6 对象的扩展(2)的全部内容,希望文章能够帮你解决重学ES6 对象的扩展(2)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(57)

评论列表共有 0 条评论

立即
投稿
返回
顶部