ES6的常用功能
来源:互联网 发布:java工程师一般年薪 编辑:程序博客网 时间:2024/06/05 04:29
块级作用域变量
在 ES6 中,我们使用 let/const
,而非 var
来声明变量。
var
有什么缺点呢?
var
的问题是变量会泄露到其它代码块,比如 for
循环或是 if
块。
test(false)
应该返回 outer
,但并不是,得到的值是 undefined
。
为什么?
因为即使 if
块没有执行,第 4 行的表达式 var x
也被提升了。
var 提升:
var
是在函数作用域中的,即使在声明前,它在整个函数内都是可用的。声明被提升了。所以变量在声明前就可以使用。
初始化 没有 被提升。如果使用
var
的话,一定 要在顶部声明变量。应用提升规则后,就好理解代码是如何执行的了:
ES5
varx = 'outer';
functiontest(inner){
varx;// HOISTED DECLARATION
if(inner){
x = 'inner';// INITIALIZATION NOT HOISTED
returnx;
}
returnx;
}
ECMAScript 2015帮助解决了这个问题:
ES6
letx = 'outer';
functiontest(inner){
if(inner){
letx = 'inner';
returnx;
}
returnx;// gets result from line 1 as expected
}
test(false);// outer
test(true);// inner
用 let
替代 var
以便按预期的那样去执行代码。如果没有调用 if
块,变量 x
就不会提升到块外。
Let 提升 和“temporal dead zone”
在 ES6 中,
let
会将变量提升到块顶部(而 非 像 ES5 是函数顶部)。但是在变量声明前引用变量会造成
ReferenceError
。let
是块作用域的,不可以在声明前使用。“Temporal dead zone”是块开始直到变量被声明的这段区域。
IIFE
在介绍 IIFE 之前,我们来看个例子:
ES5
{
varprivate = 1;
}
console.log(private);// 1
如你所见,private
会发生泄漏。需要使用 IIFE(立即执行函数表达式)将其包起来:
ES5
(function(){
varprivate2 = 1;
})();
console.log(private2);// Uncaught ReferenceError
如果看过 jQuery/lodash 或是其它开源项目的代码,你会注意到它们都利用了 IIFE,以避免污染全局环境,而只在全局下定义 _
、$
或是 jQuery
。
ES6 更工整,不再需要使用 IIFE,只要用块和 let
就可以了:
ES6
{
letprivate3 = 1;
}
console.log(private3);// Uncaught ReferenceError
Const
如果不希望变量的值再改变,可以使用 const
。
总之:用
let
和const
代替var
。使用
const
进行引用;避免使用var
。如果必须重新指定引用,可以用
let
代替const
。
文本模板
遇到文本模板时,不必再用嵌套连接了。比如:
ES5
varfirst = 'Adrian';
varlast = 'Mejia';
console.log('Your name is ' + first + ' ' + last + '.');
现在可以用 反引号(`) 和字符串插值 ${}
:
ES6
constfirst = 'Adrian';
constlast = 'Mejia';
console.log(`Yournameis${first}${last}.`);
多行字符串
不必像这样再连接 + n
字符串了:
ES5
vartemplate = '<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >n' +
' <div class="view">n' +
' <input class="toggle" type="checkbox" [checked]="todo.isDone">n' +
' <label></label>n' +
' <button class="destroy"></button>n' +
' </div>n' +
' <input class="edit" value="">n' +
'</li>';
console.log(template);
ES6 中同样可以用反引号解决:
ES6
consttemplate = `<li *ngFor="let todo of todos"[ngClass]="{completed: todo.isDone}" >
<divclass="view">
<inputclass="toggle"type="checkbox"[checked]="todo.isDone">
<label></label>
<buttonclass="destroy"></button>
</div>
<inputclass="edit"value="">
</li>`;
console.log(template);
两段代码会得到完全相同的结果。
解构赋值
ES6 解构非常简明并且好用。看看下面的例子:
获取数组元素
ES5
vararray = [1,2,3,4];
varfirst = array[0];
varthird = array[2];
console.log(first,third);// 1 3
等同于:
ES6
constarray = [1,2,3,4];
const[first,,third] = array;
console.log(first,third);// 1 3
调换值
ES5
vara = 1;
varb = 2;
vartmp = a;
a = b;
b = tmp;
console.log(a,b);// 2 1
等同于
ES6
leta = 1;
letb = 2;
[a,b] = [b,a];
console.log(a,b);// 2 1
返回多个值的解构
ES5
functionmargin(){
varleft=1,right=2,top=3,bottom=4;
return{left:left,right:right,top:top,bottom:bottom};
}
vardata = margin();
varleft = data.left;
varbottom = data.bottom;
console.log(left,bottom);// 1 4
在第3行,也可以像这样用数组返回(并保存序列):
return[left,right,top,bottom];
但之后调用时需要考虑返回数据的顺序。
varleft = data[0];
varbottom = data[3];
ES6 中调用时只会选择需要的数据(第 6 行):
ES6
functionmargin(){
constleft=1,right=2,top=3,bottom=4;
return{left,right,top,bottom};
}
const{left,bottom} = margin();
console.log(left,bottom);// 1 4
注意:第3行用到了一些其它的 ES6 功能。可以将 { left: left }
简化为 { left }
。看看和 ES5 的版本相比,现在多简洁啊~很酷不是吗?
参数匹配解构
ES5
varuser = {firstName:'Adrian',lastName:'Mejia'};
functiongetFullName(user){
varfirstName = user.firstName;
varlastName = user.lastName;
returnfirstName + ' ' + lastName;
}
console.log(getFullName(user));// Adrian Mejia
等同于(但更简洁):
ES6
constuser = {firstName:'Adrian',lastName:'Mejia'};
functiongetFullName({firstName,lastName}){
return`${firstName}${lastName}`;
}
console.log(getFullName(user));// Adrian Mejia
深度匹配
ES5
functionsettings(){
return{display:{color:'red'},keyboard:{layout:'querty'}};
}
vartmp = settings();
vardisplayColor = tmp.display.color;
varkeyboardLayout = tmp.keyboard.layout;
console.log(displayColor,keyboardLayout);// red querty
等同于(但更简洁):
ES6
functionsettings(){
return{display:{color:'red'},keyboard:{layout:'querty'}};
}
const{display:{color:displayColor},keyboard:{layout:keyboardLayout}} = settings();
console.log(displayColor,keyboardLayout);// red querty
也叫对象解构。
如你所见,解构很有用,并有助于形成好的编码风格。
最佳实践:
使用数组解构获取元素或调换变量,这样就不用创建临时引用了。
对于多返回值的情况,不要用数组解构,用对象解构。
类和对象
ES6 用“类”替代“构造函数”。
在 JavaScript 中,每个对象都有原型对象。所有 JavaScript 对象都从原型上继承方法和属性。
ES5 以面向对象编程(OOP)的方式创建对象,是利用构造函数实现的:
ES5
varAnimal = (function(){
functionMyConstructor(name){
this.name = name;
}
MyConstructor.prototype.speak = functionspeak(){
console.log(this.name + ' makes a noise.');
};
returnMyConstructor;
})();
varanimal = newAnimal('animal');
animal.speak();// animal makes a noise.
ES6 提供了语法糖,可以用 class
、constructor
等新的关键字、更少的样板代码实现相同的效果。同样可以看到相比于constructor.prototype.speak = function ()
,用 speak()
定义方法更加清晰:
ES6
classAnimal{
constructor(name){
this.name = name;
}
speak(){
console.log(this.name + ' makes a noise.');
}
}
constanimal = newAnimal('animal');
animal.speak();// animal makes a noise.
可以看到两种方式(ES5/6)的结果和使用方式相同。
最佳实践:
最好使用
class
语法,避免直接操作prototype
。原因是这样代码更加简明易懂。避免出现空的构造器。如果没有指明,类会有默认的构造器的。
继承
基于前面的 Animal
类,现在想要拓展 Animal
,定义一个 Lion
类。
ES5 原型继承的方式有些复杂。
ES5
varLion = (function(){
functionMyConstructor(name){
Animal.call(this,name);
}
// prototypal inheritance
MyConstructor.prototype = Object.create(Animal.prototype);
MyConstructor.prototype.constructor = Animal;
MyConstructor.prototype.speak = functionspeak(){
Animal.prototype.speak.call(this);
console.log(this.name + ' roars ');
};
returnMyConstructor;
})();
varlion = newLion('Simba');
lion.speak();// Simba makes a noise.
// Simba roars.
在此我没有详细解读所有的代码,但是需要注意:
第 3 行,明确地用参数调用
Animal
构造器。7-8 行,将
Lion
原型赋值为Animal
的原型。11 行,从父类
Animal
调用了speak
方法。
ES6 提供了新的关键字 extends
和 super
。
ES6
classLionextendsAnimal{
speak(){
super.speak();
console.log(this.name + ' roars ');
}
}
constlion = newLion('Simba');
lion.speak();// Simba makes a noise.
// Simba roars.
效果相同,但是相比于 ES5,ES6 代码更易读,完胜。
最佳实践:
使用内置的
extends
实现继承。
原生 Promise
用 promise 替代回调地狱
ES5
functionprintAfterTimeout(string,timeout,done){
setTimeout(function(){
done(string);
},timeout);
}
printAfterTimeout('Hello ',2e3,function(result){
console.log(result);
// nested callback
printAfterTimeout(result + 'Reader',2e3,function(result){
console.log(result);
});
});
这个函数接收一个回调,在 done
后执行。我们想要先后执行两次,所以在回调中又一次调用了 printAfterTimeout
。
如果需要 3 或 4 次回调,代码很快就一团糟了。那么用 promise 如何实现呢:
ES6
functionprintAfterTimeout(string,timeout){
returnnewPromise((resolve,reject) => {
setTimeout(function(){
resolve(string);
},timeout);
});
}
printAfterTimeout('Hello ',2e3).then((result) => {
console.log(result);
returnprintAfterTimeout(result + 'Reader',2e3);
}).then((result) => {
console.log(result);
});
promise 中可以用 then
在某个函数完成后执行新的代码,而不必再嵌套函数。
箭头函数
ES6 没有移除函数表达式,但是新增了箭头函数。
ES5 中,this
的指向有问题:
ES5
var_this = this;// need to hold a reference
$('.btn').click(function(event){
_this.sendData();// reference outer this
});
$('.input').on('change',function(event){
this.sendData();// reference outer this
}.bind(this));// bind to outer this
在函数内,需要用临时变量指向 this
或者使用 bind
绑定。ES6 中可以使用箭头函数。
For…of
最开始用 for
,然后使用 forEach
,而现在可以用 for…of
:
ES6
// this will reference the outer one
$('.btn').click((event) => this.sendData());
// implicit returns
constids = [291,288,984];
constmessages = ids.map(value => `IDis${value}`);
ES6 的 for…of
也可以用来迭代。
默认参数
之前需要检测变量是否定义了,而现在可以指定 default parameters
的值。或许你之前像下面这样写过?
ES5
functionpoint(x,y,isFlag){
x = x || 0;
y = y || -1;
isFlag = isFlag || true;
console.log(x,y,isFlag);
}
point(0,0)// 0 -1 true
point(0,0,false)// 0 -1 true
point(1)// 1 -1 true
point()// 0 -1 true
这可能是检测变量有值或指定默认值的惯用模式,但也存在一些问题:
第 8 行,我们传的值是
0, 0
但是得到的是0, -1
第 9 行,传进去
false
但是得到的是true
。
如果默认参数是布尔值或将值设为 0,是没有用的。想知道为什么?我会在下面的 ES6 示例后说明。
有了 ES6,现在可以用更少的代码实现更好的效果了。
ES6
functionpoint(x = 0,y = -1,isFlag = true){
console.log(x,y,isFlag);
}
point(0,0)// 0 0 true
point(0,0,false)// 0 0 false
point(1)// 1 -1 true
point()// 0 -1 true
注意第 5 行和第 6 行我们拿到了想要的值。ES5 的示例不好用,是因为先要检测 undefined
的值,而 false
、 null
、 undefined
和 0
都是假的值。我们可以加些代码:
ES5
functionpoint(x,y,isFlag){
x = x || 0;
y = typeof(y) === 'undefined'? -1 : y;
isFlag = typeof(isFlag) === 'undefined'?true : isFlag;
console.log(x,y,isFlag);
}
point(0,0)// 0 0 true
point(0,0,false)// 0 0 false
point(1)// 1 -1 true
point()// 0 -1 true
现在当检测 undefined
值时就符合我们的要求了。
剩余参数
之前使用 arguments,而现在可以用展开操作符。
ES5 中处理不定参数很麻烦:
ES5
functionprintf(format){
varparams = [].slice.call(arguments,1);
console.log('params: ',params);
console.log('format: ',format);
}
printf('%s %d %.2f','adrian',321,Math.PI);
现在可以用展开操作符 ...
达到相同的目的。
ES6
functionprintf(format,...params){
console.log('params: ',params);
console.log('format: ',format);
}
printf('%s %d %.2f','adrian',321,Math.PI);
展开操作符
之前用 apply()
,现在可以方便地使用展开操作符 ...
了:
提示:apply()
可以将数组转化为一系列参数。例如 Math.max()
接收一系列参数,但如果想应用于数组的话可以用 apply
帮助实现。
>Math.max(2,100,1,6,43);
<100
>Math.max([2,100,1,6,43]);
<NaN
>Math.max.apply(Math,[2,100,1,6,43]);
<100
如上所述,apply
可以将数组当作参数序列进行传递:
ES5
Math.max.apply(Math,[2,100,1,6,43])// 100
ES6 可以用展开操作符:
ES6
Math.max(...[2,100,1,6,43])// 100
之前用 concat
合并数组,现在也可以用展开操作符:
ES5
vararray1 = [2,100,1,6,43];
vararray2 = ['a','b','c','d'];
vararray3 = [false,true,null,undefined];
console.log(array1.concat(array2,array3));
ES6 可以用展开操作符展开嵌套的数组:
ES6
constarray1 = [2,100,1,6,43];
constarray2 = ['a','b','c','d'];
constarray3 = [false,true,null,undefined];
console.log([...array1,...array2,...array3]);
- ES6的常用功能
- ES6之Class的常用功能解读
- 常用的es6特性
- es6常用的语法
- ES6常用功能介绍及举例说明
- 【ES6】最常用的核心内容
- ES6的一些常用特性
- ES6 export的常用用法
- 最常用的ES6特性
- ES6常用的一些语法总结
- 总结ES6常用的新特性
- 总结ES6常用的新特性
- RN es6的几种常用用法
- JavaScript之ES6的常用新特性
- es6的常用语法和优越性
- Es6的常用语法和优越性
- ES6 的功能侦测库 ES-Checker——工具
- 【web开发 ES6新增语法】常用ES2015/ES6新增语法的使用
- Swift-字符串操作以及运算
- 如何安装Ceph并对接OpenStack
- Mac OS X安装软件和使用入门
- 分布式缓存-Memcached
- 诊断SQLSERVER问题常用的日志
- ES6的常用功能
- Color.FromArgb设置颜色
- git将本地文件上传到远程仓库
- RecyclerView嵌套RecyclerView
- CocoaPods解决Unable to add a source with url
- 一步一步实现直播和弹幕
- (转) 百度地图
- C#实现窗口最小化到系统托盘
- 使用laravel部署项目的需要注意的问题:路由访问404,pdo扩展