对象的扩展

ES中,当其键与值的变量名一致的时候,我们可以这样来写eg:
let a = 1;
let o = {a} 实际上 let o = {a: a} // {a: 1}

在函数中返回值的时候,我们也可以通过这种方式来返回多个值eg:


function f(a, b){
  return {a, b}
}
f(1, 2) // {a: 1, b: 2}
({a, b} = f(1, 2)) // {a: 1, b: 2} 解构赋值
在对象中定义方法eg:
let o = {
  a:1,
  f(){ // 这里f虽是个函数,但是它的键仍是一个字符串‘f’
    return this.a
  }
}
o.f() // 1;

在函数中用于返回多个值也同样是非常实用的.
在定义对象,键值有可能是一个表达式的情况,我们应用中括号“[]”括起来eg:


let a = 'abc';
let o = {
  [a]:1,
  [a + 'a']:2
}
o // {abc: 1, abca: 2}

实用表达式来表示一个对象属性中的键的时候,要注意,当该表达式本身就是一个对象的情况,该键值会转化为“[object Object]”字符串的形式来做给键值eg:

let a = {b: 1};
let o = {
  [a]:1
}
o // {[object Object]: 1}
o['[object Object]'] // 1

对象中定义的属性若是方法并且使用getter或setter函数来存取值的时候,这时方法的name属性不在是该对象上的属性函数了,而是该属性的getter或是setter对应的属性上面eg:

let o = {
  get foo() {},
  set foo(x) {}
};

o.foo.name
// TypeError: Cannot read property 'name' of undefined

let descriptor = Object.getOwnPropertyDescriptor(o, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous。

(new Function()).name // "anonymous"

var doSomething = function() {
// ...
};
doSomething.bind().name // "bound doSomething"
若对象的方法键值是一个Symbol值,则该name返回的是Symbol的描述值eg:
let key = Symbol('description');
let o = {
[key](){}
}
o[key].name // description

is Object 的静态方法,用于判断两个值是否相等(全等)eg:
Object.is(1, 1) // true
Object.is({}, {}) // false

有个特别点值得注意
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

assign Object 的静态方法,用于对象间的合并,将源对象合并到目标对象eg:
let a = {a: 1};
let b = {b: 2, a: 2};
Object.assign(a, b)
a // {a: 2, b: 2} 若存在同名的属性时,后者会覆盖前面的属性值

当该方法只有一个参数对象时,返回该对象,若该参数不是对象的时候,会首先转化为对象然后返回,无法转换,则抛异常,若不只一个参数的时候,除了第一个参数之后的参数无法转换的时候会自动跳过(实际上在后面的参数中,除了字符串的形式会以数组的形式显示外,其他的值都会被过滤掉)eg:
let a = {a: 1};
Object.assign(a) === a //true
Object.assign(1) // "object"
Object.assign(undefined) // 报错
let obj = {a: 1};
Object.assign(obj, null) === obj // true

通过assign方法实行的是浅拷贝,当对象中还包含对象的时候,就需要注意了。
assign可用于数组,将会把数组当做是对象来处理,下标便是对象的键值eg:
Object.assign([1, 2, 3],[4, 5]) // [4, 5, 3]
当对象中存在getter函数,则使用该方法后返回的是该getter函数的返回值,而不是该getter函数eg:
let o = {get a(){return 2}}
Object.assign({}, o) // {a: 2} 这里结果a不再是一个函数了

用于对象的扩展,相当于是在对象中添加新的属性或是方法等eg:
function f(){}
Object.assign(f.prototype,{b(){return 1}}) // {b: ƒ, constructor: ƒ}

克隆对象,通常assign只能克隆原始对象自身的值,链式继承eg:


function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}

可遍历的属性
有四个操作会忽略enumerable为false的属性。

for...in循环:只遍历对象自身的和继承的可枚举的属性。
Object.keys():返回对象自身的所有可枚举的属性的键名。
JSON.stringify():只串行化对象自身的可枚举的属性。
Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
(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 属性。

百度已收录
分享