ES6新增常用属性


1.声明变量

let/const

let:用来声明变量,类似于var,但是使用let有以下特性:

(1).不存在变量提升

eg:

 

// var 的情况 console.log(foo); // 输出undefined var foo = 2;  // let 的情况 console.log(bar); // 报错ReferenceError let bar = 2;

 

(2).暂时性死区

eg:

 

var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; }

 

(3).不允许重复声明,同一作用域内只能声明一次

eg:

 

// 报错 function func() { let a = 10; var a = 1; }  // 报错 function func() { let a = 10; let a = 1; }

因此,不能在函数内部重新声明参数。
function func(arg) { let arg; } func() // 报错 function func(arg) { { let arg; } } func() // 不报错

 

(4).新增的块级作用域

 

function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }

上面的函数有两个代码块,都声明了变量n,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是 10。

 

const:

用来声明常量,使用规则除了声明之后不能被修改,其他特性和let一样,本质上是const是内存地址不得改动,而不是变量的值不能改动

 

const声明一个只读的常量。一旦声明,常量的值就不能改变。

 

const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable. 

 

上面代码表明改变常量的值会报错。

 

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

 

const foo; // SyntaxError: Missing initializer in const declaration 

 

上面代码表示,对于const来说,只声明不赋值,就会报错。

 

const的作用域与let命令相同:只在声明所在的块级作用域内有效。

 

if (true) { const MAX = 5; } MAX // Uncaught ReferenceError: MAX is not defined 

 

const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

 

