ES6类型扩展-对象扩展

对象类别

一直以来对象的术语描述没有统一的标准,于是ES6规范定义了每一个类别的对象,对象的类别如下:

1、普通(Ordinary)对象
具有JS对象所有的默认内部行为

2、特异(Exotic)对象
具有某些与默认行为不符的内部行为

3、标准(Standard)对象
ES6规范中定义的对象,例如,Array、Date等。标准对象既可以是普通对象,也可以是特异对象

4、内建对象
脚本开始执行时存在于JS执行环境中的对象,所有标准对象都是内建对象

对象简写

属性简写

当一个对象的属性与本地变量同名时,只写属性名即可

let name = 'wmui', age = 10
let obj = {name,age}

// 等价于 
let obj = {name: "wmui", age: 10}

方法简写

对象的方法也可以简写,消除了冒号和function关键字

let obj = {
  name: 'wmui',
  sayName() {
   return this.name
  }
}

// 等价于
let obj = {
  name: 'wmui',
  sayName: function() {
   return this.name
  }
}

两者的唯一区别是,简写方法可以使用super关键字,普通方法不可以

可计算属性名

在ES5中是无法为对象字面量定义可计算属性名的,比如说属性名是一个变量。但在ES6中可以通过为属性名加一对方括号来定义可计算属性名。

let suffix = ' name'
let obj = {
  ['first' + suffix]: 'wang',
  ['last' + suffix]: 'li'
}

console.log(obj['first name']) // wang
console.log(obj['last name']) // li

判断相等

在JS中,判断两个值是否相等,通常会使用相等运算符或全等运算符,但是有时候即使使用全等运算符也不一定完全靠得住

console.log(+0 === -0);//true
console.log(NaN === NaN);//false

ES6引入了Object.is()方法来弥补全等运算符的不准确运算。这个方法接受两个参数,如果这两个参数类型相等且具有相同的值,则返回true,否则返回false

console.log(Object.is(+0, -0)) // false
console.log(Object.is(3, '3')) // false
console.log(Object.is([], [])) // false
console.log(Object.is(NaN, NaN)) // true

对象合并

ES6新增了Object.assign()方法用于对象合并,该方法的第一个参数是目标对象,后面的参数是源对象,源对象的数量不限。

Object.assign()方法可以把源对象按照顺序将属性复制到目标对象中,如果目标对象与源对象有同名属性,或者源对象与源对象有同名属性,那么后面的属性会覆盖前面的属性。

注意: 属性的复制实际上是浅拷贝的过程,并且只拷贝源对象自身的属性,对于继承属性和不可枚举属性是不拷贝的

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

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
let o = {};
Object.defineProperty(o, 'name', {
  enumerable: false,
  value: 'wmui'
})

console.log(Object.assign({a: 1},o)) // {a: 1}
let o = {a: {b: 2}};
let o2 = Object.assign({}, o)
o.a.b = 3
console.log(o2.a.b) // 3

属性名重复

在ES6中,无论是在严格模式还是非严格模式下,代码不再检查重复属性,对于每一组重复属性,都会选取最后一个取值

如果是在ES5严格模式下,当同时存在多个同名属性时会抛出错误

let o = {
  name: 'li',
  name: 'wang'
}
console.log(o.name) // wang

枚举顺序

ES6严格规定了对象的自有属性被枚举时的返回顺序,这会影响到Object.getOwnPropertyNames()方法及Reflect.ownKeys返回属性的方式,自有属性枚举顺序的基本规则如下:

1、所有数字键按升序排序

2、所有字符串键按照它们被加入对象的顺序排序

3、所有symbol键按照它们被加入对象的顺序排序

let obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1
};
obj.d = 1;
console.log(Object.getOwnPropertyNames(obj).join("")); // "012acbd"

对象原型

__proto__属性,用来读取或设置当前对象的prototype对象,目前所有浏览器都部署了该属性。

标准规定,只有浏览器必须部署__proto__属性。开发过程中最好不要直接使用该属性,而是使用Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)来代替

