分享

ECMAScript 6中除类之外的OOP新特性

 慈溪全媒体 2015-05-18
(点击上方蓝字,可快速关注我们)

ECMAScript 6 中最主要的 OOP 新特性是类,除此之外,也包含了一些对象字面量(object literals)的新特性和对象新的可用方法。本文将主要描述这些内容。


对象字面量的新特性


方法定义


在 ECMAScript 5中,方法是函数值的属性:


var obj = {

myMethod: function () {

···

}

};


在 ECMAScript 6中,方法依旧是函数值的属性,但现在定义方式更简洁了:


let obj = {

myMethod() {

···

}

};


Getters 和 setters 依旧像 ECMAScript 5 那样使用(注意方法定义的语法相似度)


let obj = {

get foo() {

console.log('GET foo');

return 123;

},

set bar(value) {

console.log('SET bar to '+value);

// return value is ignored

}

};


让我们使用obj:


> obj.foo

GET foo

123

> obj.bar = true

SET bar to true

true


还有一种简明方式来定义生成函数值的属性:


let obj = {

* myGeneratorMethod() {

···

}

};


这段代码相当于:


let obj = {

myGeneratorMethod: function* () {

···

}

};


属性值简写


属性值简写能让你将属性定义简写成对象字面量:如果变量的名称指定的属性值也是属性键,你就可以省略键,如下所示:


let x = 4;

let y = 1;

let obj = { x, y };


最后一行相当于:


let obj = { x: x, y: y };


属性值简写在重构方面也非常有用:


let obj = { x: 4, y: 1 };

let {x,y} = obj;

console.log(x); // 4

console.log(y); // 1


属性值简写的另外一个例子就是多值返回。


可计算的属性值


这里有两种设置属性指定属性键的方式:


  1. 通过一个固定的名字:obj.foo = true

  2. 通过一个表达式: obj['b'+'ar'] = 123


使用对象字面量时, ECMAScript 5只能选择第一种,而 ECMAScript 6则增加了第二种的选择:


let propKey = 'foo';

let obj = {

[propKey]: true,

['b'+'ar']: 123

};


这个新语法同样也可以结合方法定义:


let obj = {

['h'+'ello']() {

return 'hi';

}

};

console.log(obj.hello()); // hi


可计算的属性值的主要用法就是标志( symbols):你可以定义一个公共的标志 (symbols)并将其作为一个特殊的属性键。一个典型的例子就是将标志(symbols)储存在Symbol.iterator,如果对象有关于这个键的方法,就能成为可迭代的。这个方法返还一个用来迭代对象并使用 for-of 循环构造的迭代器。就像以下代码这样:


let obj = {

* [Symbol.iterator]() { // (A)

yield 'hello';

yield 'world';

}

};

for (let x of obj) {

console.log(x);

}

// Output:

// hello

// world


A行是生成方法定义了一个可计算的键(标志(symbols)储存在Symbol.iterator)。


对象的新方法


Object.assign(target, source_1, source_2, ···)

这个方法将source合并到target:这会改变target的值,首先复制列出所有source_1的属性,再到source_2的属性,等等,最后将返回target。


let obj = { foo: 123 };

Object.assign(obj, { bar: true });

console.log(JSON.stringify(obj));

// {"foo":123,"bar":true}


让我们仔细看看 Object.assign() 是怎么运作的:


  • 支持两种类型的属性键: Object.assign()支持将 strings 和标志(symbols)作为属性键

  • 只能枚举自己的属性:Object.assign()忽略属性继承和不枚举属性。

  • 通过赋值来复制:target对象中的属性是通过赋值创建的(内部操作[[put]])这意味着如果target有(自己的或者继承的)setters,将在复制的时候被调用。另一种方式是定义新属性,可以不用调用setter直接创建自己的新属性,最初是建议使用Object.assign()的变体来定义以取代赋值,这个提议已经被ECMAScript 6否决,但也有可能在更后面的版本中重新考虑。