if (true) { console.log(MAX); // ReferenceError const MAX = 5; } 

 

上面代码在常量MAX声明之前就调用,结果报错。

 

const声明的常量,也与let一样不可重复声明。

 

var message = "Hello!"; let age = 25;  // 以下两行都会报错 const message = "Goodbye!"; const age = 30; 

 

 

 

2.字符串扩展

 

(1).codePointAt()

ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。

(2).fromCodePoint()

ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符,弥补了String.fromCharCode()方法的不足。在作用上,正好与上面的codePointAt()方法相反。注意,fromCodePoint方法定义在String对象上,而codePointAt方法定义在字符串的实例对象上。

(3).repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

(4).indexof()补充includes() startswith() endswith()

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

3.字符串的正则方法

字符串对象共有 4 个方法,可以使用正则表达式:match()replace()search()split()

ES6 将这 4 个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。

  • String.prototype.match 调用 RegExp.prototype[Symbol.match]
  • String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
  • String.prototype.search 调用 RegExp.prototype[Symbol.search]
  • String.prototype.split 调用 RegExp.prototype[Symbol.split]

u 修饰符

 

ES6 对正则表达式添加了u修饰符,含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

 

/^\uD83D/u.test('\uD83D\uDC2A') // false /^\uD83D/.test('\uD83D\uDC2A') // true 

 

上面代码中,\uD83D\uDC2A是一个四个字节的 UTF-16 编码,代表一个字符。但是,ES5 不支持四个字节的 UTF-16 编码,会将其识别为两个字符,导致第二行代码结果为true。加了u修饰符以后,ES6 就会识别其为一个字符,所以第一行代码结果为false

 

一旦加上u修饰符号,就会修改下面这些正则表达式的行为。

 

(1)点字符

 

点(.)字符在正则表达式中,含义是除了换行符以外的任意单个字符。对于码点大于0xFFFF的 Unicode 字符,点字符不能识别,必须加上u修饰符。

 

var s = '𠮷'; /^.$/.test(s) // false /^.$/u.test(s) // true 

 

上面代码表示,如果不添加u修饰符,正则表达式就会认为字符串为两个字符,从而匹配失败。

 

(2)Unicode 字符表示法

 

ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表达式中必须加上u修饰符,才能识别当中的大括号,否则会被解读为量词。

 

/\u{61}/.test('a') // false /\u{61}/u.test('a') // true /\u{20BB7}/u.test('𠮷') // true 

 

上面代码表示,如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配 61 个连续的u

 

(3)量词

 

使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的 Unicode 字符。

 

/a{2}/.test('aa') // true /a{2}/u.test('aa') // true /𠮷{2}/.test('𠮷𠮷') // false /𠮷{2}/u.test('𠮷𠮷') // true 

 

(4)预定义模式

 

u修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF的 Unicode 字符。

 

/^\S$/.test('𠮷') // false /^\S$/u.test('𠮷') // true 

 

上面代码的\S是预定义模式,匹配所有非空白字符。只有加了u修饰符,它才能正确匹配码点大于0xFFFF的 Unicode 字符。

 

利用这一点,可以写出一个正确返回字符串长度的函数。

 

function codePointLength(text) { var result = text.match(/[\s\S]/gu); return result ? result.length : 0; } var s = '𠮷𠮷'; s.length // 4 codePointLength(s) // 2 

 

(5)i 修饰符

 

有些 Unicode 字符的编码不同,但是字型很相近,比如,\u004B\u212A都是大写的K

 

/[a-z]/i.test('\u212A') // false /[a-z]/iu.test('\u212A') // true 

 

上面代码中,不加u修饰符,就无法识别非规范的K字符。

y 修饰符

除了u修饰符,ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。

y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

var s = 'aaa_aa_a'; var r1 = /a+/g; var r2 = /a+/y; r1.exec(s) // ["aaa"] r2.exec(s) // ["aaa"] r1.exec(s) // ["aa"] r2.exec(s) // null 

上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null

如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。

var s = 'aaa_aa_a'; var r = /a+_/y; r.exec(s) // ["aaa_"] r.exec(s) // ["aa_"] 

上面代码每次匹配,都是从剩余字符串的头部开始。

使用lastIndex属性,可以更好地说明y修饰符。

const REGEX = /a/g;  // 指定从2号位置(y)开始匹配 REGEX.lastIndex = 2;  // 匹配成功 const match = REGEX.exec('xaya');  // 在3号位置匹配成功 match.index // 3  // 下一次匹配从4号位开始 REGEX.lastIndex // 4  // 4号位开始匹配失败 REGEX.exec('xaya') // null 

上面代码中,lastIndex属性指定每次搜索的开始位置,g修饰符从这个位置开始向后搜索,直到发现匹配为止。

y修饰符同样遵守lastIndex属性,但是要求必须在lastIndex指定的位置发现匹配。

const REGEX = /a/y;  // 指定从2号位置开始匹配 REGEX.lastIndex = 2;  // 不是粘连,匹配失败 REGEX.exec('xaya') // null  // 指定从3号位置开始匹配 REGEX.lastIndex = 3;  // 3号位置是粘连,匹配成功 const match = REGEX.exec('xaya'); match.index // 3 REGEX.lastIndex // 4 

实际上,y修饰符号隐含了头部匹配的标志^

/b/y.exec('aba') // null 

上面代码由于不能保证头部匹配,所以返回nully修饰符的设计本意,就是让头部匹配的标志^在全局匹配中都有效。

下面是字符串对象的replace方法的例子。

const REGEX = /a/gy; 'aaxa'.replace(REGEX, '-') // '--xa' 

上面代码中,最后一个a因为不是出现在下一次匹配的头部,所以不会被替换。

单单一个y修饰符对match方法,只能返回第一个匹配,必须与g修饰符联用,才能返回所有匹配。

'a1a2a3'.match(/a\d/y) // ["a1"] 'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"] 

y修饰符的一个应用,是从字符串提取 token(词元),y修饰符确保了匹配之间不会有漏掉的字符。

const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y; const TOKEN_G = /\s*(\+|[0-9]+)\s*/g; tokenize(TOKEN_Y, '3 + 4') // [ '3', '+', '4' ] tokenize(TOKEN_G, '3 + 4') // [ '3', '+', '4' ] function tokenize(TOKEN_REGEX, str) { let result = []; let match; while (match = TOKEN_REGEX.exec(str)) { result.push(match[1]); } return result; } 

上面代码中,如果字符串里面没有非法字符,y修饰符与g修饰符的提取结果是一样的。但是,一旦出现非法字符,两者的行为就不一样了。

tokenize(TOKEN_Y, '3x + 4') // [ '3' ] tokenize(TOKEN_G, '3x + 4') // [ '3', '+', '4' ] 

上面代码中,g修饰符会忽略非法字符,而y修饰符不会,这样就很容易发现错误。

数组的解构赋值

基本用法

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

以前,为变量赋值,只能直接指定值。

let a = 1; let b = 2; let c = 3; 

ES6 允许写成下面这样。

let [a, b, c] = [1, 2, 3]; 

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [ , , third] = ["foo", "bar", "baz"]; third // "baz" let [x, , y] = [1, 2, 3]; x // 1 y // 3 let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined z // [] 

如果解构不成功,变量的值就等于undefined

let [foo] = []; let [bar, foo] = [1]; 

以上两种情况都属于解构不成功,foo的值都会等于undefined

另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

let [x, y] = [1, 2, 3]; x // 1 y // 2 let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4 

上面两个例子,都属于不完全解构,但是可以成功。

如果等号的右边不是数组(或者严格地说,不是可遍历的结构,参见《Iterator》一章),那么将会报错。

// 报错 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {}; 

上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。

对于 Set 结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']); x // "a" 

事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

function* fibs() { let a = 0; let b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } let [first, second, third, fourth, fifth, sixth] = fibs(); sixth // 5 

上面代码中,fibs是一个 Generator 函数(参见《Generator 函数》一章),原生具有 Iterator 接口。解构赋值会依次从这个接口获取值。

默认值

解构赋值允许指定默认值。

let [foo = true] = []; foo // true let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b' 

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null 

上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() { console.log('aaa'); } let [x = f()] = [1]; 

上面代码中,因为x能取到值,所以函数f根本不会执行。上面的代码其实等价于下面的代码。

let x; if ([1][0] === undefined) { x = f(); } else { x = [1][0]; } 

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];  // x=1; y=1 let [x = 1, y = x] = [2];  // x=2; y=2 let [x = 1, y = x] = [1, 2]; // x=1; y=2 let [x = y, y = 1] = [];  // ReferenceError: y is not defined 

