浅谈让你的代码更简短,更整洁,更易读的ES6小技巧


Posted in Javascript onOctober 25, 2018

写在文章前面

这篇文章翻译自ES6 tips and tricks to make your code cleaner, shorter, and easier to read!. 文章就代码整洁方面对es6进行了总结。如有错误欢迎指出。

template literals 模板字符串

模板字符串使字符串的使用变得比以前更简单了,他们以反引号开始(`),并且能过使用${变量}来插入变量。我们来比较一下下面两行代码。

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是使用函数作用域的,这就是为什么我们是为什么我们越来越频繁的使用匿名的立即执行函数表达式(iife)来实现整个JavaScript文件的封装。我们这么做是为了把所有的变量隔离在文件内从而避免变量冲突。
现在我们有了块级作用域和两个崭新的块级作用域的变量声明

let declaration 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,一切都变了。 现在,每次循环都会创建一个全新的块级作用域吧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)}

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没有让变量完全不可变,只是锁定他的赋值,当你有一个复杂的变量(数组或者对象)的时候,值还是可以被修改的。

{
  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'}
}

Problem with block scoping functions函数块级作用域化带来的问题

函数的声明也可以限制在块级作用域中。

{
  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一直被限定在块级作用域内。

Spread 扩展运算符

ES6介绍了...操作符,这个操作符指的就是‘扩展运算符‘。他的主要用途有两个:1. 将一个数组或者对象放到一个新的数组或者对象中 2. 将数组中的多个参数合并在一起

第一个用途可能是你将会使用的最多的。所以我们先来看他。

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

如果我们想把一个数组内的一组参数传递给函数,这个时候扩展运算符就十分的有用了。

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

一个对象也可以扩展的,它会把每个键值对写入新的对象中。( 对象扩展已经在提议的第四阶段,而且将会在es2018中正式出现 。但这种特性目前只被chrome60及以后的版本,Firefox55及以后,node 6.4.0及以后的版本所支持)【译者注:在2ality博客中的es2018一文中得知,在刚刚结束的TC39会议中,ECMA2018的特性被敲定了。

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] 不同的数组
c.push(5);
console.log(a); // [1, 2, 3, 5] 
console.log(c); // [1, 2, 3, 5] 同一个数组

第二个用法是把变量聚集到一个数组里面。当你不知道一个函数到底有多少的传参的时候会这个方法会变得非常的有用。

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

Default Parameter 参数默认值

函数现在可以使用默认的参数值来定义了。不传参或者未定义值都被初始化为默认值。但是需要注意的是,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

默认值的类型可以不仅仅是值类型---还可以是表达式或者函数。

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 ]

Destructuring解构

解构是拆开等号左边的数组或者对象的过程。这个数组或者对象可以来自一个变量,一个函数,或者一个等式

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

有时,你可能想提取出值然后费赔给新的变量,这个可以通过在等号左侧使用一个“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允许你在键与变量名字相同的情况下省略该键。

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之前要怎么做, 方法三是使用解构和简洁表达法。

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添加了使用动态分配的键创建或添加属性的功能。

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

箭头函数

箭头函数有两个比较重要的特点: 他们的结构以及他们的this 指向

他们比传统的函数有更简单的结构因为他们不需要关键字function 而且他们可以自动返回在箭头后面的一部分,无论箭头后面的是什么。

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

如果函数有多于一个的计算式,可以使用花括号来包起来,然后函数返回块作用域返回的任何内容。

箭头函数一个最重要的用途之一就是应用在数组的相关函数中,像.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' ]

在拥有一个更短的表达方式的同时,箭头函数还修复了有关于this绑定行为经常出现的问题。ES6之前解决这个问题通常是使用一个self变量来存储这个指向。

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

这个this的赋值是一定要做的,因为this的绑定是动态的。这就意味着this在eventlistener内部和在doSomething内部指的并不是同一个东西。

在箭头函数内部,this的绑定是语义上的就是指当前的,而不是动态的。这也是箭头函数的主要设计特点。

虽然这种词法上的this很棒,但是有些时候,他却不是我们想要的那样。

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

当我们使用a.oneThing(6),  这个this.otherThing(6) 会抛出引用失败的错误,因为this没有指向对象a,而是指向了环境作用域。如果你正在使用ES6的代码使用ES6的语法,这个是你需要注意的事情。

for...of loops (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

使用新的for ... of循环,在每个循环内部保存了一个let val = a[idx]。

数组,字符串,generator以及从collection 在标准JavaScript中都是可迭代的。普通的对象无法正常的使用for...of来迭代,除非你自己定义一个迭代器。

Number Literals 数字字面量

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中不太知名的部分。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript实际应用:innerHTMl和确认提示的使用
Jun 22 Javascript
用javascript编写的第一人称射击游戏
Feb 25 Javascript
JavaScript操作XML 使用百度RSS作为新闻源示例
Feb 17 Javascript
javascript执行环境及作用域详解
May 05 Javascript
JS实现HTML标签转义及反转义
Apr 14 Javascript
bootstrap制作jsp页面(根据值让table显示选中)
Jan 05 Javascript
javascript基础知识讲解
Jan 11 Javascript
JavaScript实现自动跳转文本功能
May 25 Javascript
微信小程序之绑定点击事件实例详解
Jul 07 Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 Javascript
微信小程序自定义组件实现tabs选项卡功能
Jul 14 Javascript
详解Webpack loader 之 file-loader
Nov 07 Javascript
代码整洁之道(重构)
Oct 25 #Javascript
Vue使用NPM方式搭建项目
Oct 25 #Javascript
小程序云开发实战小结
Oct 25 #Javascript
vue使用原生js实现滚动页面跟踪导航高亮的示例代码
Oct 25 #Javascript
在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法
Oct 25 #Javascript
浅谈高大上的微信小程序中渲染html内容—技术分享
Oct 25 #Javascript
使用ECharts实现状态区间图
Oct 25 #Javascript
You might like
PHP 文件扩展名 获取函数
2009/06/03 PHP
第四章 php数学运算
2011/12/30 PHP
PHP 自定义错误处理函数的使用详解
2013/05/10 PHP
解析array splice的移除数组中指定键的值,返回一个新的数组
2013/07/02 PHP
php生成excel文件的简单方法
2014/02/08 PHP
php变量与JS变量实现不通过跳转直接交互的方法
2017/08/25 PHP
PHP上传文件及图片到七牛的方法
2018/07/25 PHP
设定php简写功能的方法
2019/11/28 PHP
讲两件事:1.this指针的用法小探. 2.ie的attachEvent和firefox的addEventListener在事件处理上的区别
2007/04/12 Javascript
跨浏览器通用、可重用的选项卡tab切换js代码
2011/09/20 Javascript
javascript倒计时功能实现代码
2012/06/07 Javascript
javascript页面上使用动态时间具体实现
2014/03/18 Javascript
jQuery插件制作之全局函数用法实例
2015/06/01 Javascript
JS实现为排序好的字符串找出重复行的方法
2016/03/02 Javascript
Node.js实现数据推送
2016/04/14 Javascript
从零学习node.js之详解异步控制工具async(八)
2017/02/27 Javascript
js Dom实现换肤效果
2017/10/21 Javascript
深入浅析JSONAPI在PHP中的应用
2017/12/24 Javascript
图文详解vue框架安装步骤
2019/02/12 Javascript
jQuery事件绑定和解绑、事件冒泡与阻止事件冒泡及弹出应用示例
2019/05/13 jQuery
jquery实现垂直无限轮播的方法分析
2019/07/16 jQuery
vue父子模板传值问题解决方法案例分析
2020/02/26 Javascript
[58:09]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第三场 6.2
2018/06/03 DOTA
Python xlrd读取excel日期类型的2种方法
2015/04/28 Python
浅谈Python 集合(set)类型的操作——并交差
2016/06/30 Python
python读取各种文件数据方法解析
2018/12/29 Python
python单线程文件传输的实例(C/S)
2019/02/13 Python
如何在sublime编辑器中安装python
2020/05/20 Python
Python pytesseract验证码识别库用法解析
2020/06/29 Python
一款纯css3实现的颜色渐变按钮的代码教程
2014/11/12 HTML / CSS
自荐信模版
2013/10/24 职场文书
《小儿垂钓》教学反思
2014/02/23 职场文书
初一学生期末评语
2014/04/24 职场文书
妇联领导班子剖析材料
2014/08/21 职场文书
公司法定代表人授权委托书
2014/09/29 职场文书
关于公司年会的开幕词
2016/03/04 职场文书