【JavaWeb学习】ES6 ( ECMAScript )

本文详细介绍了ES6中的重要特性,包括Babel的安装与使用,用于将ES6代码转为ES5,以及let和const命令的作用。接着讲解了对象解构赋值、字符串Unicode表示法、模板字符串等。重点讨论了Promise对象,解释了其解决异步编程问题的方式,以及如何通过async函数简化异步操作。最后提到了模块化管理和Class类的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Babel 转码器

1.1. 命令

安装 Babel cnpm install --save-dev @babel/core
安装转码规则 cnpm install --save-dev @babel/preset-env
安装命令行转码工具 cnpm install --save-dev @babel/cli

转码结果输出到标准输出npx babel 待转码文件.js
将转码结果写入到一个文件(--out-file-o参数指定输出文件)
npx babel 待转码文件.js --out-file 输出文件.jsnpx babel 待转码文件.js -o 输出文件.js
整个目录转码(--out-dir-d参数指定输出目录)
npx babel src --out-dir libnpx babel src --d lib

1.2. 作用

Babel 是 ES6 的一部分,是 ES6 新特性;
Babel 是 ES6 的转码器,可以将 ES6 代码转为 ES5 代码;
Babel 是配置文件,配置 ES6 环境;
Babel 是 ES5 的基础知识。

2. Let 和 Const 命令

var关键字:函数集的作用域;
let关键字:块级作用域({}级的作用域,只在一层{}内生效);
const关键字:声明一个只读的常量,一旦声明就不能改变。

2.1. let命令

for循环内很适合使用let 每次循环都创建一个新的i

for(let i=0; i<10; ++i){
    console.log(i); // 0 1 2 3 4 5 6 7 8 9
};
console.log(i); // ReferenceError: i is not defined
var a = [];
for(let i=0; i<10; ++i){
    a[i] = function(){
        console.log(i);
    };
}
a[6](); // 6
for(var i=0; i<10; ++i){
    a[i] = function(){
        console.log(i);
    };
}
a[6](); // 10

let不存在变量提升: let声明的变量必须在声明之后使用

console.log(a); // undefined
var a;

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b;

let不允许重复声明: 不可以在相同作用域内重复声明同一个变量

var i = 10;
var i = 20;
console.log(i); // 20
let i = 1;
console.log(i); // SyntaxError: Identifier 'a' has already been declared

2.2. const命令

const声明的常量不能改变值: 因此在声明的时候必须赋值

const PI = 3.14;
console.log(PI); // 3.14
PI = 2; // TypeError: Assignment to constant variable.
console.log(PI);
const a; // SyntaxError: Missing initializer in const declaration

const在块级作用域内生效
const不存在变量提升: const声明的变量必须在声明之后使用
const不允许重复声明: 不可以在相同作用域内重复声明同一个变量

3. 对象解构赋值

var user = {
    name: "yyt",
    age: 16
};
console.log(user.name, user.age); // yyt 16
// 解构赋值
const {name, age} = user; // 用一组常量将 user 内的属性提取出来,便于以后多次使用
console.log(name, age); // yyt 16

对象的属性没有顺序,变量必须与属性同名才能取到正确的值。
对象的解构赋值可以很方便地将现有对象的方法赋值给某个变量。
如果要将一个已经声明的变量用于解构赋值,必须非常小心(建议不要这样做,何苦呢):

let name = "yyt";
var user = {
    name: "lyl"
};
// let{name} = user; // SyntaxError: Identifier 'name' has already been declared
({name} = user);
console.log(name); // lyl

4. 字符串

  • 字符串Unicode 表示法
    ES6 加强了对 Unicode 的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。
    统一码(Unicode)也叫万国码、单一码,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
  • 字符串遍历器接口
for(let i of "nakahara chuya"){
    console.log(i);
}
  • 模板字符串
    增强版的字符串,用反引号 ` 标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
// 动态创建一个a标签,其 href属性也是动态的
let href = "https://www.bilibili.com";
let name = "nakahara chuya";
// 当我们向页面动态写入标签时,标签的数据一般来源于服务器
var atab = "<a href='"+ href +"'>"+ name +"</a>";
console.log(atab);
// 模板语法
var btab = `<a href="${href}">${name}</a>`;
console.log(btab);
  • 判断一个字符串是否包含在另一个字符串中
    includes():返回布尔值,表示是否找到了参数字符串
    startsWith():返回布尔值,表示参数字符串是否在原字符串的头部
    endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部
    注:支持第二个参数,表示开始搜索的位置。
let s = "Hello World!";
console.log(s.startsWith("llo")); // false
console.log(s.startsWith("llo", 2)); // true
console.log(s.endsWith("!")); // true
console.log(s.includes("Wor")); // true
  • repeat()
    返回一个新字符串,将原来的字符串重复n次。n = 0时返回一个空字符串。
let s = "Hello World! ";
console.log(s.repeat(3)); // Hello World! Hello World! Hello World! 
  • 长度补全
    s.padStart(n, str):如果s的长度小于n,那么用str在头部进行补全;
    s.padEnd(n, str):如果s的长度小于n,那么用str在尾部进行补全;
let s = "yyt";
console.log(s.padStart(10, "sikoyi ")); // sikoyi yyt
console.log(s.padStart(5, "sikoyi ")); // siyyt
console.log(s.padStart(15, "sikoyi ")); // sikoyi sikoyyyt
console.log(s.padEnd(10, "sikoyi ")); // yytsikoyi 
console.log(s.padEnd(5, "sikoyi ")); // yytsi
console.log(s.padEnd(15, "sikoyi ")); // yytsikoyi sikoy
  • 消除空格
    trimStart():消除字符串头部的空格;
    trimEnd():消除尾部的空格;
    返回一个新字符串,不会修改原始字符串。
const s = "  yyt  ";
console.log(s.trimEnd()); // "  yyt"
console.log(s.trimStart()); // "yyt  "
  • at()
    接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)。
    如果参数位置超出索引范围,那么返回undefined
const s = "nakahara chuya";
console.log(s.at(6)); // "r"
console.log(s.at(-6)); // " "
console.log(s.at(-16)); // undefined

5. 数组

  • 扩展运算符 ...:将一个数组转为用逗号分隔的参数序列
    替代函数的apply方法;
    合并数组;
var arr = [10, 20, 30, 40];
console.log(...arr); // 10 20 30 40
// 替代 apply
var es5 = Math.max.apply(null, [14, 3,77]);
var es6 = Math.max(...[14, 3, 77]);
console.log(es5, es6); // 77 77
// 合并数组
var arr1 = [1,2,3,4];
var arr2 = [5,6,7,8];
console.log(arr1.concat(arr2)); // [ 1, 2, 3, 4, 5, 6, 7, 8]
console.log([...arr1, ...arr2]); // [ 1, 2, 3, 4, 5, 6, 7, 8]
  • Array.from():将类数组(arguments、元素集合、类似数组的对象)转换为真正的数组
    类数组只能使用数组的读取方式和length属性,不能使用数组的方法,如push
// arguments:函数中的可选参数
function add(){
    console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3 }
    var result = Array.from(arguments);
    result.push(4);
    console.log(result); // [ 1, 2, 3, 4 ]
}
add(1,2,3);
//元素集合
let links = document.querySelectorAll("a");
console.log(Array.from(links)); // (3) [a, a, a]
// 类似数组的对象
var user = {
    "0":"yyt",
    "1":16,
    "2":"female",
    length:3
};
console.log(Array.from(user)); // (3) ['yyt', 16, 'female']
  • Array.of():将一组值转换为数组
console.log(Array.of(1,2,3,4)); // [ 1, 2, 3, 4 ]

6. 对象

  • 属性的简洁表示方法: 属性名和属性值是同样的变量名称时可以进行省略;属性是函数的也可以进行简写。
let name = "yyt";
let age = 16;
var user = {
    name,
    age,
    sex:"female",
    getName(){ console.log(this.name);}
};
console.log(user); // { name: 'yyt', age: 16, sex: 'female' }
user.getName(); // yyt
  • 属性名表达式
    使用字面量定义对象时,可以用表达式作为对象的属性名,即把表达式放在方括号内。
var iName = "yyt";
var user = {
    [iName]: "yyt",
    age:16
};
console.log(user); // { yyt: 'yyt', age: 16 }
  • 对象的扩展运算符:把对象展开
var z = {a:1, b:2};
var n = {z};
console.log(n); // { z: { a: 1, b: 2 } }
var n = {...z};
console.log(n); // { a: 1, b: 2 }

7. 函数

ES6允许使用箭头=>来声明函数。

var fn1 = function(x, y){
    return x+y;
}
var fn2 = (x,y) => x+y;

如果箭头函数不需要参数或者需要多个参数,那么要用一个空括号代替参数;

var fn3 = () => 1;

如果箭头函数的代码块多于一条语句,那么要用花括号把它括起来,并用return返回;

var fn4 = (x, y) => {
	var z = 10;
	return x+y+z;
};

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外加一层括号,否则会报错;

var fn5 = (x, y) => ({x:1, y:2});

箭头函数的一个用法是简化回调函数(匿名函数)。
注: 箭头函数没有自己的this对象,内部的this就是定义它时上层作用域的this

8. Set 数据结构

类似于数组,成员值唯一,没有重复值。

  • 添加、删除、是否存在、清空;size属性
var s = new Set();
var arr = [1, 1, 2, 3, 4, 5];
arr.forEach(x => s.add(x));
console.log(s); // Set(5) { 1, 2, 3, 4, 5 }
s.delete(2);
console.log(s); // Set(4) { 1, 3, 4, 5 }
console.log(s.has(3)); // true
console.log(s.size); // 4
s.clear();
console.log(s); // Set(0) {}
  • Set函数可以接收一个数组作为参数
  • Set可以使用扩展运算符来读取数据
var s = new Set([1, 2, 3, 4, 5]);
s = [...s];
console.log(s); // [ 1, 2, 3, 4, 5 ]
  • 数组去除重复成员
var s = new Set([1, 1, 2, 3, 4, 5]);
s = [...s];
console.log(s); // [ 1, 2, 3, 4, 5 ]
  • 字符串去除重复字符
var s = new Set("yyt is the greatest");
s = [...s].join('');
console.log(s); // "yt ishegra"

9. Promise 对象

Promise异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。
Promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的API,各种异步操作都可以用同样的方法进行处理。有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外, Promise 对象提供统一的接口,使得控制异步操作更加容易。

  • 语法上说:Promise对象是一个构造函数,用来生成Promise实例
    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署;Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
  • 功能上说:Promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

9.1. promise的优点

  • 指定回调函数的方法更加灵活
    旧方法:必须在启动异步任务前指定;
    promise:启动异步任务 → \rightarrow 返回 promise 对象 → \rightarrow 给promise 对象绑定回调函数 ( 甚至可以在异步任务结束后指定,可以有多个回调函数)
  • 支持链式调用,解决回调地狱的问题
    回调地狱:回调函数嵌套调用,外部回调函数的异步执行的结果是嵌套的回调函数执行的条件

9.2. promise 的状态

状态指的是Promise实例对象中的一个属性PromiseState,有三个可能的值:pending 未决定的;resolved / fullfilled 成功;rejected失败。
状态改变只有两种情况:pending → \rightarrow resolved 或 pending → \rightarrow rejected,一个 promise 对象只能改变一次,无论成功 (value) 还是失败 (reason) 都会返回一个结果数据。

9.3. promise 对象的值

实例对象中的属性 PromiseResult,保存着对象成功/失败的结果。只有回调函数 resolve 和 reject 可以对属性值进行修改,在then方法的回调当中可以把属性值取出来进行后续修改。
在这里插入图片描述

9.4. 如何使用 Promise

  • 构造函数 Promise(excutor){}
    excutor:一个函数,是执行器,(resolve, reject)=>{},它会在程序运行到 new Promise()这里时立即同步调用,在它里面进行异步操作;
    resolveexcutor里的异步操作成功时调用的回调函数,(value)=>{}
    rejectexcutor里的异步操作失败时调用的回调函数,(reason)=>{}
  • 指定回调函数 Promise.prototype.then(onResolve, onReject),返回一个新的 Promise 对象
    onResolveedexcutor里的异步操作成功时resolve()修改了状态后调用的回调函数,(value)=>{}
    onRejectedexcutor里的异步操作失败时reject()修改了状态后调用的回调函数,(reason)=>{}
  • 指定回调函数 Promise.prototype.catch(onReject)
    onRejectedexcutor里的异步操作失败时reject()修改了状态后调用的回调函数,(reason)=>{}
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
 } else {
    reject(error);
 }
});
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

9.5. promise.then()的返回结果

promise实例对象调用then方法后返回一个promise对象,then()接收的参数是两个回调函数,执行回调时

  • 抛出错误,then()返回的promise状态为rejected,值 PromiseResult是错误的值;
  • 返回非 promise 类型的对象,then()返回的promise状态为resolved,值 PromiseResult就是返回值;
  • 返回 promise 对象,then()返回的promise状态和值与改对象相同;
  • 无返回值,then()返回的promise状态为pending,值 PromiseResultundefined

9.11. 注

  • 如何改变 promise 的状态
    resolve(value):如果当前是pending状态,会修改为resolved
    reject(reason):如果当前是pending状态,会修改为rejected
    抛出异常:如果当前是pending状态,会修改为rejected
  • 用多个then()给一个 promise 指定多个成功/失败的回调函数,当状态改变时都会调用
const promise = new Promise(function(resolve, reject) { ... };
promise.then(value=>{}, reason=>{});
promise.then(value=>{}, reason=>{});
  • 改变promise的状态和指定回调函数,先后顺序
    正常情况下,先指定回调,再改变状态:执行器中是异步任务,在异步回调中调用resolve()/reject();当状态发生改变时调用回调函数,得到数据;
    也可以先改变状态,再指定回调:执行器中是同步任务,在执行器中直接调用 resolve()/reject();或者延迟更长的时间才调用then()(比如在then的回调中加个定时器);指定回调时调用回调函数,得到数据。
  • 异常穿透
    当使用 promise 的 then 进行链式调用时,可以在最后指定失败的回调.catch(reason=>{}),前面任何一步中出现了异常,都会调用最后的这个失败回调。
  • 中断 promise 链
    在回调函数中返回一个 pending 状态的 promise 对象,就可以在使用 promise 的 then 进行链式调用时在中间中断,不再调用后面的回调函数。
  • 手写 promise

加载图片

<div id="img"></div>
// url:图片路径
function loadImage(url){
    // resolve 和 reject 这两个名字是固定的,不能修改
    const promise = new Promise(function(resolve, reject){
        // 异步处理:消耗时间的代码
        const img = new Image(); // 创建新的图片对象
        img.src = url; // 加载图片
        img.onload = function(){ // 加载成功
            resolve(img);
        };
        img.onerror = function(){ // 加载失败
            reject(new Error('Could not load image at ' + url));
        };
    });
    return promise; // 返回的 promise 中保存了加载图片这个操作的结果
}
// 我自己的QQ空间里的图片的路径
const promise = loadImage('http://r.photo.store.qq.com/psc?/V12IERdR1cPfzX/ruAMsa53pVQWN7FLK88i5vkRidgmc*LxvhToGvTwO*qaF0WOEtHMYujvzyWnw7uV17PyS7hQfls84YKHq5CTHBDTWtNegFXR.XSHZHR3sSU!/r');
var div = document.getElementById("img");
promise.then(function(data){ // 异步的操作成功
    div.appendChild(data); // data是返回的img的标签,在const img = new Image();时创建
}, function(error){ // 异步的操作失败
    div.innerHTML = "image load faild.";
    console.log(error);
});
  • Promise封装Ajax,让网络请求的异步操作变得更简单
const getJSON = function (url){
    const promise = new Promise(function(resolve, reject){
        // 异步操作:网络请求代码
        const handler = function(){
            if(this.readyState !== 4){ // 状态码为 0 1 2 3 时都代表操作没有完成
                return;
            };
            if(this.status === 200){
                resolve(this.response);
            }else{
                reject(new Error(this.statusText));
            }
        };
        const client = new XMLHttpRequest();
        client.open("GET", url); // 默认请求方式 GET,发送 url 网址
        client.onreadystatechange = handler; // 接收前后端交互的状态
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();
    });
    return promise;
}
getJSON("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php").then(function(data){
    console.log(data);
}, function(error){
    console.log(error);
});

10. Async 函数

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async函数可以将异步操作变为同步操作async函数中可以没有 await,但await必须写在async函数中。

  • async函数的返回值是一个promise对象,该对象的结果由 async 函数执行的返回值决定:
    返回一个普通值:返回的是一个成功的promise对象,对象的值就是这个普通值;
    返回一个promise对象:返回的promise对象和执行结果一样;
    抛出异常:返回的是一个失败的promise对象,对象的值就是异常值;
  • await语句右侧是一个表达式:
    表达式是一个 promise 对象:返回 promise 成功的;如果promise失败,抛出异常,需要通过 try…catch…捕获处理;
    表达式是普通值:返回这个值。
// 定时器是异步的
function print(){
    setTimeout(()=>{console.log("delay 1000 ms");}, 1000);
    console.log("中也!");
}
print();

// async将定时器变为同步效果
function timeout(ms){
    const promise = new Promise(function(resolve, reject){
        setTimeout(function(){console.log("yi?"); resolve()}, ms);
    });
    return promise;
};
// 将 "async" 放在函数定义前,再将 "await" 放在具有异步操作的代码前面。
async function asyncPrint(ms, value){
	console.log("wait... ...");
    await timeout(ms);
    console.log(value);
};
asyncPrint(1000, "quya!");

网络请求之间有依赖关系,很多接口可能要依赖于上一个接口的数据才能执行。

11. Class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。

class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    getName(){
        console.log(this.name);
    }
}
var p = new Person("yyt", 16);
p.getName();

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,那么会默认添加一个空的constructor()类不存在变量提升

  • 实例属性
    类的实例对象可调用的属性,如上面代码中的p.namep.age
  • 实例方法
    通过类的实例对象调用方法,如上面代码中的p.getName()
  • 静态属性
    静态属性指的是Class本身的属性,即 Class.propName,要通过类名添加属性。
class Person{
}
Person.type = "People";
var p = new Person();
console.log(Person.type); // People
  • 静态方法
    类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。如果静态方法包含this关键字,这个this指的是类,而不是实例。
    如果一个实例方法和一个静态方法崇明,那么最终哪个方法被调用要看是通过实例对象调用还是通过类调用。
class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    getName(){
        console.log(this.name);
    }
    static classMethod(){
        console.log("static method Oh~");
        console.log(this.name); // `this`指向当前类,而不是实例对象
    }
}
var p = new Person("yyt", 16);
p.classMethod(); // Uncaught TypeError: p.classMethod is not a function
p.getName(); // yyt
Person.classMethod(); // static method Oh~; Person
  • 继承
    Class可以通过extends关键字实现继承,让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承,要清晰和方便很多。
    ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错,这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。
class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    getName(){
        console.log(this.name);
    }
    static getSpecies(){
        console.log("Person");
    }
}
class Student extends Person{
    constructor(name, age, sex){
        super(name, age);
        this.sex = sex;
    }
    getSex(){
        console.log(this.sex);
    }
}
var s = new Student("yyt", 16, "female");
s.getName(); // yyt
s.getSex(); // female
Student.getSpecies(); // Person

12. Module

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的 require 、Python 的 import ,甚至就连CSS 都有 @import ,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。ES6 模块是通过 export 命令显式指定输出的代码,再通过 import 命令输入。
我们采用Nodejs方式测试Module语法。但是nodejs采用的是CommonJS的模块化规范,使用require引入
模块;而import是ES6的模块化规范关键字。想要使用import,必须引入babel转义支持,通过babel进行编译,使其变成node的模块化代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值