《六》ES6中对象的扩展

属性名表达式:

JavaScript定义对象的属性,有两种方法:一是直接用标识符作为属性名,二是用表达式作为属性名,这时要将表达式放在方括号之内。

// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;

但是,如果使用字面量方式定义对象,在ES5中只能使用方法一定义属性。

var obj = {
  foo: true,
  abc: 123
};

ES6允许字面量定义对象时,用方法二作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

属性的简洁表示法:

ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

let name = 'Lee'
const Person = {
  name,
  hello() { console.log('我的名字是', this.name); }
};

// 等同于
const Person = {
  name : 'Lee',
  hello : fucntion() { console.log('我的名字是', this.name); }
};

属性的可枚举性和遍历:

属性的可枚举性:

对象的每个属性都有一个描述对象Descriptor,用来控制该属性的行为。Object.getOwnPropertyDescriptor()方法可以获取该属性的描述对象。描述对象的enumerable属性,称为"可枚举性",如果该属性为false,就表示某些操作会忽略当前属性。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

目前,有四个操作会忽略enumerable为false的属性:

  1. for…in循环:只遍历对象自身的和继承的可枚举的属性。
  2. Object.keys():返回对象自身的所有可枚举的属性的键名。
  3. JSON.stringify():只串化对象自身的可枚举的属性。
  4. Object.assign():忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
属性的遍历:

ES6一共有5种方法可以遍历对象的属性:

  1. for…in:for…in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
  2. Object.keys():返回一个数组,包括对象自身的所有可枚举属性的键名(不含Symbol属性)。
  3. Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有可枚举属性的键名(不含Symbol属性)。
  4. Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有Symbol属性的键名。
  5. Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有键名,不管键名是Symbol还是字符串,也不关是否可枚举。

以上5种方法遍历对象的键名,都遵守同样的属性遍历的次序规则:首先遍历所有的数值键,按照数值升序排列;其次遍历所有字符串键,按照加入时间升序排列;最后遍历所有Symbol键,按照加入时间升序排列。

Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]

对象的扩展运算符:

对象的扩展运算符用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

对象的扩展运算符等同于使用Object.assign()方法,也是浅拷贝。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

如果对象的扩展运算符后面是一个空对象,则没有任何效果。

{...{}, a: 1}
// { a: 1 }

如果对象的扩展运算符后面不是对象,则会自动将其转换为对象。如果对象的扩展运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象。如果对象的扩展运算符后面是数值、布尔值、null、undefined,由于转成的对象没有自身属性,所以返回一个空对象。

{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

// 等同于 {...Object(1)}
{...1} // {}

// 等同于 {...Object(true)}
{...true} // {}

// 等同于 {...Object(undefined)}
{...undefined} // {}

// 等同于 {...Object(null)}
{...null} // {}

由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

链判断运算符:

如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取message.body.user.firstName,安全的写法是写成下面这样:

const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';

这样的层层判断非常麻烦,因此ES2020引入了链判断运算符?.,直接在链式调用的时候判断左侧的对象是否为null或undefined,如果是的,就不再往下运算,而是返回undefined。

const firstName = message?.body?.user?.firstName || 'default';

Null判断运算符:

读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值,常见的做法是通过||运算符指定默认值。

const headerText = response.settings.headerText || 'Hello, world!';

但是这样写是错的。开发者的原意是,只要属性的值为null或undefined,默认值就会生效,但是属性的值如果为空字符串或false或0,默认值也会生效。

为了避免这种情况,ES2020引入了一个新的Null判断运算符??,它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';

Object.is():

ES5比较两个值是否相等,只有两个运算符:相等运算符和全等运算符。它们都有缺点,前者会自动转换数据类型;后者的NaN不等于自身,以及+0等于-0。

ES6的Object.is()方法用来比较两个值是否严格相等,与全等运算符的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

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

+0 === -0 //true
NaN === NaN // false

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

Object.assign():

Object.assign()方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。如果只有一个参数,Object.assign()方法会直接返回该参数。

Object.assign()方法实行的是浅拷贝,而不是深拷贝。
Object.assign()方法拷贝的属性是有限制的,只拷贝源对象的自身可枚举属性,不拷贝继承属性,也不拷贝不可枚举属性。

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

如果第一个参数不是对象,则会先转成对象,再返回。由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。这是因为只有字符串的包装对象会产生可枚举属性。

typeof Object.assign(2) // "object"
Object.assign(undefined) // 报错
Object.assign(null) // 报错

如果非对象参数出现在源对象的位置,那么只有字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。

const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.getOwnPropertyDescriptors():

ES2017引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。Object.getOwnPropertyDescriptors()方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。

const obj = {
  foo: 123,
  get  bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

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

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

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

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

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

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

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

Object.fromEntries():

Object.fromEntries()方法是Object.entries()方法的逆操作,用于将一个键值对数组转为对象。

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

猜你喜欢

转载自blog.csdn.net/wsln_123456/article/details/106418953
今日推荐