对象字面量简化与增强


在 JavaScript 中,使用底下的方式创建对象字面量应该是屡见不鲜了:

var x;
var y;

// 经过一些计算得到 x、y 的值...

var o = {
    x : x,
    y : y
};

在 ES6 中现在可以直接写:

let x;
let y;

// 经过一些计算得到 x、y 的值...

let o = {
    x,
    y
};

变量名称会成为特性名称,而变量值会成为特性值。当特性实际参考函数时,在过去会这么写:

var o = {
    doSome : function() {
        //...
    },
    doOther : function(param) {
        // ...
    }
};

ES6 可以简单地写为:

let o = {
    doSome() {
        //...
    },
    doOther(param) {
        // ...
    }
};

然而,简单的写法看似只有简化,与传统的写法还有点不同,简单的写法中可以使用super关键字:

> let o = {
...       doABC() {
.....         return super.toString();
.....     }
... };
undefined
> o.doABC();
'[object Object]'
> var o = {
...       doABC : function() {
.....         return super.toString();
        return super.toString();
               ^^^^^

SyntaxError: 'super' keyword unexpected here

>

super是个关键字,不是个参考名称,在可以使用super的场合,super代表着对象的__proto__,这之后会再讨论。

在过去,如果打算让特性名称是计算后的结果,必须透过[],例如:

var prefix = 'doSome';
var n = 1;
var o = {};
o[prefix + n] = function() {
    // ...
};

在 ES6 中,可以直接这么作了:

let prefix = 'doSome';
let n = 1;
let o = {
    [prefix + n] : function() {
    }
};

或者是直接采用简便模式:

let prefix = 'doSome';
let n = 1;
let o = {
    [prefix + n]() {
    }
};

在〈符号〉中曾经谈过,Symbol值是独一无二的,如果对象上需要有个独一无二的特性,可以使用Symbol值作为特性,这时就要搭配[]来指定。例如:

> let hook = Symbol('some method hook');
undefined
> let o = {
...     [hook]() {
.....       console.log('do some ....');
.....   }
... };
undefined
> o[hook];
[Function: [some method hook]]
> o[hook]();
do some ....
undefined
>

会使用Symbol作为特性,通常是作为对象间某个特定的协定,例如〈符号〉就曾看过,对象若要实现迭代器,会使用Symbol.iterator作为特性,因而其他想想要获取迭代器,就可以透过Symbol.iterator

Symbol.toStringTag也是个例子,在〈检验对象〉中看过,调用对象的toString(),要返回'[object class]'格式的字符串,例如:

> let o = {};
undefined
> o.toString();
'[object Object]'
>

Object实例会返回[object Object]、数组会返回[object Array]、函数会返回[object Function]等,在 ES6 中,若想指定 class 部份的描述,可以使用Symbol.toStringTag特性来指定:

> let o = {
...     [Symbol.toStringTag] : 'Foo'
... };
undefined
> o.toString();
'[object Foo]'
>

在〈不只是加减乘除的运算符〉中看过,如果运算过程牵涉到基本类型与对象,可以定义对象的valueOf方法,使之返回可用于计算的基本类型,在 ES6 中有了个专门的符号Symbol.toPrimitive可用于设定特性,例如:

> let o = {
...     [Symbol.toPrimitive]() {
.....       return 10;
.....   }
... };
undefined
> 2 + o;
12
> o.valueOf();
{ [Symbol(Symbol.toPrimitive)]: [Function: [Symbol.toPrimitive]] }
>

可以看到,定义了Symbol.toPrimitive特性的对象,在必须转换为基本类型的场合,就会使用该特性而不是valueOf

当你定义一个构造函数,用该构造函数new出来的对象,在instanceof判断时会是true,而在〈检验对象〉中看过,ES5 中instanceof是根据原型链来查找。

而在 ES6 中,instanceof是否为true,还可借由构造函数的Symbol.hasInstance特性来决定:

> function Foo() {}
undefined
> let foo = new Foo();
undefined
> foo instanceof Foo;
true
> Foo[Symbol.hasInstance](foo);
true
>

构造函数的Symbol.hasInstance特性是个函数,接受一个对象,判定该对象是否为此构造函数的一个实例,构造函数的Symbol.hasInstance特性之writablefalse,如果想定义自己的Symbol.hasInstance特性,可以透过Object.defineProperty

> function ArrayLike() {}
undefined
> Object.defineProperty(ArrayLike, Symbol.hasInstance, {
...     value : function(obj) {
.....     return obj.length !== undefined;
.....   }
... });
[Function: ArrayLike]
> let o1 = {};
undefined
> let o2 = {length : 0};
undefined
> o1 instanceof ArrayLike;
false
> o2 instanceof ArrayLike;
true
>

使用Symbol作为特性,无法使用for (var prop in obj)的方式迭代,然而,透过Object.getOwnPropertyDescriptor获取的特性描述中,enumerabletrue

> Object.getOwnPropertyDescriptor(o, Symbol.toStringTag);
{ value: 'Foo',
  writable: true,
  enumerable: true,
  configurable: true }
> for(let p in o) {
...     console.log(p);
... }
undefined
>

想要一次获取对象上使用Symbol的特性,可以透过Object.getOwnPropertySymbols,例如:

> Object.getOwnPropertySymbols(o);
[ Symbol(Symbol.toStringTag) ]
> Object.getOwnPropertySymbols(Array.prototype);
[ Symbol(Symbol.iterator), Symbol(Symbol.unscopables) ]
>




展开阅读全文