上面最后一个表达式之所以会报错,是因为xy做默认值时,y还没有声明。

对象的解构赋值

简介

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb" 

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb" let { baz } = { foo: 'aaa', bar: 'bbb' }; baz // undefined 

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined

如果解构失败,变量的值等于undefined

let {foo} = {bar: 'baz'}; foo // undefined 

上面代码中,等号右边的对象没有foo属性,所以变量foo取不到值,所以等于undefined

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。

// 例一 let { log, sin, cos } = Math;  // 例二 const { log } = console; log('hello') // hello 

上面代码的例一将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。例二将console.log赋值到log变量。

如果变量名与属性名不一致,必须写成下面这样。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world' 

这实际上说明,对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。

let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' }; 

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // error: foo is not defined 

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

与数组一样,解构也可以用于嵌套结构的对象。

let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p: [x, { y }] } = obj; x // "Hello" y // "World" 

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p, p: [x, { y }] } = obj; x // "Hello" y // "World" p // ["Hello", {y: "World"}] 

下面是另一个例子。

const node = { loc: { start: { line: 1, column: 5 } } }; let { loc, loc: { start }, loc: { start: { line }} } = node; line // 1 loc  // Object {start: Object} start // Object {line: 1, column: 5} 

上面代码有三次解构赋值,分别是对locstartline三个属性的解构赋值。注意,最后一次对line属性的解构赋值之中,只有line是变量,locstart都是模式,不是变量。

下面是嵌套赋值的例子。

let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true] 

如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。

