【重温基础】12.使用对象 下

简介: 【重温基础】12.使用对象 下

5.ES8新增:Object.values(),Object.entries()

ES7中新增加的 Object.values()Object.entries()与之前的Object.keys()类似,返回数组类型。

回顾下Object.keys()

var a = { f1: 'hi', f2: 'leo'};
Object.keys(a); // ['f1', 'f2']

5.1 Object.values()

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

let a = { f1: 'hi', f2: 'leo'};
Object.values(a); // ['hi', 'leo']

如果参数不是对象,则返回空数组:

Object.values(10);   // []
Object.values(true); // []

5.2 Object.entries()

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

let a = { f1: 'hi', f2: 'leo'};
Object.entries(a); // [['f1','hi'], ['f2', 'leo']]
  • 用途1
    遍历对象属性。
let a = { f1: 'hi', f2: 'leo'};
for (let [k, v] of Object.entries(a)){
    console.log(
        `${JSON.stringfy(k)}:${JSON.stringfy(v)}`
    )
}
// 'f1':'hi'
// 'f2':'leo'
  • 用途2: 将对象转为真正的Map结构。
let a = { f1: 'hi', f2: 'leo'};
let map = new Map(Object.entries(a));

手动实现Object.entries()方法:

// Generator函数实现:  
function* entries(obj){
    for (let k of Object.keys(obj)){
        yield [k ,obj[k]];
    }
}
// 非Generator函数实现:
function entries (obj){
    let arr = [];
    for(let k of Object.keys(obj)){
        arr.push([k, obj[k]]);
    }
    return arr;
}

6.ES8新增:Object.getOwnPropertyDescriptors()

之前有Object.getOwnPropertyDescriptor方法会返回某个对象属性的描述对象,新增的Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象

let a = {
    a1:1,
    get f1(){ return 100}
}
Object.getOwnPropetyDescriptors(a);
/*
{ 
    a:{ configurable:true, enumerable:true, value:1, writeable:true}
    f1:{ configurable:true, enumerable:true, get:f, set:undefined}
}
*/

实现原理:

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

引入这个方法,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。

let a = {
    set f(v){
        console.log(v)
    }
}
let b = {};
Object.assign(b, a);
Object.a(b, 'f');
/*
f = {
    configurable: true,
    enumable: true,
    value: undefined,
    writeable: true
}
*/

valueundefined是因为Object.assign方法不会拷贝其中的getset方法,使用getOwnPropertyDescriptors配合Object.defineProperties方法来实现正确的拷贝:

let a = {
    set f(v){
        console.log(v)
    }
}
let b = {};
Object.defineProperties(b, Object.getOwnPropertyDescriptors(a));
Object.getOwnPropertyDescriptor(b, 'f')
/*
    configurable: true,
    enumable: true,
    get: undefined,
    set: function(){...}
*/

Object.getOwnPropertyDescriptors方法的配合Object.create方法,将对象属性克隆到一个新对象,实现浅拷贝。

const clone = Object.create(Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj));
// 或者
const shallowClone = (obj) => Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

7.ES9新增:对象的拓展运算符

7.1 介绍

对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:

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

对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是undefinednull就会报错无法转成对象。

let {a, ...b} = null;      // 运行时报错
let {a, ...b} = undefined; // 运行时报错

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

let {...a, b, c} = obj;     // 语法错误
let {a, ...b, c} = obj;     // 语法错误

注意

  • 1.解构赋值是浅拷贝。
let a = {a1: {a2: 'leo'}};
let {...b} = a;
a.a1.a2 = 'leo';
b.a1.a2 = 'leo';
  • 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3;    // { b: 2 }
o3.a;  // undefined

7.2 使用场景

  • 1.取出参数对象所有可遍历属性,拷贝到当前对象中。
let a = { a1:1, a2:2 };
let b = { ...a };
b;   // { a1:1, a2:2 }
// 类似Object.assign方法
  • 2.合并两个对象。
let a = { a1:1, a2:2 };
let b = { b1:11, b2:22 };
let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22}
// 等同于
let ab = Object.assign({}, a, b);
  • 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。
let a = { a1:1, a2:2, a3:3 };
let r = { ...a, a3:666 };   
// r {a1: 1, a2: 2, a3: 666}
// 等同于
let r = { ...a, ...{ a3:666 }};
// r {a1: 1, a2: 2, a3: 666}
// 等同于
let r = Object.assign({}, a, { a3:666 });
// r {a1: 1, a2: 2, a3: 666}
  • 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。
let a = { a1:1, a2:2 };
let r = { a3:666, ...a };
// r {a3: 666, a1: 1, a2: 2}
// 等同于
let r = Object.assign({}, {a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
// 等同于
let r = Object.assign({a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
  • 5.拓展运算符后面可以使用表达式。
let a = {
    ...(x>1? {a:!:{}),
    b:2
}
  • 6.拓展运算符后面如果是个空对象,则没有任何效果。
{...{}, a:1};  // {a:1}
  • 7.若参数是nullundefined则忽略且不报错。
let a = { ...null, ...undefined }; // 不报错
  • 8.若有取值函数get则会执行。
// 不会打印 因为f属性只是定义 而不没执行
let a = {
    ...a1,
    get f(){console.log(1)}
}
// 会打印 因为f执行了
let a = {
    ...a1,
    ...{
        get f(){console.log(1)}
    }
}


目录
相关文章
|
6月前
|
编译器 C++
c++primer plus 6 读书笔记 第十章 对象和类
c++primer plus 6 读书笔记 第十章 对象和类
|
7月前
|
Python
继承概念深度解析:代码视角下的科普之旅
继承概念深度解析:代码视角下的科普之旅
32 0
|
编译器 C++
【C++从0到王者】第二十三站:多态的概念、定义以及实现
【C++从0到王者】第二十三站:多态的概念、定义以及实现
57 1
|
7月前
|
设计模式
二十三种设计模式全面解析-深入探讨状态模式的高级应用技术:释放对象行为的无限可能
二十三种设计模式全面解析-深入探讨状态模式的高级应用技术:释放对象行为的无限可能
|
7月前
|
设计模式 Java 数据库
二十三种设计模式全面解析-单例设计模式:解密全局独一无二的实例创造者
二十三种设计模式全面解析-单例设计模式:解密全局独一无二的实例创造者
|
存储 安全 Java
基础一:一切都是对象
基础一:一切都是对象
105 0
|
编译器 C++
爱上c++的第三天(核心课程):类和对象--对象特性
初始化数据,主要作用于创建对象时为对象的成员属性赋值,构造函数由系统自动调用,无需手动调用。
96 0
爱上c++的第三天(核心课程):类和对象--对象特性
|
JavaScript 前端开发
【重温基础】12.使用对象 上
【重温基础】12.使用对象 上
154 0
|
存储 Java
Java面向对象基础(二)
Java面向对象基础(二)
174 0
Java面向对象基础(二)
|
JavaScript 安全 前端开发
【重温基础】4.函数
【重温基础】4.函数
112 0