Symbol的toStringTag属性

1,419 阅读2分钟

MDN上的解释为:

Symbol.toStringTag是一个内置symbol,它通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签,通常只有内置的Object.prototype.toString()方法会去读取这个标签并把它包含在自己的返回值里。

也就是说,这个属性定义了Object.prototype.toString()方法的返回值。

我们先看Object.prototype.toString()方法。

Object.prototype.toString()

mdn上对Object.prototype.toString()的解释为:

Object.prototype.toString()返回一个表示该对象的字符串。

每个对象都有一个toString()方法,当该对象表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。

默认情况下,toString()方法被每个Object对象继承,如果此方法在自定义对象中未被覆盖,toString()返回“[object type]”,其中type是对象的类型。

var toStringFunc = Object.prototype.toString
toStringFunc.call(new Date)                         // '[object Date]'
toStringFunc.call(new String)                       // '[object String]'
toStringFunc.call(Math)                             // '[object Math]'
toStringFunc.call(undefined)                        // '[object Undefined]'
toStringFunc.call(null)                             // '[object Null]'

也可以自定义一个方法,来覆盖默认的toString()方法。该toString()不能传入任何参数,并且必须返回一个字符串。自定义的toString()方法可以是任何我们需要的值,但如果它附带有关对象的信息,它将变得非常有用。

function Dog(name, color) {
    this.name = name
    this.color = color
}
var dog = new Dog('Gabby', 'white')
// 默认的toString()
dog.toString()                              // '[object Object]'

// 覆盖默认的toString()
Dog.prototype.toString = function () {
    return `Dog${this.name} is ${this.color}`
}
dog.toString()                              // 'Dog Gabby is white'

Symbol.toStringTag

前面说到,Symbol.toStringTag其实是定义了Object.prototype.toString()方法的返回值。以Map为例,Object.prototype.toString.call(new Map())的返回值通常是'[object Map]',这个Map就是存储在Symbol.singleTag这个键中的值。

但是并不是所有的对象都有toStringTag属性,没有toStringTag属性的某些对象也能被toString()方法识别并返回特定的类型标签。比如:

let toStringFunc = Object.prototype.toString
toStringFunc.call('foo')                        // '[object String]'
toStringFunc.call([1, 2])                       // '[object Array]'
toStringFunc.call(3)                            // '[object Number]'
toStringFunc.call(true)                         // '[object Boolean]'
toStringFunc.call(undefined)                    // '[object Undefined]'
toStringFunc.call(null)                         // '[object Null]'

另外一些对象的类型toString()方法能识别它们就是因为引擎为它们设置好了toStringTag标签,toString()会返回Symbol.toStringTag键对应的值。比如上面提到的Map,还有:

Object.prototype.toString.call(function* () {}) 
// '[object GeneratorFunction]'

Object.prototype.toString.call(Promise.resolve())
// '[object Promise]'

但自己创建的类不会有这份特殊待遇,toString()找不到toStringTag属性时只好返回默认的Object标签。

class TestToStringTagClass {}
Object.prototype.toString.call(new TestToStringTagClass())
// '[object Object]'

加上toStringTag属性,自定义的类也会有自定义的类型标签了。

class TestToStringTagClass {
    get [Symbol.toStringTag]() {
        return "TestToStringTag"
    }
}
Object.prototype.toString.call(new TestToStringTagClass())
// '[object TestToStringTag]'