ES6 语言基础
引言
ES 6(于2015年6月正式发布)是JavaScript语言的下一代标准,相对于ES 5(于2011年6月正式发布)新增了一些语法规则和数据结构方法,例如比较典型的Set和Map数据结构和箭头函数等,可以理解成传统JavaScript的升级版,后续还会有ES 7、ES 8版本等。Vue 3发布以来,极力推荐采用ES 6的语法来开发代码,另外本书的实战项目将全部采用ES 6代码。由于移动端操作系统和浏览器兼容性问题的限制,虽然大部分机型原生就支持ES 6语法的JavaScript,但是仍有一部分市场占有率较低的机型无法支持ES 6语法,例如Android系统4.4及以下版本和iOS系统8.4及以下版本。因此,为了项目的健壮性和更强的适配性,会采用Node.js的Babel工具来将ES 6代码转换成兼容性更强的ES 5代码。由于ES 6的语法内容很多,相对复杂,因此本章只会对实战项目中用到的ES 6语法结合ES 5的写法来对比讲解和演示。
1. 变量声明
1. let、var和const
在ES 6语法中,新增了let和const来声明变量,在ES 6之前,ES 5中只有全局作用域和函数作用域,代码如下:
if(true) {
var a = 'Tom'
}
console.log('a',a) // Tom
作用域是一个独立的地盘,让变量不外泄出去,但是上面的代码中的变量a就作为全局作用域外泄了出去,所以此时JavaScript没有区块作用域(或称为块级作用域)的概念。在ES 6中加入区块作用域之后,代码如下:
if(true) {
let a = 'Tom'
}
console.log('a',a) // Uncaught ReferenceError: a is not defined
let和var都可以用来声明变量,但是在ES 6中,有下面一些区别:
-
使用var声明的变量没有区块的概念,可以跨块访问。
-
使用let声明的变量只能在区块作用域中访问,不能跨块访问。在相同的作用域下,使用var和let具有相同的效果,建议在ES 6语法中使用let来声明变量,这样可以更加明确该变量所处的作用域。const表示声明常量,一般用于一旦声明就不再修改的值,并且const声明的常量必须经过初始化,代码如下:
const a = 1
a = 2 // Uncaught TypeError: Assignment to constant variable
const b // Uncaught SyntaxError: Missing initializer in const declaration
总结一下,如果在ES 5中习惯了使用var来声明变量,在切换到ES 6时,就需要思考一下变量的用途和类型,选择合适的let和const来使代码更加规范和语义化。
2. 箭头函数
ES 6新增了使用“箭头”(=>)声明函数,代码如下:
let f = v => v
// 等同于
var f = function (v) {
return v
}
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分,当函数的内容只有返回语句时,可以省去大括号和return指令,代码如下:
let f = () => 5
// 等同于
var f = function () { return 5 }
let sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2
}
如果箭头数的内容部分多于一条语句,就要用大括号将它们括起来,并且使用return语句返回,代码如下:
let sum = (num1, num2) => {
let num = 0
return num1 + num2 + num;
}
箭头函数会默认绑定外层的上下文对象this的值,因此在箭头函数中,this的值和外层的this是一样的,不需要使用bind或者call的方法来改变函数中的上下文对象,例如下面的代码:
mounted () {
this.foo = 1
setTimeout(function(){
console.log(this.foo) // 打印出1
}.bind(this),200)
}
//相当于
mounted () {
this.foo = 1
setTimeout(() => {
console.log(this.foo) // 同样打印出1
},200)
}
上面的代码中,在Vue.js的mounted方法中,this指向当前的Vue组件的上下文对象,如果想要在setTimeout的方法中使用this来获取当前Vue组件的上下文对象,那么非箭头函数需要使用bind,箭头函数则不需要。箭头函数是实战项目中使用最多的ES 6语法,所以掌握好其规则和用法是非常重要的。
3. 对象属性和方法的简写
ES 6允许在大括号中直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁,代码如下:
const foo = 'bar'
const baz = {foo}
// 等同于
const baz = {foo: foo}
console.log(baz) // {foo: "bar"}
const foo = 'bar' const baz = {foo} // 等同于 const baz = {foo: foo} console.log(baz) // {foo: "bar"}
{
name: 'item',
data () {
return {
name:'bar'
}
}
mounted () {
},
methods: {
clearSearch () {
}
}
}
// 相当于
{
name: 'item',
data :function() {
return {
name:'bar'
}
}
mounted :function() {
},
methods: {
clearSearch :function() {
}
}
}
在上面的代码中,展示了采用ES 6语法来创建Vue组件所需的方法和属性,包括name属性、mounted方法、data方法等,是后面实战项目中经常使用的写法。
4. 对象解构
在ES 6中,可以使用解构从数组和对象中提取值并赋给独特的变量,代码如下:
// 数组
const input = [1, 2];
const [first, second] = input;
console.log(first,second) // 1 , 2
// 对象
const o = {
a: "foo",
b: 30,
c: "Johnson"
};
const {a, b, c} = o;
console.log(a,b,c) // foo , 30 , Johnson
在上面的代码中,花括号“{ }”表示被解构的对象,a、b和c表示要将对象中的属性存储到其中的变量中。
2. 模块化
在ES 6版本之前,JavaScript一直没有模块(Module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如Ruby的require、Python的import,甚至就连CSS都有@import,但是JavaScript任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。好在广大的JavaScript程序员自己制定了一些模块加载方案,主要有CommonJS和AMD两种。前者用于Node.js服务器,后者用于浏览器。
1. import和export
随着ES 6的到来,终于原生支持了模块化功能,即import和export,而且实现得相当简单,完全可以取代CommonJS和AMD规范成为浏览器和服务器通用的模块化解决方案。
在ES 6的模块化系统中,一个模块就是一个独立的文件,模块中的对外接口采用export关键字导出,可以将export放在任何变量、函数或类声明的前面,从而将它们暴露给外部代码使用,代码如下:要导出数据,在变量前面加上export关键字:
export var name = "小明";
export let age = 20;
// 上面的写法等价于下面的写法
var name = "小明";
let age = 20;
export {
name:name,
age:age
}
// export对象简写的方式
export {name,age}
要导出函数,需要在函数前面加上export关键字:
export function sum(num1,num2){
return num1 + num2;
}
// 等价于
let sum = function (num1,num2){
return num1 + num2;
}
export sum
所以,如果没有通过export关键字导出,在外部就无法访问该模块的变量或者函数。有时会在代码中看到使用export default,它和export具有同样的作用,都是用来导出对外提供接口的,但是它们之间还有一些区别:
- export default用于规定模块的默认对外接口,并且一个文件只能有一个export default,而export可以有多个。
- 通过export方式导出,在导入时要加{ },export default则不需要。在一个模块中可以采用import来导入另一个模块export的内容。导入含有多个export的内容,可以采用对象简写的方式,也是现在使用比较多的方式,代码如下:
//other.js
var name = "小明"
let age = 20
// export对象简写的方式
export {name,age}
//import.js
import {name,age} from "other.js"
console.log(name) // 小明
console.log(age) // 20
导入只有一个export default的内容,代码如下:
//other.js
export default function sum(num1,num2) {
return num1 + num2;
}
//import.js
import sum from "other.js"
console.log(sum(1,1)) // 2
有时也会在代码中看到module.exports的用法,这种用法是从Node.js的CommonJS演化而来的,它其实就相当于:
module.exports = xxx
// 相当于
export xxx
ES 6的模块化方案使得原生JavaScript的“拆分”能力提升了一个大的台阶,几乎成为当下最流行的写法,并且应用在大部分的企业项目中。
3. Promise 和 async/await
1. Promise
Promise是一种适用于异步操作的机制,比传统的回调函数解决方案更合理和更强大。从语法上说,Promise是一个对象,从它可以获取异步操作的结果:成功或失败。在Promise中,有三种状态:pending(进行中)、resolved(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,无法被Promise之外的方式改变。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。创建一个Promise对象,代码如下:
var promise = new Promise(function(resolve, reject) {
...
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
在上面的代码中,创建了一个Promise对象,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。这是两个内置函数,resolve函数的作用是将Promise对象的状态变为“成功”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject函数的作用是将Promise对象的状态变为“失败”,在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。当代码中出现错误(Error)时,就会调用catch回调方法,并将错误信息作为参数传递出去。Promise对象实例生成后,可以用then方法分别指定resolved(成功)状态和rejected(失败)状态的回调函数以及catch方法,比如:
promise.then(function(value) {
// success逻辑
}, function(error) {
// failure逻辑
}).catch(function(){
// error逻辑
});
then()方法返回的是一个新的Promise实例(不是原来那个Promise实例)。因此,可以采用链式写法,即then()方法后面再调用另一个then()方法,比如:
getJSON("/1.json").then(function(post) {
return getJSON(post.nextUrl);
}).then(function (data) {
console.log("resolved: ", data);
}, function (err){
console.log("rejected: ", err);
});
下面是一个用Promise对象实现的Ajax操作get方法的例子。
var getJSON = function(url) {
// 返回一个Promise对象
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest(); //创建XMLHttpRequest对象
client.open("GET", url);
client.onreadystatechange = onreadystatechange;
client.responseType = "json"; //设置返回格式为json
client.setRequestHeader("Accept", "application/json");//设置发送格式为json
client.send();//发送
function onreadystatechange() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/data.json").then(function(data) {
console.log(data);
}, function(error) {
console.error(error);
});
了解Promise的基本知识可以便于后续学习使用服务端渲染。当然,Promise的应用场合还是比较多的,如果想要深入了解,可以访问网址:https://developer.mozilla.org/en-US/docs/Web/Java Script/Reference/Global_Objects/Promise,进行系统的学习。
2. async/await
async/await语法在2016年就已经提出来了,属于ES 7中的一个测试标准(目前来看是直接跳过ES 7,列为ES 8的标准了),它主要为了解决下面两个问题:
-
过多的嵌套回调问题
-
以Promise为主的链式回调问题
前面讲解过Promise,虽然Promise解决了恐怖的嵌套回调问题,但是解决得并不彻底,过多地使用Promise会引发以then为主的复杂链式调用问题,同样会让代码阅读起来不那么顺畅,而async/await就是它们的救星。
async/await是两个关键字,主要用于解决异步问题,其中async关键字代表后面的函数中有异步操作,await关键字表示等待一个异步方法执行完成。这两个关键字需要结合使用。当函数中有异步操作时,可以在声明时在其前面加一个关键字async,代码如下:
async function myFunc() {
//异步操作
}
使用async声明的函数在被调用时会将返回值转换成一个Promise对象,因此async函数通过return返回的值会进入Promise的resolved状态,成为then方法中回调函数的参数,代码如下:
// myFunc()返回一个Promise对象
async function myFunc() {
return 'hello';
}
// 使用then方法就可以接收到返回值
myFunc().then(value => {
console.log(value); // hello
})
如果不想使用Promise的方式接收myFunc()的返回值,可以使用await关键字更加简洁地获取返回值,代码如下:
async function myFunc() {
return 'hello';
}
let foo = await myFunc(); // hello
await表示等待一个Promise返回,但是await后面的Promise对象不会总是返回resolved状态,如果发生异常,则进入rejected状态,那么整个async异步函数就会中断执行,为了记录错误的位置和编写异常逻辑的代码,需要使用try/catch,代码如下:
try {
let foo = await myFunc(); // hello
}catch(e){
// 错误逻辑
console.log(e)
}
下面举一个例子,在后面的实战项目开发中,经常会用到数据接口请求数据,接口请求一般是异步操作,例如在Vue的mounted方法中请求数据,代码如下:
async mounted () {
// 代码编写自上而下,一行一行,以便于阅读
let resp = await ajax.get('weibo/list')
let top = resp[0]
console.log(top)
}
在上面的代码中,ajax.get()方法会返回一个Promise,采用await进行了接收,并且await必须包含在一个用async声明的函数中。可以看出,在使用了async/await之后,整个代码的逻辑更加清晰,没有了复杂的回调和烦琐的换行。至此,对于实战项目中用到的相关ES 6语法基本讲解完毕,如果想进一步了解ES 6的更多语法知识,可以自行在其官网上学习。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南