「前端开发」 -

JS-ES6-05

ECMAScript6 —— 对象的扩展

Posted by eliochiu on November 9, 2022

对象简化表示

属性简写

ES6允许在对象中写入变量和函数作为对象的属性和方法,这样书写更简洁:

1
2
3
4
5
6
7
8
9
10
11
12
var foo = 'bar';
var baz = {foo};
baz; // {foo: 'bar'}

function(x, y) {
    return {x, y}
}

// 等价于
function(x, y) {
    return {x: x, y: y}
}

方法简写

除了属性,方法也可以进行简写:

1
2
3
4
5
6
7
8
9
10
11
12
var o = {
    method() {
        return "Hello!";
    }
};

// 等价于
var o = {
    method: function() {
        return "Hello!";
    }
};

下面是一个实际的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
var birth = '2001/05/01';

var Person = {
    name: "张三",

    // 等价于birth: '2001/05/01'
    birth,
    
    // 等价于hello: function() {...}
    hello() {
        console.log('我的名字是', this.name);
    }
}

Object.is()

ES5比较两个值是否相等,只有两个运算符:等值运算符(==)和全等运算符(===),他们都有缺点。前者会自动进行类型转换,后者的NaN不等于自身。

ES6提供了一个方法Object.is(),用来判断两个值是否相等,与全等运算符(===)类似。

1
2
Object.is('foo', 'foo'); // true
Object.is({}, {}); // false

比较特殊的是,+0不等于-0,NaN等于自身。

1
2
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true

ES5可以使用以下代码部署该方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Object.defineProperty(Object, 'is', {
    value: function(x, y) {
        if (x === y) {
            // 针对+0和-0的情况
            return x !== 0 || 1 / x === 1 / y;
        }

        // 针对NaN的情况
        return x !== x && y !== y;
    },

    configurable: true,
    writable: true,
    enumerable: false
});

Object.assign()

Object.assign方法用于将源对象的所有可枚举属性复制到目标对象中。

1
2
3
4
5
var target = {a: 1};
var s1 = {b: 2};
var s2 = {c: 3};
Object.assign(target, s1, s2);
target; // {a: 1, b: 2, c: 3};

注意:assign执行的是浅拷贝

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

Object.keys()

ES5引入了Object.keys()方法,返回一个数组,成员是参数对象自身的所有可遍历的属性(非继承)的键名。

1
2
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj); // ["foo", "baz"]

ES2017增加了一个提案,引入了Object.keys配套的Object.values(), Object.entries()作为遍历对象的一个补充手段,供for ... of循环使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
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(value); // 1, 2, 3
}

for (let [key, value] in entries(obj)) {
    console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.values()

Object.values()返回一个数组,成员是参数对象的所有可遍历的属性(非继承)的键值。

1
2
var obj = { foo: 'bar', baz: 42 };
Object.values(obj); // ['bar', 42];

Object.values()方法会自动过滤类型为Symbol的值。

1
2
3
4
5
6
var obj = {
    [Symbol()]: 123,
    foo: 'abc'
};

Object.values(obj); // ['abc']

Object.entries()

Object.entries()返回一个数组,成员是参数对象的所有可遍历的属性(非继承)的键名键值数组。

1
2
var obj = { foo: 'bar', baz: 42 };
Object.entries(obj); // [['foo', 'bar'],['baz': 42]];

如果原对象的键是Symbol类型的,那么它会被忽略。

1
2
3
4
5
6
var obj = {
    [Symbol()]: 123,
    foo: 'abc'
};

Object.entries(obj); // [['foo', 'abc']]

Object.entries()方法有很多用途,主要用于遍历对象的属性:

1
2
3
4
5
6
7
8
9
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
    console.log(
        `${JSON.stringnify(k)}: ${JSON.stringnify(v)}`
    );
}

// "one": 1
// "two": 2

该方法还可以将对象转成真正的Map结构:

1
2
3
var obj = { foo: 'bar', baz: 42 };
var map = new Map(Object.entries(obj));
map // Map{ foo: 'bar', baz: 42 }

自己实现Object.entries()

1
2
3
4
5
6
7
function entries(obj) {
    let arr = [];
    for (let key of Object.keys(obj)) {
        arr.push([key, obj[key]]);
    }
    return arr;
}

对象的扩展运算符

在数组篇,我们介绍了扩展运算符。例如:

1
2
3
const [a, ...b] = [1, 2, 3];
a // 1
b // [2, 3]

ES2017将这个运算引入到对象中。对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性分配到指定的对象上面。所有的键和它们的值都会复制到新对象上面。

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

注意:解构赋值是一个浅拷贝,如果一个键的值是复合类型的值,解构赋值得到的是这个值的引用,而不是这个值的副本。

1
2
3
4
let obj = {a: {b: 1}};
let {...x} = obj;
obj.a.b = 2;
x.a.b // 2