// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
实际运行如下:// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
注意:
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,应写成函数表达式。
ES6的块级作用域必须有大括号
// 第一种写法,报错 没有大括号,所以不存在块级作用域,而let只能出现在当前作用域的顶层
if (true) let x = 1;
// 第二种写法,不报错
if (true) {
let x = 1;
}
函数声明也是如此,严格模式下,函数只能声明在当前作用域的顶层。
Object.freeze()
方法。const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
除了将对象本身冻结,对象的属性也应该冻结:var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
var
和function
。ES6除了let
和const
,还有import
和class
。window
对象,在Node中指global
对象。ES5中,顶层对象的属性和全局变量是等价的。但是在ES6中,var
function
声明的全局变量依旧是顶层对象的属性,而 let
const
class
声明的全局变量不属于顶层对象的属性。var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
window
,self
, frames
, 全局环境中的this
, 函数不作为对象的方法运行,而是单纯作为函数运行,this
会指向顶层对象,但是严格模式下,返回undefined
self
global
globalThis
作为顶层对象,任何环境下都可以通过它拿到顶层对象;===
,来判断一个位置是否有值。只有严格等于undefined
,默认值才会生效。undefined
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 x;
{x} = {x: 1};
// SyntaxError: syntax error
// 正确的写法
let x;
({x} = {x: 1});
({} = [true, false]);
({} = 'abc');
({} = []);
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
length
属性,因此还可以对这个属性解构赋值const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5
undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。Unicode
的支持,允许采用\uxxxx
形式表示一个字符,其中xxxx
表示字符的Unicode码点;'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
iterator
,使得字符串可以被for ... of
循环遍历for
循环无法识别这样的码点let text = String.fromCodePoint(0x20BB7);
//for循环会认为它包含两个字符(都不可打印)
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
for (let i of text) {
console.log(i);
}
// ""
JSON.stringify()
方法有可能返回不符合UTF-8标准的字符串。UTF-8标准规定,0xD800
到0xDFFF
之间的码点,不能单独使用,必须配对使用;比如,\uD834\uDF06
是两个码点,但是必须放在一起配对使用,代表字符
。这是为了表示码点大于0xFFFF
的字符的一种变通方法。JSON.stringify()
的问题在于,它可能返回0xD800
到0xDFFF
之间的单个码点。为了确保返回的是合法的UTF-8字符,ES2019改变了JSON.stringify()
的行为。如果遇到0xD800
到0xDFFF
之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。·JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
String.fromCharCode()
方法,用于从Unicode
码点返回对应字符,但是这个方法不能识别码点大于0xFFFF
的字符;String.fromCodePoint()
方法,可识别大于0xFFFF
的字符;indexOf
方法,用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新的方法:
normalize()
includes()
startsWith()
endsWith()
repeat()
:返回一个新的字符串,将原字符串重复n次padStart()
用于头部补全,padEnd()
用于尾部补全;'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
trimStart()
和trimEnd()
方法;它们的行为与trim()
一致,trimStart()
消除字符串头部的空格,trimEnd()
消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。trimLeft()
是trimStart()
的别名,trimRight()
是trimEnd()
的别名。matchAll()
方法返回一个正则表达式在当前字符串的所有匹配;