模板模版字符串使得字符串拼接比以前更容易。 它以反向标记(Tab 正上方那个字符)开始,并且可以使用 ${variable}
插入变量。 比较以下两行代码:
var fName = 'Peter', sName = 'Smith', age = 43, job = 'photographer'; var a = 'Hi, I/'m ' + fName + ' ' + sName + ', I/'m ' + age + ' and work as a ' + job + '.'; var b = `Hi, I'm${ fName }${ sName }, I'm${ age }and work as a${ job }.`;
这会让一切变的更简单,代码更易于阅读。 你可以将任何内容放入花括号中:变量,方程式或函数调用。 我将在整篇文章的示例中使用它们。
JavaScript 本身一直是函数式作用域,这就是我们经常将整个 JavaScript 文件封装在一个空的立即调用函数表达式(IIFE)中的原因。这样做是为了隔离文件中的所有变量,因此全局中就不会存在变量名冲突。
现在,我们有块级作用域和两个绑定到块的新变量声明。
Let
这与 var
类似,但有一些显着差异。由于它声明的变量具有块级作用域,所以可以在不影响外部变量的情况下声明具有相同名称的新局部(内部)变量。
var a = 'car' ; { let a = 5; console.log(a) // 5 } console.log(a) // car
因为它绑定到一个块(即就是 {
和 }
之间的范围),它解决了这个经典的面试问题:“输出是什么,如何让它按照你的期望工作?”
for (var i = 1; i < 5; i++){ setTimeout(()=> { console.log(i); }, 1000); }
在这种情况下,它会输出 “5 5 5 5 5”,因为变量 i
在每次迭代中都会被改变,最终所有结果以最后一次变更为准。
如果您将 var
切换为 let
,那就不一样了。现在,每个循环都会创建一个新的块作用域,其值为绑定到该循环的值。相当于你写了:
{let i = 1; setTimeout(()=> { console.log(i) }, 1000)} {let i = 2; setTimeout(()=> { console.log(i) }, 1000)} {let i = 3; setTimeout(()=> { console.log(i) }, 1000)} {let i = 4; setTimeout(()=> { console.log(i) }, 1000)} {let i = 5; setTimeout(()=> { console.log(i) }, 1000)}
var
和 let
之间的另一个区别是 let
不会像 var
一样被提升。
{ console.log(a); // undefined console.log(b); // ReferenceError var a = 'car'; let b = 5; }
由于其更严格的范围界定和更可预测的行为,有些人表示应该使用 let
而不是 var
,除非特别需要用 var
的声明提升或需要其更宽松的作用范围。
Const
如果你想在 JavaScript 中声明一个常量变量,那么惯例是将变量命名大写。然而,这并不能保证它是一个常量 - 它只是让其他开发人员知道这是一个常量,不应该改变。
现在我们有了 const
声明。
{ const c = "tree"; console.log(c); // tree c = 46; // TypeError! }
const
不会使变量不可变,只是锁定它的赋值。 如果你有一个复杂的赋值(对象或数组),那么该值仍然可以修改。
{ const d = [1, 2, 3, 4]; const dave = { name: 'David Jones', age: 32}; d.push(5); dave.job = "salesman"; console.log(d); // [1, 2, 3, 4, 5] console.log(dave); // { age: 32, job: "salesman", name: 'David Jones'} }
函数声明现在被指定为必须绑定到块级作用域。
{ bar(); // works function bar(){ /* do something */ } } bar(); // doesn't work
当你在 if
语句中声明一个函数时,问题就来了。
考虑这个:
if ( something) { function baz(){ console.log('I passed') } } else { function baz(){ console.log('I didn/'t pass') } } baz();
在 ES6 之前,这两个函数声明都会被提升,结果就是 ‘I didn/’t pass’,不管 something
是什么东西。
现在我们会得到 ‘ReferenceError’,因为 baz()
总是受到块范围的约束。
ES6 引入了 ...
运算符,它被称为“扩展运算符”。 它有两个主要用途:将数组或对象分散到新的数组或对象中,并将多个参数合并到一个数组中。
第一个用例是你可能遇到的最多的用例,所以我们先看看。
let a = [3, 4, 5]; let b = [1, 2, ...a, 6]; console.log(b); // [1, 2, 3, 4, 5, 6] This can be very useful for passing in a set of variables to a function fromanarray. function foo(a, b, c) { console.log(`a=${a}, b=${b}, c=${c}`)} let data = [5, 15, 2]; foo( ...data); // a=5, b=15, c=2
也可以扩展对象,将每个键值对输入到新对象中。(实际上是在提案的第 4 阶段,并将在 ES2018 中正式推出,仅支持 Chrome 60 或更高版本,Firefox 55 或更高版本以及 Node 6.4.0 或更高版本)
let car = { type: 'vehicle ', wheels: 4}; let fordGt = { make: 'Ford', ...car, model: 'GT'}; console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4}
...
运算符的另一个特点是它创建一个新的数组或对象。 下面的例子为 b
创建了一个新的数组,但 c
只是指向同一个数组。
let a = [1, 2, 3]; let b = [ ...a ]; let c = a; b.push(4); console.log(a); // [1, 2, 3] console.log(b); // [1, 2, 3, 4] referencing different arrays c.push(5); console.log(a); // [1, 2, 3, 5] console.log(c); // [1, 2, 3, 5] referencing the same array
第二个例子是将变量一起收集到一个数组中。 当你不知道有多少变量传递给函数时,这非常有用。
function foo(...args){ console.log(args); } foo( 'car', 54, 'tree'); // [ 'car', 54, 'tree' ]
现在可以使用默认参数定义函数。缺少或未定义的值将使用默认值进行初始化。只要小心 - 因为空值和假值会被强制为0。
function foo(a =5, b =10){ console.log( a + b); } foo(); // 15 foo( 7, 12 ); // 19 foo( undefined, 8 ); // 13 foo( 8 ); // 18 foo( null ); // 10 as null is coerced to 0
默认值可以不仅仅是值 - 它们也可以是表达式或函数。
function foo(a){ return a * 4; } function bar(x =2, y = x +4, z = foo(x)){ console.log([ x, y, z ]); } bar(); // [ 2, 6, 8 ] bar( 1, 2, 3 ); //[ 1, 2, 3 ] bar( 10, undefined, 3 ); // [ 10, 14, 3 ]
解构是拆分等号左侧的数组或对象的过程。数组或对象可以来自变量,函数或等式。
let [ a, b, c ] = [ 6, 2, 9]; console.log(`a=${a}, b=${b}, c=${c}`); //a=6, b=2, c=9 function foo(){ return ['car', 'dog', 6 ]; } let [ x, y, z ] = foo(); console.log(`x=${x}, y=${y}, z=${z}`); // x=car, y=dog, z=6
通过对象解构,可以在大括号内列出对象的键以提取该键值对。
function bar(){ return {a: 1, b: 2, c: 3}; } let { a, c } = bar(); console.log(a); // 1 console.log(c); // 3 console.log(b); // undefined
Sometimes, you want to extract the values but assign them to a new variable. This is done using a ‘key: variable’ pairing on the left of the equals sign.
有时,你想取值,将它们分配给一个新的变量。 这是通过在等号左边的 key: variable
配对完成的。
function baz(){ return { x: 'car', y: 'London', z: { name: 'John', age: 21} }; } let { x: vehicle, y: city, z: { name: driver } } = baz(); console.log( `I'm going to${city}with${driver}in their${vehicle}.` ); // I'm going to London with John in their car.
对象解构允许的另一件事是为多个变量赋值。
let { x: first, x: second } = { x: 4 }; console.log( first, second ); // 4, 4
当您从变量创建对象字面量时,ES6 允许您在与 key 与变量名称相同的情况下省略 key 名。
let a = 4, b = 7; let c = { a: a, b: b }; let concise = { a, b }; console.log(c, concise) // {a: 4, b: 7}, {a: 4, b: 7}
这也可以与解构结合使用,使你的代码更简单,更清洁。
function foo(){ return { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' } }; } // pre ES6 let a = foo(), name = a.name, age = a.age, company = a.job.company; // ES6 destructuring and concise parameters let { name, age, job: {company}} = foo();
它也可以用来解构对象传递给函数。方法 1 和 2 是你在 ES6 之前完成它的方法,方法 3 使用解构和简明的参数。
let person = { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' } }; // method 1 function old1(person){ var yearOfBirth = 2018 - person.age; console.log( `${ person.name }works at${ person.job.company }and was born in${ yearOfBirth }.`); } // method 2 function old1(person){ var age = person.age, yearOfBirth = 2018 - age, name = person.name, company = person.job.company; console.log( `${ name }works at${ company }and was born in${ yearOfBirth }.`); } // method 3 function es6({ age, name, job: {company}}){ var yearOfBirth = 2018 - age; console.log( `${ name }works at${ company }and was born in${ yearOfBirth }.`); }
使用 ES6,我们可以提取 age
, name
和 company
而无需额外的变量声明。
ES6 添加了使用动态分配的 key 创建或添加属性的功能。
let city= 'sheffield_'; let a = { [ city + 'population' ]: 350000 }; a[ city + 'county' ] = 'South Yorkshire'; console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }
箭头函数有两个主要方面:结构和 this
绑定。
它们可以具有比传统功能更简单的结构,因为它们不需要功能关键字,并且它们自动返回箭头之后的任何内容。
var foo = function(a, b){ return a * b; } let bar = (a, b) => a * b;
如果函数需要的不仅仅是一个简单的计算,可以使用大括号,并且该函数会返回花括号块范围返回的任何内容。
let baz = (c, d) => { let length = c.length + d.toString().length; let e = c.join(', '); return `${e}and there is a total length of${length}`; }
对于箭头函数最有用的地方之一是在 map()
, forEach()
或 sort()
之类的数组函数中。
let arr = [ 5, 6, 7, 8, 'a' ]; let b = arr.map( item=> item + 3 ); console.log(b); // [ 8, 9, 10, 11, 'a3' ]
除了具有更短的语法外,它还修复了在绑定行为经常出现的问题。具有 ES6 之前的功能的修复是将 this
引用存储,通常作为自变量存储。
var clickController = { doSomething: function (..){ var self = this; btn.addEventListener( 'click', function(){ self.doSomething(..) }, False ); } };
这必须做到,因为这个绑定是动态的。 这意味着这个事件监听器里面的这个和 doSomething
里面的这个不是指同一个事物。
在箭头函数里,这个绑定是词法的,而不是动态的。 这是箭头功能的主要设计特征。
虽然这种绑定可能是伟大的,有时这不是想要的。
let a = { oneThing: (a) => { let b = a * 2; this.otherThing(b); }, otherThing: (b) => {....} }; a.oneThing(6);
当我们使用 a.oneThing(6)
时, this.otherThing(b)
引用失败,因为这不是指向一个对象,而是指向周围的作用域。 如果您使用 ES6 语法重写遗留代码,则需要注意这一点。
ES6 增加了一种迭代数组中每个值的方法。 这与现有的 ... in
不同,它循环使用 key/index
。
let a = ['a', 'b', 'c', 'd' ]; // ES6 for ( var val of a ) { console.log( val ); } // "a" "b" "c" "d" // pre-ES6 for ( var idx in a ) { console.log( idx ); } // 0 1 2 3
Using the new for … of loop saves adding a let val = a[idx] inside each loop.
Arrays, strings, generators and collections are all iterable in standard JavaScript. Plain objects can’t normally be iterated over, unless you have defined an iterator for it.
使用新的 for ... of
循环,不必在在每个循环内添加一个 let val = a[idx]
。
数组,字符串,生成器和集合都可以在标准 JavaScript 中迭代。普通对象通常不能迭代,除非你已经为它定义了一个迭代器(Iterator)。
ES5 代码很好地处理了十进制和十六进制数字格式,但未指定八进制格式。事实上,它在严格的模式下被禁止。
ES6添加了一种新格式,在最初的 0 之后添加一个 o (注意是字母)以将该数字声明为八进制数。ES6 还添加了二进制格式。
Number( 29 ) // 29 Number( 035 ) // 35 in old octal form. Number( 0o35 ) // 29 in new octal form Number( 0x1d ) // 29 in hexadecimal Number( 0b11101 ) // 29 in binary form
ES6 为我们提供了更多的功能,使我们的代码更简洁,更短,更易于阅读和更强大。 我的目标是撰写本文的续篇,内容涵盖了 ES6 中不太知名的部分。