创建Symbol
新增基础类型 Symbol,用于解决 私有属性 问题
let firstName = Symbol()
let person = {}
person[firstName] = 'Nicholas'
console.log(person["firstName"])
注意, Symbol是原始值,不能 new Symbol()构建实例,只能是new Object(【Symbol 值】)
Symbol 接受可选参数,用于描述该 Symbol 值
内部存储在 [[Description]] 属性中,所以只有 Symbol 的toString 方法能读取
而 console.log 隐式调用 toString 方法
let firstName = Symbol('first name')
let person = {}
person[firstName] = 'abc'
console.log(person)
Symbol辨识
通过 typeof,返回值为 symbol
let firstName = Symbol('first name')
console.log(typeof firstName)
Symbol的使用方法
- 对象中括号引用
- 对象字面量
- Object.defineProperty
let firstName = Symbol('first name')
let person_1 = {}
person_1[firstName] = 'person111'
let person_2 = {
[firstName]: 123
}
Object.defineProperty(person_2, firstName, { writable: false })
let lastName = Symbol('last name')
Object.defineProperties(person, {
[lastName]: {
value: 111,
writable: false
}
})
Symbol的共享体系
Symbol.for() 会在 Symbol全局注册表 中查找
- 存在,则返回对应的 Symbol 值
- 不存在,则新创建一个 Symbol 存入注册表
let uid = Symbol.for("uid")
let object = {}
object[uid] = 1234
console.log(object) // { [Symbol(uid)]: 1234 }
let uid2 = Symbol.for('uid')
console.log(uid === uid2) // true
console.log(uid2, object[uid2]) // Symbol(uid) 1234
Symbol.keyFor 在 Symbol全局注册表 中检索 Symbol 相关的键值
let uid = Symbol.for("uid")
console.log(Symbol.keyFor(uid))
let uid2 = Symbol.for("uid")
console.log(Symbol.keyFor(uid2))
let uid3 = Symbol('uid')
console.log(Symbol.keyFor(uid3))
Symbol 的类型转换
- 其他类型的值不存在逻辑等价
- 尤其Number 和 String ,会报错
- String()对象实例化,内部调用了Symbol.toString()
let uid = Symbol.for('uid'),
desc = String(uid) // uid.toString()
console.log(desc) // 'Symbol(uid)'
Symbol 属性检索
ES5中
Object.keys() 可枚举属性
Object.getOwnPropertyNames() 对象的所有属性
以上一律不支持对象中 Symbol 属性
ES6新增 Object.getOwnPropertySymbols 返回对象中的所有 Symbol 属性
let uid = Symbol('uid')
let password = Symbol('password')
let obj = {
a: 123,
b: 'asd',
[uid]: '12',
[password]: 123,
}
// [ 'a', 'b' ]
console.log(Object.getOwnPropertyNames(obj))
// [ Symbol(uid), Symbol(password) ]
console.log(Object.getOwnPropertySymbols(obj))
通过 well-know Symbol 暴露内部操作
Symbol.hasInstance
用于确定对象是否为函数的实例(即 obj instanceof Object)
function Test() {}
Object.defineProperty(Test, Symbol.hasInstance, {
value: function (num) {
return (num instanceof Number) && (num > 0 && num < 100)
}
})
let test = new Number(1)
let test_0 = new Number(0)
console.log(test instanceof Test)
console.log(test_0 instanceof Test)
let a = new Number('123') // 123 === a flase
let b = Number('123') // 123 === b true
a instanceof Number // true
b instanceof Number // false
Symbol.isConcatSpreadable
用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。
标识对象是否具有 length 和 数字键
let a = {
2: '123',
3: 'awe',
'test': 'test',
length: 5,
[Symbol.isConcatSpreadable]: true
}
let arr = ['0', '1', '2']
let newArr = arr.concat(a)
console.log(newArr)
// [ '0', '1', '2', <2 empty items>, '123', 'awe', <1 empty item> ]
match && replace && search && split
-
String.match(reg) 字符串是否匹配正则表达式
-
String.replace(reg, replacement) 字符串匹配正则表达式部分内容替换
-
String.search(reg) 字符串匹配正则表达式索引位置的字符
-
String.split(reg) 字符串以正则表达式符合分割
// 等价于 /^.{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]
}
}
const message_1 = "Hello world", // 11 字符
message_2 = "Hello John" // 10 字符
const match_1 = message_1.match(hasLengthOf10),
match_2 = message_2.match(hasLengthOf10)
print("match:", match_1, match_2)
const replace_1 = message_1.replace(hasLengthOf10, '1'),
replace_2 = message_2.replace(hasLengthOf10, '1')
print("replace: ", replace_1, replace_2)
const search_1 = message_1.replace(hasLengthOf10),
search_2 = message_2.replace(hasLengthOf10)
print("search: ", search_1, search_2)
const split_1 = message_1.split(hasLengthOf10),
split_2 = message_2.split(hasLengthOf10)
print("split: ", split_1, split_1)
function print(str, ...arg) {
console.log(str)
arg.forEach((item, index) => {
console.log(index, item)
})
console.log('-'.repeat(10))
}
Symbol.toPrimitive
会将对象转换为对应的原始值
- 数字模式
- 首先用 valueOf() 方法,如果结果为原始值,则返回
- 其次才是 toString() 方法,如果结果为原始值,则返回
- 报错
- 字符串模式
- 首先用 toString() 方法,如果结果为原始值,则返回
- 其次才是 toString() 方法,如果结果为原始值,则返回
- 报错
- Default模式
- 多数按照Number模式
- Date对象则采用 string 模式
function Template(degrees) {
this.degrees = degrees
}
Template.prototype[Symbol.toPrimitive] = function (hint) {
switch (hint) {
case 'string':
return this.degrees + '\u00b0';
case 'number':
return this.degrees;
case 'default':
return this.degrees + ' degrees'
}
}
const freezing = new Template(32)
console.log(freezing + '!') // "32 degrees!"
console.log(freezing / 2) // 16
console.log(String(freezing)) // "32°"
Symbol.toStringTag
更改 Object.prototype.toString.call 的返回
function Person(name) {
this.name = name
}
Person.prototype[Symbol.toStringTag] = "Person"
let person = new Person('wgf')
console.log(Object.prototype.toString.call(person)) // [object Person]
ES5 之前JSON是由 Douglas Crockfound 的 json2.js 提供
在出现ES默认全局JSON对象后,代码中需要判断 JSON 的来源
function supportsNativeJSON() {
return JSON !== 'undefined' &&
Object.prototype.toString.call(JSON) === '[object JSON]'
}
Symbol.unscropable
由于 ES6 语法向下兼容,解决 with 语句绑定问题
with语句
等价于调用了两次 colors.push()
但是 ES6 数组引入新的方法 values,会导致上述代码歧义
(原意应为:调用 colors 数组外部变量)
var values = [1, 2, 3],
colors = ['red', 'yellow', 'blue'],
color = 'black'
with (colors) {
push(color)
push(...values)
}
console.log(colors)
默认内置
通过手动设置该属性,with 语句将不再创建这些方法的绑定,从而支持老代码的运行
Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), {
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
keys: true,
values: true
})
注意:除非使用 with 语句且正在修改代码库中的已有的代码,否则不要为自己的对象定义 Symbol.unscopable