如何使用ES6编写更清洁的代码

分类:前端技巧 热度:

模板字面量(Template Literals)

模板字面量使得使用字符串比以前更容易。它们以反引号开始,可以使用${variable}插入变量。比较这两行代码:


JavaScript 代码:
  1. var fName = 'Peter', sName = 'Smith', age = 43, job= 'photographer';
  2. var a = 'Hi, I\'m ' + fName + ' ' + sName + ', I\'m ' + age + ' and work as a ' + job + '.';
  3. var b = `Hi, I'm ${ fName } ${ sName }, I'm ${ age } and work as a ${ job }.`;

这使得工作更简单,代码更易于阅读。 你可以将任何内容放入花括号中:变量,方程式或函数调用。 我将在整篇文章的示例中使用它们。

块作用域语法(Syntax Block scoping)

JavaScript 一直被函数作用域所限制,这就是为什么将整个 JavaScript 文件包装在一个空的立即调用函数表达式(IIFE) 中的原因。这样做是为了隔离文件中的所有变量,所以不会引起变量冲突。

现在,我们有块作用域和两个绑定到块的新变量声明。

let 声明

这与 var 类似,但有一些显着差异。由于它是块作用域,可以在不影响外部变量的情况下声明具有相同名称的新变量。


JavaScript 代码:
  1. var a = 'car' ;
  2. {
  3. let a = 5;
  4. console.log(a) // 5
  5. }
  6. console.log(a) // car

因为它绑定到一个块作用域中,它解决了这个经典的面试题:“输出是什么,如何让它按照你的期望工作?”


JavaScript 代码:
  1. for (var i = 1; i < 5; i++){
  2. setTimeout(() => { console.log(i); }, 1000);
  3. }

在这种情况下,它会输出 “5 5 5 5” ,因为变量 i 在每次迭代中都会改变。

如果您将 var 换成 let ,则所有内容都会更改。现在,每个循环都会创建一个新的块作用域,其值为绑定到该循环的值。就好比你写成这样:


JavaScript 代码:
  1. {let i = 1; setTimeout(() => { console.log(i) }, 1000)}
  2. {let i = 2; setTimeout(() => { console.log(i) }, 1000)}
  3. {let i = 3; setTimeout(() => { console.log(i) }, 1000)}
  4. {let i = 4; setTimeout(() => { console.log(i) }, 1000)}

var 和 let 之间的另一个区别是 let 不会像 var 一样被提升。


JavaScript 代码:
  1. {
  2. console.log(a); // undefined
  3. console.log(b); // ReferenceError
  4. var a = 'car';
  5. let b = 5;
  6. }

由于其更紧凑的作用域和更可预测的行为,有人说你应该用 let 来代替 var ,除非你特别需要 var 声明的提升或更宽松的作用域。

const 声明

如果你想要在 JavaScript 中声明一个常量变量,以前的惯例可以用块大写来命名变量。但是,这不会保证变量的安全 – 它只是让其他开发人员知道它是一个常量,不应该更改。

现在我们可以使用 const 来声明。


JavaScript 代码:
  1. {
  2. const c = "tree";
  3. console.log(c); // tree
  4. c = 46; // TypeError!
  5. }

const 不会使变量不可变,只是锁定它的赋值。如果你有一个复杂的赋值(对象或数组),那么该值仍然可以修改。


JavaScript 代码:
  1. {
  2. const d = [1, 2, 3, 4];
  3. const dave = { name: 'David Jones', age: 32};
  4. d.push(5);
  5. dave.job = "salesman";
  6. console.log(d); // [1, 2, 3, 4, 5]
  7. console.log(dave); // { age: 32, job: "salesman", name: 'David Jones'}
  8. }

块作用域中函数的问题

函数声明现在被指定为绑定到块作用域。


JavaScript 代码:
  1. {
  2. bar(); // works
  3. function bar() { /* do something */ }
  4. }
  5. bar(); // doesn't work

当你在 if 语句中声明一个函数时,问题就来了。

考虑这个:


JavaScript 代码:
  1. if ( something) {
  2. function baz() { console.log('I passed') }
  3. } else {
  4. function baz() { console.log('I didn\'t pass') }
  5. }
  6. baz();

