浅谈让你的代码更简短,更整洁,更易读的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 入门基础知识 想学习js的朋友可以参考下
Dec 26 Javascript
Javascript 自适应高度的Tab选项卡
Apr 05 Javascript
原生js实现跨浏览器获取鼠标按键的值
Apr 08 Javascript
js 触发select onchange事件代码
Mar 20 Javascript
JavaScript实现网页对象拖放功能的方法
Apr 15 Javascript
MVVM模式中ViewModel和View、Model有什么区别?
Jun 19 Javascript
javascript中apply、call和bind的使用区别
Apr 05 Javascript
AngularJS基础 ng-href 指令用法
Aug 01 Javascript
Bootstrap图片轮播组件Carousel使用方法详解
Oct 20 Javascript
jquery表单验证插件validation使用方法详解
Jan 20 Javascript
详解浏览器缓存和webpack缓存配置
Jul 06 Javascript
NProgress显示顶部进度条效果及使用详解
Sep 21 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连接access数据库
2008/03/27 PHP
谈谈PHP中substr和substring的正确用法及相关参数的介绍
2015/12/16 PHP
浅谈php中的访问修饰符private、protected、public的作用范围
2016/11/20 PHP
php实现银联商务公众号+服务窗支付的示例代码
2019/10/12 PHP
Javascript实现DIV滚动自动滚动到底部的代码
2012/03/01 Javascript
jQuery getJSON()+.ashx 实现分页(改进版)
2013/03/28 Javascript
js 本地预览的简单实现方法
2014/02/18 Javascript
javascript实现json页面分页实例代码
2014/02/20 Javascript
JavaScript获取当前网页标题(title)的方法
2015/04/03 Javascript
学习JavaScript设计模式(继承)
2015/11/26 Javascript
JS在Chrome浏览器中showModalDialog函数返回值为undefined的解决方法
2016/08/03 Javascript
浅谈jquery中使用canvas的问题
2016/10/10 Javascript
html判断当前页面是否在iframe中的实例
2016/11/30 Javascript
jQuery实现单击按钮遮罩弹出对话框效果(1)
2017/02/20 Javascript
在百度搜索结果中去除掉一些网站的资料(通过js控制不让显示)
2017/05/02 Javascript
vue组件数据传递、父子组件数据获取,slot,router路由功能示例
2019/03/19 Javascript
小程序表单认证布局及验证详解
2020/06/19 Javascript
[52:12]FNATIC vs Infamous 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
python用ConfigObj读写配置文件的实现代码
2013/03/04 Python
Python HTMLParser模块解析html获取url实例
2015/04/08 Python
Python multiprocessing模块中的Pipe管道使用实例
2015/04/11 Python
python制作花瓣网美女图片爬虫
2015/10/28 Python
Python复制文件操作实例详解
2015/11/10 Python
selenium+python自动化测试环境搭建步骤
2019/06/03 Python
python绘制无向图度分布曲线示例
2019/11/22 Python
浅谈python 中的 type(), dtype(), astype()的区别
2020/04/09 Python
python自动化测试三部曲之request+django实现接口测试
2020/10/07 Python
法国太阳镜店:Sunglasses Shop
2016/08/27 全球购物
英国时尚运动品牌的合集:The Sports Edit
2017/12/20 全球购物
Cinque网上商店:德国服装品牌
2019/03/17 全球购物
梅西百货官网:Macy’s
2020/08/04 全球购物
如何利用cmp命令比较文件
2013/09/23 面试题
社团活动策划书范文
2014/01/09 职场文书
捐款倡议书范文
2014/02/02 职场文书
2014年加油站工作总结
2014/12/04 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL