メインコンテンツへ移動

Symbol_ES6 ノート 7

無料2016-05-08#JS#es6 symbol#js symbol#js symbol是什么

Symbol は精巧な特性で、既存の API を拡張する大きな問題を解決した

##一.Symbol とは何か

typeof Symbol() === 'symbol'、symbol は js における 7 番目の基本型です(元々あった 6 つは null, undefined, Number, Boolean, Object, String)。文字列でもオブジェクトでもありません

役割:symbol は命名衝突を回避するために使用され、ネイティブオブジェクトの改ざん(属性追加)の後遺症を解決し、属性名が将来ネイティブ属性名や他のライブラリ操作と衝突することを心配する必要がありません

##二.構文

Symbol を取得する方法は 3 通りあります。以下の通り:

###1.Symbol(desc)

symbol を返します。desc はオプションで、symbol.toString() は`Symbol(${desc})`を返します。例えば:

var obj = {
    a: 1
};

// 不用 new,Symbol 不是构造器
var safeKey = Symbol();
obj[safeKey] = 'value';
console.log(obj[safeKey]);  // value

var anotherSafeKey = Symbol('isAnimActive');
console.log(anotherSafeKey);    // Symbol(isAnimActive)

上記の var safeKey = Symbol(); は少し奇妙に見えます。new 演算子はどこでしょうか?Symbol はコンストラクタではありませんnew 演算子で呼び出すことはできません。無理に new すると、次のようなエラーが発生します:

Uncaught TypeError: Symbol is not a constructor

これは確かに奇妙です。コーディング規約では一般に先頭文字が大文字は型名を表し、new 演算子で呼び出すべきですが、API が提供するのはコーディング規約に合致しない Symbol 関数です

特徴:

  • Symbol('key') !== Symbol('key')、Symbol() を呼び出すたびに異なる値が返されます

  • Symbol は属性名として使用でき、しかも何とも(文字列、数字、他の Symbol)等しくありません。したがって Symbol 型の属性名は既存のどの属性名とも衝突せず、将来のどの新しい属性名とも衝突しません

  • for...inObject.keys(obj)Object.getOwnPropertyNames(obj) は symbol 属性をスキップします

  • Object.getOwnPropertySymbols(obj) はオブジェクトのすべての symbol 属性名を返します

  • Reflect.ownKeys(obj) はオブジェクトのすべての属性名(symbol 属性名と文字列属性名を含む)を返します

  • symbol は読み取り専用で、文字列に似ています。厳密モードで symbol に属性を追加すると TypeError がエラーになります

  • symbolは自動的に文字列に変換されません。symbol を拼接しようとすると TypeError がエラーになります。手動で toString() を呼び出してから拼接できます

サンプル(前のサンプルに続く)は以下の通り:

obj[Symbol('ready')] = true;
//!!! undefined
// 因为特点 1
console.log(obj[Symbol('ready')]);  // undefined
console.log(obj);   // Object {Symbol(): "value", Symbol(ready): true}
// 跳过 symbol 属性
for (var key in obj) {
    console.log(`obj[${key}] = ${obj[key]}`);
}   // obj[a] = 1

// 获取所有 obj 上所有 Symbol 类型的属性名
console.log(Object.getOwnPropertySymbols(obj)); // Array [ Symbol(), Symbol(ready) ]
// 获取 objs 上的所有属性名
console.log(Reflect.ownKeys(obj));  // Array [ "a", Symbol(), Symbol(ready) ]

// 只读
var s = Symbol();
s.a = 1;
console.log(s.a);   // undefined

// 不会自动转字符串
// console.log(Symbol('123') + '4');   // TypeError: can't convert symbol to string
console.log(Symbol('123').toString() + '4');    // Symbol(123)4

###2.Symbol.for(str)

symbol レジストリを表し、共有 symbol を作成するために使用されます。特徴は以下の通り:

  • Symbol.for('ready') === Symbol.for('ready')

  • Symbol('str') !== Symbol.for('str')

サンプル(前のすべてのサンプルに続く):

// Symbol.for()
obj[Symbol.for('ready')] = 'ready';
console.log(obj[Symbol.for('ready')]);  // ready
console.log(Object.getOwnPropertySymbols(obj));
// log print: Array [ Symbol(), Symbol(ready), Symbol(ready) ]

P.S. 最後に出力された配列には 2 つの Symbol(ready) がありますが、それらに対応する symbol は等しくありません。toString が返す結果が同じなだけです(特徴 2)

注意:共有 Symbol の場合、属性名が将来の DOM API と衝突しないことしか保証できません。他のコードと衝突しないことは保証できません。ライブラリ作者はそれを使用すべきではありません。ビジネスコードでモジュール間またはページ間 Symbol 共有が必要な場合にのみ使用することを推奨します

###3.Symbol.xxx

ネイティブ Symbol を取得するために使用されます。例えば Symbol.iterator。特徴は以下の通り:

  • 新しい API は後方互換性があります。例えば iterable インターフェースを実装:obj[Symbol.iterator] = gen。古いコードに影響しません

  • hook を準備しました。symbol を借助して、今後新しい特性 API はすべて古いコードに影響しません

まだ実装されていない新しい特性:

  • Symbol.hasInstanceinstanceof を拡張

  • Symbol.unscopables はメソッドが動的スコープに追加されるのを阻止

  • Symbol.match は str.match を拡張

これらの Symbol はすでにネイティブ Symbol として予約されており、すぐに実装されます。今後の(既存 API を強化する)新しい特性はすべて Symbol を通じて簡単かつ安全に追加できます

##三.应用场景

###1.DOM 要素にマーク属性を追加

おそらく多くの場合、DOM 要素にカスタム属性を追加する必要はありません。副作用があるからです(カスタム属性名が他のコードと衝突する可能性、または将来の DOM API と衝突する可能性)。そのため一般に表構造を維持して要素に対応する状態などの追加情報を保存し、毎回表を照会して目標要素の追加情報を取得します。表が大きい場合、表照会は時間がかかります。表構造が複雑な場合、表照会操作自体も非常に面倒になります。。。等等、なぜ表を照会して要素の追加情報を取得する必要があるのでしょうか?副作用があるからです。では、この副作用がなくなったらどうでしょうか?

Symbol はこの副作用を消除するために使用されます。要素関連の情報は、直接 Symbol オブジェクトを key として DOM 要素に追加すればよいです。シンプルで乱暴で安全で効果的です。例えば:

var IS_MOVING = Symbol('isMoving');
var INFO = Symbol('info');

if (!elem[IS_MOVING]) {
    anim(elem);
    elem[IS_MOVING] = true;
    elem[INFO] = {
        step: 1;
    };
}

カスタムの Symbol 型の key を持有し、その key を自分のスコープに置けば、副作用は一切なくなります

###2.ユニークな key を生成

以前はユニークな key を生成するには少し複雑なアルゴリズムが必要でしたが、現在 Symbol は最もシンプルなユニーク key 生成方法です。例えば:

// 不关注属性名,只是想简单地存取 obj
var data = {
    dir: {},
    save(val) {
        var key = Symbol();
        this.dir[key] = val;
        return key;
    },
    get(key) {
        return this.dir[key];
    }
}
var key = data.save('data');
console.log(data.get(key)); // data

###3.ネイティブ Symbol を通じてサポートされる API 拡張を実装

標準はますます多くの hook を提供し、API 拡張メソッドを実装できるようにします。例えばカスタムオブジェクトをイテラブルにします:

var obj = {
'a': 1,
'b': 2
}
// generator 实现迭代器
obj[Symbol.iterator] = function*() {
    for (var key in this) {
        yield this[key];
    }
}
for (var val of obj) {
    console.log(val);
}

##四.まとめ

Symbol は精巧な特性で、既存の API を拡張する大きな問題を解決しました(新しい特性に一連の hook メカニズムを提供し、今後安全に拡張できます)

###参考資料

  • 『ES6 in Depth』:InfoQ 中文站が提供する無料電子書籍

コメント

コメントはまだありません

コメントを書く