物件實字簡化與增強
August 4, 2022在 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
值可作為協定,這時就要搭配 []
來指定。例如:
> 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.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
特性之 writable
為 false
,如果想定義自己的 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
取得的特性描述中,enumerable
是 true
。
> 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) ]
>