浅谈让你的代码更简短,更整洁,更易读的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 相关文章推荐
Mootools 1.2教程 设置和获取样式表属性
Sep 15 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
Dec 28 Javascript
Jquery动态改变图片IMG的src地址示例
Jun 25 Javascript
css样式标签和js语法属性区别
Nov 06 Javascript
javascript实现动态加载CSS
Jan 26 Javascript
微信小程序动态的加载数据实例代码
Apr 14 Javascript
利用Angular.js编写公共提示模块的方法教程
May 28 Javascript
在Vue中使用echarts的方法
Feb 05 Javascript
Vue实现表格批量审核功能实例代码
May 28 Javascript
JS实现继承的几种常用方式示例
Jun 22 Javascript
vue移动端使用appClound拉起支付宝支付的实现方法
Nov 21 Javascript
js事件机制----捕获与冒泡机制实例分析
May 22 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
咖啡知识 除了喝咖啡还有那些知识点
2021/03/06 新手入门
ajax在joomla中的原生态应用代码
2012/07/19 PHP
Discuz7.2版的faq.php SQL注入漏洞分析
2014/08/06 PHP
php类自动加载器实现方法
2015/07/28 PHP
php基于CodeIgniter实现图片上传、剪切功能
2016/05/14 PHP
微信公众平台开发(五) 天气预报功能开发
2016/12/03 PHP
thinkphp修改配置进入默认首页的方法
2017/02/07 PHP
php求斐波那契数的两种实现方式【递归与递推】
2019/09/09 PHP
Javascript 圆角div的实现代码
2009/10/15 Javascript
jQuery不间断滚动效果(模拟百度新闻支持文字/图片/垂直滚动)
2013/02/05 Javascript
解析Javascript中中括号“[]”的多义性
2013/12/03 Javascript
javascript查询字符串参数的方法
2015/01/28 Javascript
JavaScript创建闭包的两种方式的优劣与区别分析
2015/06/22 Javascript
jquery实现选中单选按钮下拉伸缩效果
2015/08/06 Javascript
深入理解JS addLoadEvent函数
2016/05/20 Javascript
JQuery通过后台获取数据遍历到前台的方法
2018/08/13 jQuery
vue-router源码之history类的浅析
2019/05/21 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
解决vue使用vant下拉框van-dropdown-item 绑定title值不变问题
2020/08/05 Javascript
Python中删除文件的程序代码
2011/03/13 Python
python监控网卡流量并使用graphite绘图的示例
2014/04/27 Python
python中的多重继承实例讲解
2014/09/28 Python
Python正则替换字符串函数re.sub用法示例
2017/01/19 Python
Python双精度浮点数运算并分行显示操作示例
2017/07/21 Python
python爬虫 使用真实浏览器打开网页的两种方法总结
2018/04/21 Python
Python中最好用的命令行参数解析工具(argparse)
2019/08/23 Python
Python实现自定义读写分离代码实例
2019/11/16 Python
python如何查看安装了的模块
2020/06/23 Python
python读取excel数据并且画图的实现示例
2021/02/08 Python
都柏林通行卡/城市通票:The Dublin Pass
2020/02/16 全球购物
高中数学教师求职信
2013/10/30 职场文书
销售个人求职信范文
2014/04/28 职场文书
网络文明传播志愿者活动方案
2014/08/20 职场文书
2014幼儿园教育教学工作总结
2014/12/17 职场文书
2017元旦晚会开幕词
2016/03/03 职场文书
详解PHP设计模式之依赖注入模式
2021/05/25 PHP