在 ES6 之前,这两个函数声明都会被提升,不管 something 是什么,结果就是 'I didn\'t pass'。 现在我们得到'ReferenceError'(引用错误),因为 baz 总是受到块作用域的约束。

展开操作符(Spread)

ES6引入了 ... 操作符,它被称为 “展开操作符”。 它有两个主要用途:将数组或对象传播到新的数组或对象中,并将多个参数合并到一个数组中。

第一个用例是你可能遇到的最多的用例,所以我们先看看。


JavaScript 代码:
  1. let a = [3, 4, 5];
  2. let b = [1, 2, ...a, 6];
  3. console.log(b); // [1, 2, 3, 4, 5, 6]

这对于将数组中的元素作为一组变量传递给函数时非常有用。


JavaScript 代码:
  1. function foo(a, b, c) { console.log(`a=${a}, b=${b}, c=${c}`)}
  2. let data = [5, 15, 2];
  3. foo( ...data); // a=5, b=15, c=2

它也可以展开对象,将每个键值对输入到新对象中。 (对象展开实际上是在提案的第4阶段,并将在 ES2018 中正式推出,目前仅支持 Chrome 60 或更高版本,Firefox 55 或更高版本 ,以及 node 6.4.0 或更高版本)


JavaScript 代码:
  1. let car = { type: 'vehicle ', wheels: 4};
  2. let fordGt = { make: 'Ford', ...car, model: 'GT'};
  3. console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4}

展开操作符的另一个特点是它创建一个新的数组或对象。下面的例子为 b 创建了一个新的数组,但 c 只是指向同一个数组。


JavaScript 代码:
  1. let a = [1, 2, 3];
  2. let b = [ ...a ];
  3. let c = a;
  4. b.push(4);
  5. console.log(a); // [1, 2, 3]
  6. console.log(b); // [1, 2, 3, 4] 引用不同的数组
  7. c.push(5);
  8. console.log(a); // [1, 2, 3, 5]
  9. console.log(c); // [1, 2, 3, 5] 引用相同的数组

第二个用例是将变量一起收集到一个数组中。当你不知道有多少变量传递给函数时,这非常有用。


JavaScript 代码:
  1. function foo(...args) {
  2. console.log(args);
  3. }
  4. foo( 'car', 54, 'tree'); // [ 'car', 54, 'tree' ]

默认参数

现在函数定义时可以使用默认参数。缺少或未定义的值将使用默认值进行初始化。需要特别注意的是: null 和 false 值被强制为 0 。


JavaScript 代码:
  1. function foo( a = 5, b = 10) {
  2. console.log( a + b);
  3. }
  4. foo(); // 15
  5. foo( 7, 12 ); // 19
  6. foo( undefined, 8 ); // 13
  7. foo( 8 ); // 18
  8. foo( null ); // 10 as null is coerced to 0

默认值可以不仅仅是值 – 它们也可以是表达式或函数。


JavaScript 代码:
  1. function foo( a ) { return a * 4; }
  2. function bar( x = 2, y = x + 4, z = foo(x)) {
  3. console.log([ x, y, z ]);
  4. }
  5. bar(); // [ 2, 6, 8 ]
  6. bar( 1, 2, 3 ); //[ 1, 2, 3 ]
  7. bar( 10, undefined, 3 ); // [ 10, 14, 3 ]

解构(Destructuring)

解构是拆分等号左侧的数组或对象的过程。数组或对象可以来自变量,函数或等式。


JavaScript 代码:
  1. let [ a, b, c ] = [ 6, 2, 9];
  2. console.log(`a=${a}, b=${b}, c=${c}`); //a=6, b=2, c=9
  3. function foo() { return ['car', 'dog', 6 ]; }
  4. let [ x, y, z ] = foo();
  5. console.log(`x=${x}, y=${y}, z=${z}`); // x=car, y=dog, z=6

通过对象解构,可以在大括号内列出对象的键以提取该键值对。


JavaScript 代码:
  1. function bar() { return {a: 1, b: 2, c: 3}; }
  2. let { a, c } = bar();
  3. console.log(a); // 1
  4. console.log(c); // 3
  5. console.log(b); // undefined

有时,你想提取值,但将它们分配给一个新的变量。这是通过在等号左边的 ‘key: variable’ 配对完成的。


JavaScript 代码:
  1. function baz() {
  2. return {
  3. x: 'car',
  4. y: 'London',
  5. z: { name: 'John', age: 21}
  6. };
  7. }
  8. let { x: vehicle, y: city, z: { name: driver } } = baz();
  9. console.log(
  10. `I'm going to ${city} with ${driver} in their ${vehicle}.`
  11. ); // I'm going to London with John in their car.

另外需要特别注意的是,对象解构允许为多个变量赋值。


JavaScript 代码:
  1. let { x: first, x: second } = { x: 4 };
  2. console.log( first, second ); // 4, 4

对象字面量和简写模式

当你从变量创建对象字面量时,ES6 允许你在与变量名称相同的情况下省略该 key 。


JavaScript 代码:
  1. let a = 4, b = 7;
  2. let c = { a: a, b: b };
  3. let concise = { a, b };
  4. console.log(c, concise) // {a: 4, b: 7}, {a: 4, b: 7}

这也可以与解构结合使用,使你的代码更简单,更清洁。


JavaScript 代码:
  1. function foo() {
  2. return {
  3. name: 'Anna',
  4. age: 56,
  5. job: { company: 'Tesco', title: 'Manager' }
  6. };
  7. }
  8. // ES6 前
  9. let a = foo(), name = a.name, age = a.age, company = a.job.company;
  10. // ES6 结构和对象字面量简写模式
  11. let { name, age, job: {company}} = foo();

它也可以用来解构传递给函数的对象。方法 1 和 2 是你在 ES6 之前完成它的方法,方法3使用解构和对象字面量简写模式 。


JavaScript 代码:
  1. let person = {
  2. name: 'Anna',
  3. age: 56,
  4. job: { company: 'Tesco', title: 'Manager' }
  5. };
  6. // 方法 1
  7. function old1( person) {
  8. var yearOfBirth = 2018 - person.age;
  9. console.log( `${ person.name } works at ${ person.job.company } and was born in ${ yearOfBirth }.`);
  10. }
  11. // 方法 2
  12. function old1( person) {
  13. var age = person.age,
  14. yearOfBirth = 2018 - age,
  15. name = person.name,
  16. company = person.job.company;
  17. console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`);
  18. }
  19. // 方法 3
  20. function es6({ age, name, job: {company}}) {
  21. var yearOfBirth = 2018 - age;
  22. console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`);
  23. }

使用 ES6 ,我们可以提取 age,name 和 company 而无需额外的变量声明。

动态属性名称

ES6 添加了使用动态分配的 key 来创建或添加属性的功能。


JavaScript 代码:
  1. let city= 'sheffield_';
  2. let a = {
  3. [ city + 'population' ]: 350000
  4. };
  5. a[ city + 'county' ] = 'South Yorkshire';
  6. console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }

箭头函数(Arrow Functions)

箭头函数有两个主要方面:结构和 this 绑定。

它们可以具有比传统函数更简单的结构,因为它们不需要 function 关键字,并且它们会自动返回箭头之后的任何内容。


JavaScript 代码:
  1. var foo = function( a, b ) {
  2. return a * b;
  3. }
  4. let bar = ( a, b ) => a * b;

如果函数需要的不仅仅是一个简单的计算,那么你可以使用大括号,并且该函数可以返回块作用域中任何内容。


JavaScript 代码:
  1. let baz = ( c, d ) => {
  2. let length = c.length + d.toString().length;
  3. let e = c.join(', ');
  4. return `${e} and there is a total length of ${length}`;
  5. }

对于箭头函数最有用的地方之一是在 .map,.forEach 或 .sort 之类的数组函数中。


JavaScript 代码:
  1. et arr = [ 5, 6, 7, 8, 'a' ];
  2. let b = arr.map( item => item + 3 );
  3. console.log(b); // [ 8, 9, 10, 11, 'a3' ]

除了具有更短的语法外,它还修复了其周围 this 绑定行为经常出现的问题。 ES6 之前的函数修复这个问题是将 this 引用存储为 self 变量。


JavaScript 代码:
  1. var clickController = {
  2. doSomething: function (..) {
  3. var self = this;
  4. btn.addEventListener(
  5. 'click',
  6. function() { self.doSomething(..) },
  7. False
  8. );
  9. }
  10. };

ES6 之前必须这么做,因为 this 的绑定是动态的。这意味着事件监听器里面的 this 和 doSomething 里面的 this 不是指向同一个事物。

在箭头函数里, this 是词法绑定的,而不是动态的绑定。这是箭头函数的主要设计特征。

虽然 this 的词法绑定可能很方便,但是有时这并不是你想要的。


JavaScript 代码:
  1. let a = {
  2. oneThing: ( a ) => {
  3. let b = a * 2;
  4. this.otherThing(b);
  5. },
  6. otherThing: ( b ) => {....}
  7. };
  8. a.oneThing(6);

当我们使用 a.oneThing(6) 时,this.otherThing( b ) 引用失败,因为 this 不指向 a 对象,而是指向周围的作用域(愚人码头注:这里 this 指向 Window )。如果你使用ES6 语法重写遗留代码,则需要注意这一点。

for … of 循环

ES6 增加了一种迭代数组中每个值的方法。这与现有的 for ... in 循环不同,for ... in 循环使用 key/index(键/索引) 循环。


JavaScript 代码:
  1. let a = ['a', 'b', 'c', 'd' ];
  2. // ES6
  3. for ( var val of a ) {
  4. console.log( val );
  5. } // "a" "b" "c" "d"
  6. // ES6 之前
  7. for ( var idx in a ) {
  8. console.log( idx );
  9. } // 0 1 2 3

使用新的 for … of 循环相当于在每个循环内添加一个 let val = a[idx] 。

数组,字符串,generators 和 集合(collections) 都可以在标准的 JavaScript 中迭代。普通对象通常不能迭代,除非你已经为它定义了一个迭代器。

关于 JavaScript 里的循环方法请查看:forEach,for…in,for…of

Number 字面量

ES5 代码很好地处理了十进制和十六进制数字格式,但没有指定八进制格式。 事实上,它在严格的模式下是被禁止的。

ES6 增加了一种新格式,以 0 开始,后面添加一个 o 就会将该数字声明为八进制数。还添加了二进制格式。


JavaScript 代码:
  1. Number( 29 ) // 29
  2. Number( 035 ) // 35 以旧八进制形式
  3. Number( 0o35 ) // 29 以新的八进制形式
  4. Number( 0x1d ) // 29 以十六进制形式
  5. Number( 0b11101 ) // 29 以二进制形式

以及更多…

ES6 为我们提供了更多的功能,使我们的代码更简洁,更短,更易于阅读和更强大。我的目标是撰写本文的续篇,内容将涵盖 ES6 中不太知名的部分。

上一篇:前端开发的45个经典技巧 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • 如何使用ES6编写更清洁的代码
    如何使用ES6编写更清洁的代码
    模板字面量(Template Literals) 模板字面量使得使用字符串比以前更容易。它们以反引号开始,可以使用${variable}插入变量。比较这两行代码: JavaScript 代码:
  • 前端开发的45个经典技巧
    前端开发的45个经典技巧
    1、首次给变量赋值是切记使用var关键字 ( 闲谈:清楚的记得有次去面试前端,一个项目经理同时面我和另外一个人,面试官开始就是要我们俩手写一个数组
  • 2018前端开发者技能树!
    2018前端开发者技能树!
    很多同学会问老师前端该学到哪裡?如果我是设计师,想走前端要学哪些技能?这边老师提供 前端开发者技能树 供同学参考参考。 (下方有额外说明) 入门
  • 百度熊掌号WordPress自动提交插件(BaiduXZH Submit)
    百度熊掌号WordPress自动提交插件(BaiduXZH Submit)
    BaiduXZH Submit插件介绍 百度熊掌号是集搜索资源平台(原百度站长平台)、百家号平台、商家号平台、服务平台、数据开放平台、运营中心、开放平台和认证
  • WordPress 4.9.2 安全维护更新
    WordPress 4.9.2 安全维护更新
    WordPress是全球使用最多的博客程序, 彩讯网 站长之前也使用过,建站效果不错,推荐大家使用。 一直在忙,没想到都发布WordPress 4.9.2 几天了。此次更新修