Object.getPrototypeOf()

Object.getPrototypeOf(obj)方法用于获取一个对象的原型对象

Object.setPrototypeOf()

Object.setPrototypeOf()方法与proto作用相同,可以改变任意指定对象的原型。该方法接收两个参数:要改变原型的对象和替代第一个参数原型的对象

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   return 'li'
  }
}

let p = Object.create(person)
console.log(p.sayName()) // wang
console.log(Object.getPrototypeOf(p) === person) // true

// 改变原型
Object.setPrototypeOf(p,friend)
console.log(p.sayName()) // li
console.log(Object.getPrototypeOf(p) === friend) // true

对象原型的真实值被储存在内部专用属性[[protơtype]]中,调用Object.getPrototypeOf()方法返回储存在其中的值,调用Object.setPrototypeOf()方法改变其中的值。然而,这不是操作[[prototype]]值的唯一方法

super引用

ES6引入了super引用,使开发者可以更便捷的访问对象原型。

如果想重写对象实例的方法,又需要调用与它同名的原型方法,利用super引用可以这样写

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   return super.sayName() + ' and li are good friends';
  }
}
Object.setPrototypeOf(friend,person)
console.log(friend.sayName()) // wang and li are good friends
console.log(Object.getPrototypeOf(friend) === person) // true

super引用相当于指向对象原型的指针,实际上也就是Object.getPrototypeOf(this)的值

如果用es5来写这个示例,就要复杂一些,而且不容易理解

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   // 注意这里
   return Object.getPrototypeOf(this).sayName.call(this) + ' and li are good friends';
  }
}
Object.setPrototypeOf(friend,person)
console.log(friend.sayName()) // wang and li are good friends
console.log(Object.getPrototypeOf(friend) === person) // true

Super引用在多重继承情况下非常有用,因为在这种情况下,使用Object.getPrototypeOf()方法将会出现问题

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   return Object.getPrototypeOf(this).sayName.call(this) + ' and li are good friends';
  }
}
Object.setPrototypeOf(friend,person)

let p = Object.create(friend)
console.log(p.sayName()) // Uncaught RangeError: Maximum call stack size exceeded

当执行p.sayName()方法时,会调用friend.sayName()方法,而此时的this值为p。Object.getPrototypeOf(this)又会返回friend对象。所以就会进入递归调用直到触发栈溢出报错

如果使用super引用就不会有这种问题

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   return super.sayName() + ' and li are good friends';
  }
}

Object.setPrototypeOf(friend,person)

let p = Object.create(friend)
console.log(p.sayName()) // wang and li are good friends

super引用不是动态变化的,无论有多少其他方法继承了sayName()方法,super.sayName()始终指向person.sayName()方法

方法定义

在ES6以前从未正式定义过"方法"的概念,方法仅仅是一个具有功能而非数据的对象属性。而在ES6中正式将方法定义为一个函数,它会有一个内部的[[HomeObject]]属性来容纳这个方法从属的对象

let person = {
  sayName() {
   return 'wang'
  }
}

let friend = {
  sayName() {
   return super.sayName() + ' and li are good friends';
  }
}

Object.setPrototypeOf(friend,person)
console.log(friend.sayName()) 

当调用friend.sayName()方法时,该方法内部的[[HomeObject]]属性值是friend,friend的原型是person,所以super.sayName()等价于person.sayName().call(this)

对象遍历

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

let obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)) // ["a", "b", "c"]

for (let key of Object.keys(obj)) {
  console.log(key);
}
// a
// b
// c

ES8引入了Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用

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

let obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)) // [1, 2, 3]

for (let value of Object.values(obj)) {
  console.log(value);
}
// 1
// 2
// 3

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

let obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj)) // [["a", 1], ["b", 2], ["c", 3]]

for (let [key, value] of Object.entries(obj)) {
  console.log([key, value]);
}
// ["a", 1]
// ["b", 2]
// ["b", 3]
posted @ 2021-09-29 11:37  wmui  阅读(101)  评论(0编辑  收藏  举报