// 报错 let {foo: {bar}} = {baz: 'baz'}; 

上面代码中,等号左边对象的foo属性,对应一个子对象。该子对象的bar属性,解构时会报错。原因很简单,因为foo这时等于undefined,再取子属性就会报错。

注意,对象的解构赋值可以取到继承的属性。

const obj1 = {}; const obj2 = { foo: 'bar' }; Object.setPrototypeOf(obj1, obj2); const { foo } = obj1; foo // "bar" 

上面代码中,对象obj1的原型对象是obj2foo属性不是obj1自身的属性,而是继承自obj2的属性,解构赋值可以取到这个属性。

默认值

对象的解构也可以指定默认值。

var {x = 3} = {}; x // 3 var {x, y = 5} = {x: 1}; x // 1 y // 5 var {x: y = 3} = {}; y // 3 var {x: y = 3} = {x: 5}; y // 5 var { message: msg = 'Something went wrong' } = {}; msg // "Something went wrong" 

默认值生效的条件是,对象的属性值严格等于undefined

var {x = 3} = {x: undefined}; x // 3 var {x = 3} = {x: null}; x // null 

上面代码中,属性x等于null,因为nullundefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。

注意点

(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法 let x; {x} = {x: 1}; // SyntaxError: syntax error 

上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法 let x; ({x} = {x: 1}); 

上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。关于圆括号与解构赋值的关系,参见下文。

(2)解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。

({} = [true, false]); ({} = 'abc'); ({} = []); 

上面的表达式虽然毫无意义,但是语法是合法的,可以执行。

(3)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; first // 1 last // 3 

上面代码对数组进行对象解构。数组arr0键对应的值是1[arr.length - 1]就是2键,对应的值是3。方括号这种写法,属于“属性名表达式”(参见《对象的扩展》一章)。

字符串的解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" 

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello'; len // 5 

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123; s === Number.prototype.toString // true let {toString: s} = true; s === Boolean.prototype.toString // true 

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError let { prop: y } = null; // TypeError 

函数参数的解构赋值

函数的参数也可以使用解构赋值。

function add([x, y]){ return x + y; } add([1, 2]); // 3 

上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量xy。对于函数内部的代码来说,它们能感受到的参数就是xy

下面是另一个例子。

[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ] 

函数参数的解构也可以使用默认值。

function move({x = 0, y = 0} = {}) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, 0] move({}); // [0, 0] move(); // [0, 0] 

上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量xy的值。如果解构失败,xy等于默认值。

注意,下面的写法会得到不一样的结果。

function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0] 

上面代码是为函数move的参数指定默认值,而不是为变量xy指定默认值,所以会得到与前一种写法不同的结果。

undefined就会触发函数参数的默认值。

[1, undefined, 3].map((x = 'yes') => x); // [ 1, 'yes', 3 ]
Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

let s = Symbol(); typeof s // "symbol" 

上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是 Symbol 数据类型,而不是字符串之类的其他类型。

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

let s1 = Symbol('foo'); let s2 = Symbol('bar'); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)" 

上面代码中,s1s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。

如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

const obj = { toString() { return 'abc'; } }; const sym = Symbol(obj); sym // Symbol(abc) 

注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况 let s1 = Symbol(); let s2 = Symbol(); s1 === s2 // false  // 有参数的情况 let s1 = Symbol('foo'); let s2 = Symbol('foo'); s1 === s2 // false 

上面代码中,s1s2都是Symbol函数的返回值,而且参数相同,但是它们是不相等的。

Symbol 值不能与其他类型的值进行运算,会报错。

let sym = Symbol('My symbol'); "your symbol is " + sym // TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string 

但是,Symbol 值可以显式转为字符串。

let sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)' 

另外,Symbol 值也可以转为布尔值,但是不能转为数值。

let sym = Symbol(); Boolean(sym) // true !sym  // false if (sym) {  // ... } Number(sym) // TypeError sym + 2 // TypeError

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM