浅谈让你的代码更简短,更整洁,更易读的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实例教程(19) 使用HoTMetal(4)
Dec 23 Javascript
js中将HTMLCollection/NodeList/伪数组转换成数组的代码
Jul 31 Javascript
根据身份证号自动输出相关信息(籍贯,出身日期,性别)
Nov 15 Javascript
window.returnValue使用方法示例介绍
Jul 03 Javascript
使用jsonp完美解决跨域问题
Nov 27 Javascript
JS网页在线获取鼠标坐标值的方法
Feb 28 Javascript
jquery使整个div区域可以点击的方法
Jun 24 Javascript
用canvas 实现个图片三角化(LOW POLY)效果
Feb 18 Javascript
IE和Firefox之间在JavaScript语法上的差异
Apr 22 Javascript
如何用js实现鼠标向上滚动时浮动导航
Jul 18 Javascript
JavaScript中利用构造器函数模拟类的方法
Feb 16 Javascript
vue.js删除列表中的一行
Jun 30 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,不用COM,生成excel文件
2006/10/09 PHP
用PHP和ACCESS写聊天室(三)
2006/10/09 PHP
dedecms 批量提取第一张图片最为缩略图的代码(文章+软件)
2009/10/29 PHP
php带密码功能并下载远程文件保存本地指定目录 修改加强版
2010/05/16 PHP
php创建和删除目录函数介绍和递归删除目录函数分享
2014/11/18 PHP
php从csv文件读取数据并输出到网页的方法
2015/03/14 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
php+croppic.js实现剪切上传图片功能
2018/08/14 PHP
Javascript中的数学函数集合
2007/05/08 Javascript
jquery 表单取值常用代码
2009/12/22 Javascript
自写的一个jQuery圆角插件
2010/10/26 Javascript
创建公共调用 jQuery Ajax 带返回值
2012/08/01 Javascript
jquery如何改变html标签的样式(两种实现方法)
2013/01/16 Javascript
调用HttpHanlder的几种返回方式小结
2013/12/20 Javascript
JavaScript中的单引号和双引号报错的解决方法
2014/09/01 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
浅谈js构造函数的方法与原型prototype
2016/07/04 Javascript
vuejs父子组件通信的问题
2017/01/11 Javascript
从零开始学习Node.js系列教程一:http get和post用法分析
2017/04/13 Javascript
Bootstrap table使用方法总结
2017/05/10 Javascript
Vue.js通用应用框架-Nuxt.js的上手教程
2017/12/25 Javascript
详解使用create-react-app快速构建React开发环境
2018/05/16 Javascript
js input输入百分号保存数据库失败的解决方法
2018/05/26 Javascript
javascript实现小型区块链功能
2019/04/03 Javascript
nest.js 使用express需要提供多个静态目录的操作方法
2019/10/24 Javascript
你不知道的 TypeScript 高级类型(小结)
2020/08/28 Javascript
JavaScript用document.write()输出换行的示例代码
2020/11/26 Javascript
使用Python脚本对Linux服务器进行监控的教程
2015/04/02 Python
详解pandas如何去掉、过滤数据集中的某些值或者某些行?
2019/05/15 Python
老海军美国官网:Old Navy
2016/09/05 全球购物
汇源肾宝广告词
2014/03/20 职场文书
纪念九一八事变演讲稿:勿忘国耻
2014/09/14 职场文书
小学生通知书评语
2014/12/31 职场文书
人事任命书范本
2015/09/21 职场文书
使用JS实现简易计算器
2021/06/14 Javascript
CSS 使用 resize 实现图片拖拽切换预览功能(强大功能)
2021/08/23 HTML / CSS