Object.assign()的使用案例


让我们来看看几个使用案例,你可以使用Object.assign()在构造器中给this添加属性:


class Point {

constructor(x, y) {

Object.assign(this, {x, y});

}

}


Object.assign()在为缺失属性补充默认值也同样有用,在以下例子中,DEFAULTS 对象有属性的默认值和对象选项:


const DEFAULTS = {

logLevel: 0,

outputFormat: 'html'

};

function processContent(options) {

let options = Object.assign({}, DEFAULTS, options); // (A)

···

}


在A行,我们创建了一个新的对象,先复制defaults再复制options,并覆盖defaults, Object.assign()将返回我们赋给options的值来作为操作的结果。


另一个使用案例是给对象添加方法:


Object.assign(SomeClass.prototype, {

someMethod(arg1, arg2) {

···

},

anotherMethod() {

···

}

});


你也可以给函数赋值,但这不是好的方法定义语法,因为每次都需要使用 SomeClass.prototype。


SomeClass.prototype.someMethod = function (arg1, arg2) {

···

};

SomeClass.prototype.anotherMethod = function () {

···

};


最后一个Object.assign()的使用案例是拷贝( cloning)对象一种快捷的方式。


function clone(orig) {

return Object.assign({}, orig);

}


这种拷贝方式比较糟糕的一点是不能保存oirg的属性值,如果你需要这些值的话,你需要使用 property descriptors。


如果你希望拷贝的时候有原始对象一样的原型,你可以使用 useObject.getPrototypeOf 和 Object.create()


function clone(orig) {

let origProto = Object.getPrototypeOf(orig);

return Object.assign(Object.create(origProto), orig);

}

Object.getOwnPropertySymbols(obj)


在ECMAScript 6,属性键可以是 string 或者标志(symbol),现在有五个工具方法来获取obj对象的属性键:


  • Object.keys(obj) → Array<string>

  • 获取可枚举的自己属性的string属性键的全部值

  • Object.getOwnPropertyNames(obj) → Array<string>

  • 获取自己属性的string属性键的全部值

  • Object.getOwnPropertySymbols(obj) → Array<symbol>

  • 获取自己属性的标志(symbol)属性键的全部值

  • Reflect.ownKeys(obj) → Array<string|symbol>

  • 获取自己属性的属性键的全部值

  • Reflect.enumerate(obj) → Iterator

  • 获取可枚举的string属性键的全部值

  • Object.is(value1, value2)

  • 严格等于操作符(===)比较两个不同的值的时候有时候会与你预期的不同。


首先,NaN不等于NaN


> NaN === NaN

false


不幸的是,这会阻碍我们检测到NaN


> [0,NaN,2].indexOf(NaN)

-1


其次,JavaScript 有两个0,但是等于操作符将其看做相等的,


> -0 === +0

true


这样做通常是件好事:


Object.is()提供一个比===更精确的比较值的方式,如下所示:


> Object.is(NaN, NaN)

true

> Object.is(-0, +0)

false


其他的将使用===来比较


如果我们将Object.is()和 ECMAScript 6的新数组方法findIndex()结合使用,我们将在数组中找到NaN:


> [0,NaN,2].findIndex(x => Object.is(x, NaN))

Object.setPrototypeOf(obj, proto)


这个方法是将obj的原型设置为proto,在ECMAScript 5标准中不提供这样的做法,但这个做法得到一些引擎的支持,并通过一个特定的属性 __proto__ 来赋值。推荐设置原型的方法跟 ECMAScript 5是相同的:在创建对象的过程中,使用Object.create()将更快的创建的对象和设置原型,但很明显的是,他不能改变已存在对象的原型。


相关资料


  1. Using ECMAScript 6 today

  2. ECMAScript 6: classes

  3. Iterators and generators in ECMAScript 6

  4. Multiple return values in ECMAScript 6

  5. ECMAScript 6’s new array methods

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多