《七》ES6+ 中对象的扩展

globalThis 对象:

在之前,如果想要获取 JS 环境的全局对象,在不同的环境中获取的方式是不一样的。在浏览器中可以通过 this、window 来获取,在 Node 中需要通过 global 来获取。

在 ES11 中对获取全局对象进行了统一的规范,通过 globalThis 对象即可。

属性名表达式:

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'
function change(){...}

const Person = {
  name,
  change,
  handle() {...}
};

// 等同于
const Person = {
  name : name,
  change: change,
  handle: fucntion() {...}
};

属性的可枚举性和遍历:

属性的可枚举性:

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

目前,有四个操作会忽略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()]

可选链运算符 ?.

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

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

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

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

如果对象的属性是一个表达式,也可以使用可选链操作符。

var obj = {}
var key = 'name'
obj?.[key] && (obj[key] = 'Mary')

如果判断的是一个函数,那么在使用小括号调用它前也必须加上可选链操作符 ?.

const person = {
	friend: {}
}

// friend?.running 会返回 undefined,再调用 undefined() 就会报错
person?.friend?.running() // 报错
// friend?.running 返回 undefined,undefined?. 判断到左侧是 undefined 就不再往下运算,不会报错
person?.friend?.running?.() // 正确

可选链操作符不能用于赋值操作的左侧。

obj?.[key] = 'Lee' //  报错。Uncaught SyntaxError: Invalid left-hand side in assignment

空值合并运算符 ??

// 读取对象的属性时,本意是只要属性值为 null 或 undefined,默认值就生效。但此处使用或运算符,如果属性值为空字符串、false 或者 0,默认值也会生效。
const headerText = response.headerText || 'Hello, world!';

为了避免这种情况,ES11引入了空值合并运算符??,它的行为类似||,但是只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值。

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

Object.is()

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

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

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

Object.is('foo', 'foo') // true
Object.is({}, {}) // 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.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():返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。

Object.entries() 的参数不仅仅可以是对象;还可以是数组和字符串,此时会将索引作为 key。

const str = 'Hello'
const arr = [1, 2, 3]
console.log(Object.entries(str))
console.log(Object.entries(arr))

请添加图片描述

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

Object.fromEntries()

Object.fromEntries():是 Object.entries() 方法的逆操作,用于将一个键值对的二维数组或 Map 类型的数据转为对象。

// 键值对的二维数组
Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
]) // { foo: "bar", baz: 42 }

// Map
let m = new Map()
m.set('name', 'Lee')
let obj = Object.fromEntries(m)
console.log(obj) // {name: 'Lee'}

Object.getPrototypeOf()Object.setPrototypeOf()obj.isPrototypeOf()

Object.getPrototypeOf():用于获取对象的隐式原型对象

Object.setPrototypeOf():用于设置对象的隐式原型对象。

obj.isPrototypeOf():用于判断某个对象是否在另一个对象的隐式原型链上。

var person = {name: 'Lee'}
var student = {score: 500}

Object.setPrototypeOf(student, person) // 设置 student 的隐式原型对象为 person
console.log(Object.getPrototypeOf(student)) // 获取 student 的隐式原型对象
console.log(person.isPrototypeOf(student)) // true。判断 person 是否在 student 的隐式原型链上

Object.hasOwn()

Object.hasOwn(obj, propKey):用于判断某个对象自身(非原型链)是否有某个属性。

Object.hasOwn() 用来替代 obj.hasOwnProperty(propKey)
原因:因为 hasOwnProperty() 是放在 Object 的显示原型上的,对于隐式原型指向 null 的对象,根本就无法访问到 hasOwnProperty()

// 创建一个对象,并将其隐式原型指向 null
const obj = Object.create(null)
console.log(obj.hasOwnProperty('name'))

请添加图片描述

let obj = {
	name: 'Lee',
}
obj.__proto__.age = 18
console.log(Object.hasOwn(obj, 'name')) // true
console.log(Object.hasOwn(obj, 'age')) // false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值