ES6中的默认参数
- ES5的写法
var link = function (color, height, url) {
var color = color || 'red'
var height = height || 50
var url = url || 'www.baidu.com'
console.log(color)//blue
console.log(height)//50
console.log(url)//www.google.com
}
link('blue', 0, 'www.google.com')
- 在ES6中,我们可以把默认值直接放进函数签名
var link = function (color = 'red', height = 50, url = "www.baidu.com") {
console.log(color)//blue
console.log(height)//0
console.log(url)//www.google.com
}
link('blue', 0, 'www.google.com')
ES6中的模板表达式
- ES5的写法
function getMsg(firstName, lastName, id) {
var name = 'Your name is ' + firstName + ' ' + lastName
var url = 'http://localhost:3000/api/message/' + id
return name + ', ' + url
}
console.log(getMsg('Dot', 'Dolby', '12345'))//Your name is Dot Dolby, http://localhost:3000/api/message/12345
- 在ES6中,在反引号包裹的字符串中使用${NAME}语法来表示模板字符:
function getMsg(firstName, lastName, id) {
var name = `Your name is ${firstName} ${lastName}`
var url = `http://localhost:3000/api/message/${id}`
return `${name}, ${url}`
}
console.log(getMsg('Dot', 'Dolby', '12345'))//Your name is Dot Dolby, http://localhost:3000/api/message/12345
ES6中的多行字符串
- ES5的写法
var brotherStr1 = '“我好歹认识两个字,写了遗书,还留了一封信呢。”路上,麻子妈和宋老太这样说。\n'
+ '宋老太问:“信上写的什么哪?”\n'
+ '“写的是‘我不是死了,只是走了’。”\n'
+ '并非死别,只是生离。\n'
+ '痛苦与幸福,生不带来,死不带去。\n'
var brotherStr2 = '而他一生所憎恶的,全都令他魂牵梦萦。\n他简直就像石缝里亿万年间挤压而生的一小撮树芽,摇摇欲坠,形容扭曲,但郁郁葱葱。'
console.log(brotherStr1)
// “我好歹认识两个字,写了遗书,还留了一封信呢。”路上,麻子妈和宋老太这样说。
// 宋老太问:“信上写的什么哪?”
// “写的是‘我不是死了,只是走了’。”
// 并非死别,只是生离。
// 痛苦与幸福,生不带来,死不带去。
console.log(brotherStr2)
// 而他一生所憎恶的,全都令他魂牵梦萦。
// 他简直就像石缝里亿万年间挤压而生的一小撮树芽,摇摇欲坠,形容扭曲,但郁郁葱葱。
- ES6中,只要充分利用反引号。
var brotherStr1 = `“我好歹认识两个字,写了遗书,还留了一封信呢。”路上,麻子妈和宋老太这样说。
宋老太问:“信上写的什么哪?”
“写的是‘我不是死了,只是走了’。”
并非死别,只是生离。
痛苦与幸福,生不带来,死不带去。`
var brotherStr2 = `而他一生所憎恶的,全都令他魂牵梦萦。
他简直就像石缝里亿万年间挤压而生的一小撮树芽,摇摇欲坠,形容扭曲,但郁郁葱葱。`
console.log(brotherStr1)
// “我好歹认识两个字,写了遗书,还留了一封信呢。”路上,麻子妈和宋老太这样说。
// 宋老太问:“信上写的什么哪?”
// “写的是‘我不是死了,只是走了’。”
// 并非死别,只是生离。
// 痛苦与幸福,生不带来,死不带去。
console.log(brotherStr2)
// 而他一生所憎恶的,全都令他魂牵梦萦。
// 他简直就像石缝里亿万年间挤压而生的一小撮树芽,摇摇欲坠,形容扭曲,但郁郁葱葱。
ES6中的解构赋值
对象字面量和数组字面量提供了一种简单的定义一个特定的数据组的方法。
let x = [1, 2, 3, 4, 5];
解构赋值使用了相同的语法,不同的是在表达式左边定义了要从原变量中取出什么变量。
var x = [1, 2, 3, 4, 5];
var [y, z] = x;
console.log(y); // 1
console.log(z); // 2
解构数组
- 变量声明并赋值 时的解构
var foo = ["one", "two", "three"];
var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"
- 变量先声明后赋值时的解构
var a, b;
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
- 为了防止从数组中取出一个值为undefined的对象,可以为这个对象设置默认值。
var a, b;
[a = 5, b = 7] = [1];
console.log(a); // 1
console.log(b); // 7
- 在一个解构表达式中可以交换两个变量的值。
var a = 1;
var b = 3;
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
- 从一个函数返回一个数组是十分常见的情况,解构使得处理返回值为数组时更加方便。在下面例子中,[1, 2] 作为函数的 f() 的输出值,可以使用解构用一句话完成解析。
function f() {
return [1, 2];
}
var a, b;
[a, b] = f();
console.log(a); // 1
console.log(b); // 2
- 可以忽略你不感兴趣的返回值:
function f() {
return [1, 2, 3];
}
var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3
- 也可以忽略全部返回值
[, ,] = f();
- 当解构一个数组时,可以使用剩余模式,将数组剩余部分赋值给一个变量。
var [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]
- 注意:如果剩余元素右侧有一个逗号,会抛出语法错误的异常,因为剩余元素必须是数组的最后一个元素。
var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma
- ...的扩展应用:...就是脱掉衣服———脱掉数组的外壳
function sort(...arr){
console.log(arr.sort())
}
sort(3, 1, 5) //[1, 3, 5]
function max(arr){
return Math.max(...arr)
}
max([3, 4, 1]) // 4
//类数组对象转数组
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(p=> {
console.log(p.innerText);
});
[...ps].forEach(p=>{console.log(p.innerText)});
解构对象
var o = { p: 42, q: true };
var { p, q } = o;
console.log(p); // 42
console.log(q); // true
// 用新变量名赋值
var { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true
- 变量可以先赋予默认值。当要提取的对象没有对应的属性,变量就被赋予默认值。
var { a = 10, b = 5 } = { a: 3 };
console.log(a); // 3
console.log(b); // 5
- 浏览器很蠢,没有圆括号浏览器会认为等号两边是两个块,圆括号括起来可以让浏览器将对象的解构赋值当做表达式去执行,也就是加了圆括号对象的解构赋值操作可以正确被执行。
let name
let age
{name: name, age: age} = {name: 'jirengu', age: 4}//Uncaught SyntaxError: Unexpected token :
let name
let age
({name: name, age: age} = {name: 'jirengu', age: 4})//{name: "jirengu", age: 4}
ES6中改进的对象表达式
- ES5的写法
var serviceBase = {
port: 3000,
url: 'www.baidu.com'
}
var getAccounts = function () {
return [1, 2, 3]
}
var accountServiceES5 = {
port: serviceBase.port,
url: serviceBase.url,
getAccounts: getAccounts,
toString: function () {
return JSON.stringify(this.valueOf())
},
getUrl: function () {
return 'http://' + this.url + ':' + this.port
},
valueOf_1_2_3: getAccounts()
}
console.log(accountServiceES5)
//{ port: 3000,
// url: 'www.baidu.com',
// getAccounts: [Function: getAccounts],
// toString: [Function: toString],
// getUrl: [Function: getUrl],
// valueOf_1_2_3: [ 1, 2, 3 ] }
console.log(accountServiceES5.port)//3000
- 在ES6的对象表达式中,我们把getAccounts: getAccounts简化为getAccounts,并且我们还可以用proto直接设置prototype,我们还可以调用 super 和动态索引(valueOf_1_2_3)
var serviceBase = {
port: 3000,
url: 'www.baidu.com'
}
var getAccounts = function () {
return [1, 2, 3]
}
var accountService = {
__proto__: serviceBase,
getAccounts,
toString() {
return JSON.stringify((super.valueOf()))
},
getUrl() {
return 'http://' + this.url + ':' + this.port
},
['valueOf_' + getAccounts().join('_')]: getAccounts()
}
console.log(accountService)
// { getAccounts: [Function: getAccounts],
// toString: [Function: toString],
// getUrl: [Function: getUrl],
// valueOf_1_2_3: [ 1, 2, 3 ] }
console.log(accountService.__proto__.port)//3000
ES6中的箭头函数
箭头函数神奇之处在于他会让你写正确的代码。比如,this在上下文和函数中的值应当是相同的,它不会变化,通常变化的原因都是因为你创建了闭包。使用箭头函数可以让我们不再用that = this或者self = this或者_this = this或者.bind(this)这样的代码,这些代码特别丑。
- ES5中,要先用一个变量保存当前的this值,执行事件时用这个变量才能确保正确的结果
var _this = this
$('.btn').click(function (e) {
_this.sendData()
})
- ES6写法,其实写法上就相当于将
function(){}
换成了()=>{}
$('.btn').click((e) => {
this.sendData()
})
- 在ES5中
var logUpperCase = function () {
var _this = this
this.string = this.string.toUpperCase()
return function () {
return console.log(_this.string)
}
}
logUpperCase.call({ string: 'es6 rocks' })()
- 在ES6中我们无需_this,以下两种写法结果是等价的,
注意,在ES6中你可以合理的把箭头函数和旧式 function 函数混用。当箭头函数所在语句只有一行时,它就会变成一个表达式,它会直接返回这个语句的值。但是如果你有多行语句,你就要明确的使用return。
var logUpperCase = function () {
this.string = this.string.toUpperCase()
return () => { return console.log(this.string) }
}
logUpperCase.call({ string: 'es6 rocks' })()
var logUpperCase = function () {
this.string = this.string.toUpperCase()
return () => console.log(this.string)
}
logUpperCase.call({ string: 'es6 rocks' })()
- 在ES5中
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9']
var messages = ids.map(function (value) {
return 'ID is ' + value // 显式返回
});
- 在ES6中
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9']
var messages = ids.map(value => `ID is $(value)`)// 隐式返回
- 在只有一个参数的函数签名中,括号是可有可无的,但是如果多于一个参数时就要加上。
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9']
var messages = ids.map((value, index, list) => `ID of ${index} element is ${value} `) // 隐式返回
ES6中的Promise
Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象,用于处理异步请求。中文含义:诺言,结果:诺言可能兑现,也可能食言。
语法:
new Promise(
/* executor */
function(resolve, reject) {...}
);
executor是一个带有 resolve 和 reject 两个参数的函数 。executor 函数在Promise构造函数执行时同步执行,被传递 resolve 和 reject 函数(executor 函数在Promise构造函数返回新建对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦完成,可以调用resolve函数来将promise状态改成fulfilled,或者在发生错误时将它的状态改为rejected。
如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
一个 Promise有以下几种状态:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功。
- rejected: 意味着操作失败。
Promise原型上有几个重要的方法分别是:catch和then
catch用于添加一个否定(rejection) 回调到当前 promise, 返回一个新的promise。
then用于添加肯定和否定回调到当前 promise, 返回一个新的 promise, 将以回调的返回值 来resolve。
因为Promise.prototype.then和Promise.prototype.catch方法返回promise 对象, 所以它们可以被链式调用。
看个例子:
- ES5中的写法
setTimeout(function(){
console.log('Yay!')
setTimeout(function(){
console.log('Wheeyee!')
}, 1000)
}, 1000)
大约1s后打印出Yay!
,再过大约1s打印出Wheeyee!
- ES6写法
var wait1000 = ()=> new Promise((resolve, reject)=> {setTimeout(resolve, 1000)})
wait1000()
.then(function() {
console.log('Yay!')
return wait1000()
})
.then(function() {
console.log('Wheeyee!')
});
与ES5的写法等价,虽然我现在不会用,但是大家都觉得好,那应该就是好的吧^__^
,mark一下,之后继续学习。
块级作用域的let和const
let是一个更新的var,可以让你把变量作用域限制在当前块里。我们用{}来定义块,但是在ES5中这些花括号没有块级作用域的概念。
function testScope(a) {
var b = 0
if (a) {
var b = 1
}
{ // 让块来的更疯狂
var b = 100
{
var b = 1000
}
}
return b
}
console.log(testScope(true))//1000
这个结果一定不是我们想要的,将var换成let以限制变量的作用域
function testScope(a) {
let b = 0
if (a) {
let b = 1
// return b//1
}
{
let b = 100
// return b//100
{
let b = 1000
// return b//1000
}
}
return b
}
console.log(testScope(true))//0
运行结果是0,因为在if块中也有let,相当于每一个let都限制了变量的作用域
如果是以下情况呢?
function testScope(a) {
let b = 0
let b = 1
let b = 2
return b
}
console.log(testScope(true))
会报错,SyntaxError: Identifier 'b' has already been declared。说明已经用let限制了作用域的同名变量无法被覆盖,换成var b=1
同样会报错。
说到const,事情就简单多了。此声明创建一个常量,其作用域可以是全局或本地声明的块,常量不能和它所在作用域内的其他变量或函数重名。为了演示,这里有定义了一堆常量,并且由于作用域的原因,这些定义都是有效的。
function calculateTotalAmount(vip) {
const amount = 0
if (vip) {
const amount = 1
}
{ // 更多的块
const amount = 100
{
const amount = 1000
}
}
return amount
}
console.log(calculateTotalAmount(true))//0
const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。
const MY_AGE = 2
// MY_AGE=7//报错,TypeError: Assignment to constant variable.
// const MY_AGE=3//报错,SyntaxError: Identifier 'MY_AGE' has already been declared
console.log(MY_AGE)//2
if (MY_AGE === 2) {
let MY_AGE = 5
console.log(MY_AGE)//5
var MY_AGE = 4//报错,MY_AGEHUI被提升到全局作用域并引发错误,SyntaxError: Identifier 'MY_AGE' has already been declared
}
ES6中的类
我们都知道JS语言的面向对象编程中没有类的概念,所以类的创建和使用让人身份恼火,我的另一篇博客里有关于面向对象中怎样创建类的介绍,这里不赘述。
ES6中引入的类(classes) 是 JavaScript 基于原型的继承的语法糖。类语法不是向JavaScript引入一个新的面向对象的继承模型,而是提供一个更简单清晰的语法来创建对象并处理继承。
- 定义类的一种方法是使用一个类声明。使用带有class关键字的类名去声明一个类。
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
注意:变量和函数声明会提升,值不会提升,类声明也不会提升,所以首先要声明一个类才能去访问它,不然会报错。
- 另一个定义类的方法是类表达式,类表达式可以是被命名的或者匿名的
/* 匿名类 */
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
/* 命名的类 */
let Rectangle = class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
同样的,类表达式也不会提升,所以也要先声明类表达式,再去访问它。
一个类只能拥有一个名为 “constructor”的特殊方法。如果类包含多个构造函数的方法,则将抛出 一个SyntaxError。
extends关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类。如果子类中存在构造函数,则需要在使用“this”之前首先调用super(),构造函数可使用super关键字来调用父类的构造函数。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
speak() {
super.speak();
console.log(this.name + ' barks');
}
}
var aaa = new Animal('dot')
aaa.speak()//dot makes a noise.
var bbb = new Dog('blus')
bbb.speak()
//blus makes a noise.
// blus barks
ES6中的模块化
ES6之前JavaScript并没有对模块化有过原生的支持,人们想出来AMD,RequireJS,CommenJS等等,现在终于有import和export运算符来实现了。
ES6中你可以用export来暴露你的类
这是ES5中的module.js文件:
module.exports = {
port: 3000,
getAccounts: function() {
...
}
}
在ES5的main.js中,用require('模块')来导入:
var service = require('module.js')
console.log(service.port) // 3000
但是在ES6中,我们用export和import。
比如这是ES6中的module.js文件:
export var port = 3000
export function getAccounts(url) {
...
}
在需要引入的main.js文件中,可以用import {名称} from '模块'语法:
import {port, getAccounts} from 'module'
console.log(port) // 3000
或者就直接在main.js中引入所有的变量:
import * as service from 'module'
console.log(service.port) // 3000
参考资料: