理解ES6中的Symbol (understanding es6 第5章)

英文電子書點此閱讀《understanding es6》

目錄

symbol 和 symbol屬性

一開始出於對象屬性的私有性的考慮,在基礎數據類型中添加了symbol(string, number, boolean, null, undefined)。但後來真正改變的只是key值不再僅僅是字符串了,私有性的初衷被放棄了。

創建symbols

  • symbol 沒有字面量的形式。要用全局的 Symbol() 函數來創建。
  • 由於它屬於基本變量,也不能 new 一個 。
  • Symbol 函數可以接受一個可選的參數作爲其描述。這個描述不能用於獲取對象的屬性,只是debug有用。
let firstName = Symbol()
let secondName = Symbol('lala')

let person = {}
person[secondName] = 'wuwuwuwu'

使用Symbols

  • 用法同computed property name
let firstName = Symbol("first name");

// use a computed object literal property
let person = {
    [firstName]: "Nicholas"
};

// make the property read only
Object.defineProperty(person, firstName, { writable: false });

共享Symbols

  • 如果想要創建一個共享的symbol,用 Symbol.for() 方法,而不是 Symbol() 方法
  • Symbol.for()接收一個參數,作爲該symbol的字符標識符,同時也是symbol的描述
// 首先它會全局搜索是否有這樣一個key 叫 "uid" 的 symbol, 如果有就返回這個symbol。
// 如果沒有,會創建一個新的symbol,並且註冊到全局的symbol 體系中。並返回這個新symbol。


let uid = Symbol('uid')  
let object = {}

object[uid] = 12345

console.log(object[uid]);       // 12345
console.log(uid);               // "Symbol(uid)"

let uid2 = Symbol.for("uid");

console.log(uid === uid2);      // true

Symbol.keyFor(uid2);     // "uid"
//如果不用 Symbol.for來創建symbol, 那全局的symbol中就沒有它,用keyfor尋找會找不到。
Symbol Coercion 類型強制轉換
  • symbol 不能被隨意轉換爲string/number。除非顯式轉換。
  • symbol 被轉爲 布爾型數據時, 都是true
var uid = Symbol.for('uid')

var des = String(uid)
//"Symbol(uid)"

des = uid + ''
//VM1517:1 Uncaught TypeError: Cannot convert a Symbol value to a string

des = +uid
//VM1520:1 Uncaught TypeError: Cannot convert a Symbol value to a number
檢索對象的symbol
  • 之前的 Object.keys()/Object.getOwnPropertyNames() 都拿到不到symbol
  • ES6新加了 Object.getOwnPropertySymbols()方法來以數組的方式收集對象的自有symbols
let uid = Symbol.for("uid");
let object = {
    [uid]: "12345"
};

let symbols = Object.getOwnPropertySymbols(object);
用 well-known Symbols 暴露內部操作(似乎沒什麼用)
  • 這些 well-known Symbols 的 descriptor 就是類似 “Symbol.hasInstance” 這樣的,同時也被定義在了 Global 的 Symbol 體系上。

  • Symbol.hasInstance 定義在 Fuction.prototype 上

Object.getOwnPropertySymbols(Function.prototype)  //[Symbol(Symbol.hasInstance)]

 obj instanceof Array
// 就等於 用 Array 的 symbol 屬性來取得其值
Array[Symbol.hasInstance](obj)
  • 也可以改寫該函數,但必須用Object.defineProperty() 方法
function SpecialNumber() {
    // empty
}

Object.defineProperty(SpecialNumber, Symbol.hasInstance, {
    value: function(v) {
        return (v instanceof Number) && (v >=1 && v <= 100);
    }
});

let two = new Number(2),
    zero = 0;

console.log(two instanceof SpecialNumber);    // true
console.log(zero instanceof SpecialNumber);   // false

注意instanceof 的左側必須是一個顯式聲明的對象,從而來調用instanceof,因爲非對象只會簡單地返回一個 false

後面的東西太細節了,用的時候去看api吧。

放點代碼:

// effectively equivalent to /^.{10}$/
let hasLengthOf10 = {
    [Symbol.match]: function(value) {
        return value.length === 10 ? [value] : null;
    },
    [Symbol.replace]: function(value, replacement) {
        return value.length === 10 ? replacement : value;
    },
    [Symbol.search]: function(value) {
        return value.length === 10 ? 0 : -1;
    },
    [Symbol.split]: function(value) {
        return value.length === 10 ? ["", ""] : [value];
    }
};

let message1 = "Hello world",   // 11 characters
    message2 = "Hello John";    // 10 characters


let match1 = message1.match(hasLengthOf10),
    match2 = message2.match(hasLengthOf10);

console.log(match1);            // null
console.log(match2);            // ["Hello John"]

let replace1 = message1.replace(hasLengthOf10, "Howdy!"),
    replace2 = message2.replace(hasLengthOf10, "Howdy!");

console.log(replace1);          // "Hello world"
console.log(replace2);          // "Howdy!"

let search1 = message1.search(hasLengthOf10),
    search2 = message2.search(hasLengthOf10);

console.log(search1);           // -1
console.log(search2);           // 0

let split1 = message1.split(hasLengthOf10),
    split2 = message2.split(hasLengthOf10);

console.log(split1);            // ["Hello world"]
console.log(split2);            // ["", ""]


function Temperature(degrees) {
    this.degrees = degrees;
}

// hint 是被JS引擎自動賦給的
Temperature.prototype[Symbol.toPrimitive] = function(hint) {

    switch (hint) {
        case "string":
            return this.degrees + "\u00b0"; // degrees symbol

        case "number":
            return this.degrees;

        case "default":
            return this.degrees + " degrees";
    }
};

let freezing = new Temperature(32);

console.log(freezing + "!");            // "32 degrees!"
console.log(freezing / 2);              // 16
console.log(String(freezing));          // "32°"

判斷是否是原生的json:

function supportsNativeJSON() {
    return typeof JSON !== "undefined" &&
        Object.prototype.toString.call(JSON) === "[object JSON]";
}
function Person(name) {
    this.name = name;
}

Person.prototype[Symbol.toStringTag] = "Person";

let me = new Person("Nicholas");

console.log(me.toString());                         // "[object Person]"
console.log(Object.prototype.toString.call(me));    // "[object Person]"

summary

  • Symbols 是ES6新加的基本數據類型,只有用Symbol() 才能創建
  • 雖然不是特別私有,但Symbol類型的 屬性 更不容易被錯誤地刪改
  • 不能用 Object.keys() 或 Object.getOwnPropertyNames() 來獲取symbol 屬性,但可以用Object.getOwnPropertySymbols() 來檢索symbol屬性。

  • 可以改寫特殊的 well-known symbols,來自定義方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章