ECMAScript 6 学习笔记(八):对象的扩展

栏目: JavaScript · 发布时间: 6年前

内容简介:ES6 中允许直接写入变量和函数,作为对象的属性和方法:ES6 允许字面量定义对象时,用表达式作为对象的属性名。不过需要注意的是,属性名表达式与新的表达方式不能同时使用,会报错。方法和函数一样,也有

对象的新表达方法

ES6 中允许直接写入变量和函数,作为对象的属性和方法:

var foo = 1;
var bar = {foo}; // {foo: 1}

var obj = {
  method() {
    return 0;
  }
}

属性名称表达式

ES6 允许字面量定义对象时,用表达式作为对象的属性名。不过需要注意的是,属性名表达式与新的表达方式不能同时使用,会报错。

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

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

const foo = 'bar';
const bar = 'abc';
const baz = { [foo] }; // 报错

方法的 name 属性

方法和函数一样,也有 name 属性,用来返回方法名,不过有一些特殊情况需要注意:

  1. 如果对象的方法使用了取值函数( getter )和存值函数( setter ),则 name 属性不是在该方法上面,而是该方法的属性描述对象的 getset 上面。
const obj = {
  get foo() {},
  set foo(x) {}
};

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

const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"
  1. bind() 方法创造的函数, name 属性返回 bound 再加上原函数名。
var func = function() {};
func.bind().name // "bound func"
  1. Function 构造函数创建的函数, name 属性返回 anomymous
(new Function()).name // "anonymous"
  1. 如果对象的方法是一个 Symbol 值,则 name 属性返回这个 Symbol 的描述。
const key1 = Symbol('description');
let obj = {
  [key1]() {}
};
obj[key1].name // "[description]"

属性的可枚举性和遍历

可枚举性

对于一个对象来说,可以用 Object.getOwnPropertyDescriptor 查看该对象属性的描述对象(Descriptor),可通过其中的 enumerable 判断属性是否可枚举,这个属性的作用是为了让某些操作忽略不可枚举的属性。

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

目前有四个操作会忽略不可枚举的属性:

for...in
Object.keys()
JSON.stringify()
Object.assign()

另外,ES6 规定,所有 Class 的原型的方法都是不可枚举的。

属性的遍历方法

for...in
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()

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

  • 首先遍历所有数值键,按照数值升序排列;
  • 其次遍历所有字符串键,按照加入时间升序排列;
  • 最后遍历所有 Symbol 键,按照加入时间升序排列。

super 关键字

ES6 新增的与 this 类似的关键字,用来指向当前对象的原型对象。注意, super 关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。JavaScript 引擎内部, super.foo 等同于 Object.getPrototypeOf(this).foo (属性)或 Object.getPrototypeOf(this).foo.call(this) (方法)。

const proto = {
  foo: 'hello'
};

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

// 报错
const obj = {
  foo: super.foo
}

// 报错
const obj = {
  foo: () => super.foo
}

// 报错
const obj = {
  foo: function () {
    return super.foo
  }
}

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

对象的新增方法

Object.is()

行为类似严格相等运算符( === ),但是解决了严格相等运算符的一些问题:

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

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

Object.assign()

用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。该方法的第一个参数是目标对象,后面的参数都是源对象。需要注意的是,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。同时还要注意,这个方法实行的是浅拷贝,而不是深拷贝。

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 会直接返回该参数。如果该参数不是对象,则会先转成对象,然后返回。如果参数无法转成对象( undefinednull ),就会报错。其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。

const v1 = 'abc';
const v2 = true;
const v3 = 10;

Object(true) // {[[PrimitiveValue]]: true}
Object(10)  //  {[[PrimitiveValue]]: 10}
Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}

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

Object.assign() 只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }

常见用途

  1. 为对象添加属性
class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  } // 将属性 x 和属性 y 添加到 Point 类的对象实例
}
  1. 为对象添加方法
Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});
  1. 克隆对象
function clone(origin) {
  return Object.assign({}, origin);
} // 只克隆值,不能克隆继承的值

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
} // 保持继承链
  1. 合并多个对象
const merge = (...sources) => Object.assign({}, ...sources); // 返回一个新对象
  1. 为属性指定默认值
const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'
};

function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
  console.log(options);
  // ...
}

Object.getOwnPropertyDescriptors()

对应于 ES5 的 Object.getOwnPropertyDescriptor() ,该方法用来返回指定对象所有自身属性(非继承属性)的描述对象。该方法的主要目的是解决 Object.assign() 无法正确拷贝 get 属性和 set 属性的问题。使用该方法配合 Object.defineProperties() 方法即可实现正确拷贝:

const shallowMerge = (target, source) => Object.defineProperties(
  target,
  Object.getOwnPropertyDescriptors(source)
);

另外,使用该方法还可以实现一个对象继承另一个对象的新写法:

// 传统写法
const obj = {
  __proto__: prot,
  foo: 123,
};
// 或者
const obj = Object.create(prot);
obj.foo = 123;

// 新的写法
const obj = Object.create(
  prot,
  Object.getOwnPropertyDescriptors({
    foo: 123,
  })
);

关于 __proto__ 属性

__proto__ 属性(前后各两个下划线),用来读取或设置当前对象的 prototype 对象。目前,所有浏览器(包括 IE11)都部署了这个属性。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的 Object.setPrototypeOf() (写操作)、 Object.getPrototypeOf() (读操作)、 Object.create() (生成操作)代替。

Object.setPrototypeOf()

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

Object.setPrototypeOf(object, prototype)
// 等同于
function setPrototypeOf(obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。

Object.setPrototypeOf(1, {}) === 1 // true
Object.setPrototypeOf('foo', {}) === 'foo' // true
Object.setPrototypeOf(true, {}) === true // true

Object.getPrototypeOf()

用来读取一个对象的原型对象,如果参数不是对象,会被自动转为对象。

function Rectangle() {
  // ...
}

const rec = new Rectangle();

Object.getPrototypeOf(rec) === Rectangle.prototype
// true

Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype
// false

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

  1. Object.keys()

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

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

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。该方法会过滤属性名为 Symbol 值的属性,如果该方法的参数是一个字符串,会返回各个字符组成的一个数组。

Object.values({ [Symbol()]: 123, foo: 'abc' });
// ['abc']

Object.values('foo')
// ['f', 'o', 'o']

如果参数不是对象, Object.values 会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以, Object.values 会返回空数组。

  1. Object.entries()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。除了返回值不一样,该方法的行为与 Object.values 基本一致。

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

以上三个函数配合使用:

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

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

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

for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.fromEntries()

Object.entries() 方法的逆操作,用于将一个键值对数组转为对象。该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。

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

const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }

以上所述就是小编给大家介绍的《ECMAScript 6 学习笔记(八):对象的扩展》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Numerical Linear Algebra

Numerical Linear Algebra

Lloyd N. Trefethen、David Bau III / SIAM: Society for Industrial and Applied Mathematics / 1997-06-01 / USD 61.00

Numerical Linear Algebra is a concise, insightful, and elegant introduction to the field of numerical linear algebra.一起来看看 《Numerical Linear Algebra》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具