ES6 对象的扩展:属性名和方法简写、对象新增方法、super关键字

参考资料:阮一峰老师的es6

目录

  • 属性和方法的简写
  • 对象的新增方法
    • (1)Object.is()
    • (2)Object.assign()
    • (3)Object.getOwnPropertyDescriptors()
    • (4)proto属性
    • (5)Object.setPrototypeOf(),Object.getPrototypeOf()
    • (6)Object.keys(),Object.values(),Object.entries()
    • (7)Object.fromEntries()
  • 属性的可枚举性和遍历
    • 可枚举性
    • 属性的遍历
  • super关键字
  • 对象的扩展运算符
  • 链判断运算符和Null判断运算符


属性和方法的简写

eg:

let obj = {
  name: 'nan',
  age, // 等同于 age: age
  say () {
    console.log('Hello')
  }
}

对象的新增方法

  • Object.is(): 比较两个值是否严格相等
  • Object.assign(): 用于对象的合并,将源对象的所有可枚举属性复制到目标对象上
  • Object.getOwnPropertyDescriptors(): 返回指定对象所有自身属性(非继承属性)的描述对象
  • __proto__属性,Object.setPrototypeOf()Object.getPrototypeOf(): 用来读取 | 设置当前对象的原型对象
  • Object.keys()Object.values()Object.entries(): 返回一个数组,成员是由参数对象自身的(不含继承)所有可遍历属性的键名 | 键值 | 键值对数组
  • Object.fromEntries(): 是Object.entries()的逆操作,用于将一个键值对数组转为对象。

(1)Object.is()

Object.is()用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

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

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

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

可以理解为它是把要比较的两个值转化为字符串来进行对比的。


(2)Object.assign()

Object.assign()方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象上,如果对象里属性名相同,会被覆盖。

Object.assign(目标对象, 源对象1, 源对象2...)

可以理解成将多个对象合并为一个新的对象。

const target = { a: 1 };

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

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

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

Object.assign常见用途主要有以下几种:

(1)为对象添加属性

class People {
  constructor(name, age) {
    Object.assign(this, {name, age});
  }
}

上面方法通过Object.assign方法,将name属性和age属性添加到People类的对象实例。

(2)为对象添加方法

Object.assign(People.prototype, {
  say (arg1, arg2) {···},
  jump () {···}
});

// 等同于下面的写法
People.prototype.say = function (arg1, arg2) {···};
People.prototype.jump = function () {···}; 

上面通过Object.assign方法,给People的原型对象上添加了sayjump这两个方法。

(3)克隆对象

function clone(origin) {
  return Object.assign({}, origin);
}

(4)合并多个对象

const mergeObjs = 
  (target, ...sources) => Object.assign(target, ...sources);

如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。

const mergeObjs = 
  (...sources) => Object.assign({}, ...sources);

(3)Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象
ES6 对象的扩展:属性名和方法简写、对象新增方法、super关键字_第1张图片


(4)proto属性

__proto__属性(前后各两个下划线),用来读取或设置当前对象的原型对象(prototype)。

const obj = {...};
obj.__proto__ = obj2;

无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。


(5)Object.setPrototypeOf(),Object.getPrototypeOf()

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。

let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);

上面代码将proto对象设为obj对象的原型。

Object.getPrototypeOf方法用于读取一个对象的原型对象。

function People() {
  // ...
}

const people = new People();

Object.getPrototypeOf(people) === People.prototype
// true

(6)Object.keys(),Object.values(),Object.entries()

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

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

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

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

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

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

(7)Object.fromEntries()

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

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

该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。


属性的可枚举性和遍历

可枚举性

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

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

描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。

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

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的所有可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

这四个操作之中,前三个是 ES5 就有的,最后一个Object.assign()是 ES6 新增的。

其中,只有for...in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理对象自身的属性。

操作中引入继承的属性会让问题复杂化,大多数时候,我们要的是对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。


属性的遍历

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

(1)for…in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

  • 首先遍历所有数值键,按照数值升序排列。
  • 其次遍历所有字符串键,按照加入时间升序排列。
  • 最后遍历所有 Symbol 键,按照加入时间升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]

上面代码中,Reflect.ownKeys方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2和10,其次是字符串属性b和a,最后是 Symbol 属性。


super关键字

this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto); // 将proto设置为obj的原型对象
obj.find() // "hello"

注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。


对象的扩展运算符

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

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

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

对象的扩展运算符等同于使用Object.assign()方法。

let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

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

let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);

链判断运算符和Null判断运算符

链判断运算符

日常开发中,如果要读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取obj.name.firstName,安全的写法是写成下面这样。

const firstName = (obj
  && obj.name
  && obj.name.firstName || 'default';

这样的层层判断非常麻烦,因此 ES2020 引入了链判断运算符(?.),简化上面的写法。

const firstName = obj ?. name ?. firstName || 'default';

上面代码使用了?.运算符,直接在链式调用的时候判断,左侧的对象是否为nullundefined。如果是的,就不再往下运算,而是返回undefined

下面是判断对象方法是否存在,如果存在就立即执行的例子。

obj.say?.()

上面代码中,obj.say如果有定义,就会调用该方法,否则直接返回undefined

Null 判断运算符

ES2020 引入了一个新的 Null 判断运算符(??)。它的行为类似||,但是只有运算符左侧的值为nullundefined时,才会返回右侧的值。

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

上面代码中,默认值只有在属性值为nullundefined时,才会生效。

你可能感兴趣的:(ES6)