让你的代码更简洁、更短小、更易读!ES6 技巧和窍门。

2025-05-26

让你的代码更简洁、更短小、更易读!ES6 技巧和窍门。

模板字面量

模板字面量使处理字符串比以前更加容易。它们以反引号开头,可以使用 ${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 }.`;
Enter fullscreen mode Exit fullscreen mode

这让编程变得简单得多,代码也更易读。你可以在花括号内放入任何内容:变量、方程式或函数调用。我将在本文的示例中使用这些内容。

块级作用域

JavaScript 一直以来都是由函数限定作用域的,因此将整个 JavaScript 文件包裹在一个空的立即执行函数表达式 (IIFE) 中变得很常见。这样做是为了隔离文件中的所有变量,从而避免变量冲突。
现在我们有了块级作用域,并且两个新的变量声明绑定到一个块级。

Let 声明

这与 var 类似,但有一些显著的区别。由于它是块作用域,因此可以声明一个同名的新变量,而不会影响外部变量。

var a = 'car' ;
{
    let a = 5;
    console.log(a); // 5
}
console.log(a); // car
Enter fullscreen mode Exit fullscreen mode

因为它绑定到块范围,所以它解决了这个经典的面试问题:
“输出是什么以及如何让它按预期工作”

for (var i = 1; i < 5; i++){
    setTimeout(() => { console.log(i); }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

在这种情况下,它输出“5 5 5 5 5”,因为变量 i 在每次迭代中都会发生变化。
如果你把 var 换成 let,那么一切都会改变。现在,每个循环都会创建一个新的块作用域,并将 i 的值绑定到该循环。就像你写的一样。

{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)} 
Enter fullscreen mode Exit fullscreen mode

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

{ 
    console.log(a); // undefined
    console.log(b); // ReferenceError
    var a = 'car';
    let b = 5;
}
Enter fullscreen mode Exit fullscreen mode

由于其范围更严格且行为更可预测,有些人说您应该使用“let”而不是“var”,除非您特别需要“var”声明的提升或更宽松的范围。

常量

以前,在 JavaScript 中,如果想声明一个常量变量,通常的做法是用大写字母来命名变量,但这并不能保证变量的安全,只能让其他开发者知道它是一个常量,不能被修改。
现在,我们有了 const 声明。

{
    const c = "tree";
    console.log(c);  // tree
    c = 46;  // TypeError! 
}
Enter fullscreen mode Exit fullscreen mode

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'}
}
Enter fullscreen mode Exit fullscreen mode

块作用域函数的问题
现在指定函数声明绑定到块作用域。

{
    bar(); // works
    function bar() { /* do something */ }
}
bar();  // doesn't work
Enter fullscreen mode Exit fullscreen mode

当你在 if 语句中声明一个函数时,问题就出现了。考虑一下:

if ( something) {
    function baz() { console.log('I passed') }
} else {
    function baz() { console.log('I didn\'t pass') } 
} 
baz();
Enter fullscreen mode Exit fullscreen mode

在 ES6 之前,无论发生什么,两个函数声明都会被提升,结果都会是“未通过”。
现在我们得到了“ReferenceError”,因为 baz 始终受块级作用域约束。

传播

ES6 引入了一个...称为展开运算符的运算符。它主要有两个用途:将一个数组或对象展开为一个新的数组或对象,以及将多个参数合并为一个数组。第一个用例可能是您最常遇到的,所以我们先来看一下。

let a = [3, 4, 5];
let b = [1, 2, ...a, 6];
console.log(b);  // [1, 2, 3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

这对于将一组变量从数组传递给函数非常有用。

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
Enter fullscreen mode Exit fullscreen mode

对象也可以被展开,将每个键值对输入到新对象中。

let car = { type: 'vehicle ', wheels: 4};
let fordGt = { make: 'Ford', ...car, model: 'GT'};
console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4}
Enter fullscreen mode Exit fullscreen mode

扩展运算符的另一个特性是它可以创建新的数组或对象。下面的示例为 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
Enter fullscreen mode Exit fullscreen mode

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

function foo(...args) {
    console.log(args); 
} 
foo( 'car', 54, 'tree');  //  [ 'car', 54, 'tree' ] 
Enter fullscreen mode Exit fullscreen mode

默认参数

现在可以使用默认参数定义函数。缺失或未定义的值将使用默认值初始化。但需要注意的是,null 和 false 值会被强制转换为 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
Enter fullscreen mode Exit fullscreen mode

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

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 ]
Enter fullscreen mode Exit fullscreen mode

解构

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

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
Enter fullscreen mode Exit fullscreen mode

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

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
Enter fullscreen mode Exit fullscreen mode

有时你想提取值,但又想将它们赋给一个新变量。这可以通过在等号左侧使用“键:变量”组合来实现。

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. 
Enter fullscreen mode Exit fullscreen mode

对象解构允许的另一件事是将一个值分配给多个变量。

let { x: first, x: second } = { x: 4 };
console.log( first, second ); // 4, 4
Enter fullscreen mode Exit fullscreen mode

对象字面量和简洁参数

当您从变量创建对象文字时,如果键与变量名相同,ES6 允许您省略键。

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 }
Enter fullscreen mode Exit fullscreen mode

这也可以与解构结合使用,使您的代码更简单、更干净。

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();
Enter fullscreen mode Exit fullscreen mode

它还可以用于解构传递给函数的对象。方法 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 }.`);
} 
Enter fullscreen mode Exit fullscreen mode

使用 ES6 我们可以提取年龄、姓名和公司,而无需额外的变量声明。

动态属性名称

ES6 增加了使用动态分配的键创建或添加属性的能力。

let  city = 'sheffield_';
let a = {
    [ city + 'population' ]: 350000
};
a[ city + 'county' ] = 'South Yorkshire';
console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }
Enter fullscreen mode Exit fullscreen mode

箭头函数

箭头函数主要有两种方式:它们的结构和它们的 this 绑定。
它们的结构比传统函数简单得多,因为它们不需要 function 关键字,并且会自动返回箭头后面的内容。

var foo = function( a, b ) {
    return a * b;
} 
let bar = ( a, b ) => a * b;
Enter fullscreen mode Exit fullscreen mode

如果函数需要的不仅仅是简单的计算,则可以使用花括号,
并且函数返回从块范围返回的任何内容。

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}`;
}
Enter fullscreen mode Exit fullscreen mode

箭头函数最有用的地方之一是数组函数,例如.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' ]
Enter fullscreen mode Exit fullscreen mode

除了语法更简洁之外,它还修复了this绑定行为中经常出现的问题。ES6 之前的函数修复方法是存储this引用,通常以self变量的形式。

var clickController = {
    doSomething: function (..) {
        var self = this;
        btn.addEventListener(
            'click', 
            function() { self.doSomething(..) }, 
            False
       );
   } 
};
Enter fullscreen mode Exit fullscreen mode

必须这样做,因为this绑定是动态的。这意味着事件监听器中的 this 和 doSomething 中的 this 指向的不是同一个对象。
在箭头函数中,this 绑定是词法的,而不是动态的。这是箭头函数的主要设计特性。
虽然词法的 this 绑定可能很棒,但有时这并不是我们想要的。

let a = {
    oneThing: ( a ) => {
         let b = a * 2;
         this.otherThing(b);
    }, 
    otherThing: ( b ) => {....} 
};
a.oneThing(6);
Enter fullscreen mode Exit fullscreen mode

当我们使用 时a.oneThing(6)this.otherThing( b )引用会失效,因为 this 指向的不是 a 对象,而是其周围的作用域。如果你使用 ES6 语法重写旧代码,这一点需要注意。

for…of 循环

ES6 增加了一种遍历数组中每个值的方法。这与现有的for ... in遍历键/索引的循环不同。

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
Enter fullscreen mode Exit fullscreen mode

使用新的循环可以省去在每个循环中for … of添加 的麻烦。 数组、字符串、生成器和集合在标准 JavaScript 中都是可迭代的。普通对象通常无法迭代,除非你为其定义了迭代器。let val = a[idx]

数字文字

ES5 代码可以很好地处理十进制和十六进制数字格式,但未指定八进制格式,并且在严格模式下被主动禁止。ES6 添加了一种新的格式,在初始 0 后添加一个 'o' 来声明该数字为八进制。他们还添加了二进制格式。

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
Enter fullscreen mode Exit fullscreen mode

还有更多……

ES6 为我们提供了更多功能,让我们的代码更简洁、更短小、更易读、更健壮。我计划写一篇续篇,介绍 ES6 中那些鲜为人知的部分。
如果你等不及,可以读读 Kyle Simpson 的《你不懂 JS》这本书,或者访问这个很棒的网站

如果您喜欢这篇文章,请做出反应并在评论中告诉我您的想法!

文章来源:https://dev.to/samwsoftware/make-your-code-cleaner-shorter-and-easier-to-read-es6-tips-and-tricks-2k37
PREV
从零开始学习 Go Web 开发
NEXT
我如何在 5 个月内将工资翻倍并获得了一份很棒的工作