ES6
ES 全称 EcmaScript,是脚本语言的规范,而平时经常编写的 JavaScript,是 EcmaScript 的一种实现,
所以,ES 新特性其实指的就是 JavaScript 的新特性。 // ES 6-11,指的是 ES 的几个版本
为什么要学习新特性 ?
- 语法简洁、功能丰富
- 框架开发应用
- 前端开发职位要求
EcmaScript 概述
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。
ECMA( European Computer Manufacturers Association )中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为 Ecma 国际。
- Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个:
(1)ECMA-262( ECMAScript )历史版本: // 从 ES6 开始,每年发布一个版本,版本号比年份最后一位大 1
- 第 1 版 — 1997 年:制定了语言的基本规范
- 第 2 版 — 1998 年:较小改动
- 第 3 版 — 1999 年:引入正则、异常处理、格式化输出等。IE 开始支持
- 第 4 版 — 2007 年:过于激进,未发布
- 第 5 版 — 2009 年:引入严格模式、JSON,扩展对象、数组、原型、字符串、日期方法
- 第 6 版 — 2015 年:模块化、面向对象语法、Promise、箭头函数、let、const、数组解构赋值等等
- 第 7 版 — 2016 年:幂运算符、数组扩展、Async/await 关键字
- 第 8 版 — 2017 年:Async/await、字符串扩展
- 第 9 版 — 2018 年:对象解构赋值、正则扩展
- 第 10 版 — 2019 年:扩展对象、数组方法
- 第 11 版 — 2020 年:链式操作、动态导入等
- ...
(2)谁在维护 ECMA-262
TC39( Technical Committee 39 )是推进 ECMAScript 发展的委员会。其会员都是公司( 其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等 )。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。
(3)为什么要学习 ES6
- ES6 的版本变动内容最多,具有里程碑意义
- ES6 加入许多新的语法特性,编程实现更简单、高效
- ES6 是前端发展趋势,就业必备技能
(4)ES6 兼容性:http://kangax.github.io/compat-table/es6
ES6 新特性
[1] let 关键字:声明局部变量 // 以后声明变量,使用 let 就对了
<script>
let a; // 单个声明
let b,c,d; // 批量声明
let e = 100; // 单个声明,并赋值
let f = 200,g = 'test'; // 批量声明,并赋值
</script>
特性:
- 不允许重复声明
<script> let dog = '小狗'; let dog = '大狗'; // Uncaught SyntaxError: Identifier 'dog' has already been declared </script>
- 块级作用域( 局部变量 )
<script> { let cat = "猫"; console.log(cat); // 猫 } console.log(cat); // index.html:15 Uncaught ReferenceError: cat is not defined </script>
- 不存在变量提升
<script> console.log(dog); // undefined console.log(cat); // Uncaught ReferenceError: Cannot access 'cat' before initialization var dog = "狗"; // 存在声明提升,输出变量的默认值 let cat = "猫"; // 不存在声明提升 </script>
- 不影响作用域链
<script> { let cat = "猫"; function fn(){ console.log(cat); // 猫 } fn(); } </script>
[2] const 关键字:声明常量 // 通常,声明对象类型使用 const,非对象类型声明选择 let
<script>
const DOG = '狗';
console.log(DOG)
</script>
特性:
- 声明必须赋初始值
<script> const DOG; console.log(DOG); // Uncaught SyntaxError: Missing initializer in const declaration </script>
- 常量的标识符一般大写( 纯属习惯 )
- 常量的值不允许修改
<script> const DOG = "狗"; console.log(DOG); // 狗 DOG = "大狗"; console.log(DOG); // Uncaught TypeError: Assignment to constant variable. </script>
- 不允许重复声明,同 let
- 块级作用域( 局部变量 ),同 let
- 对于数组和对象的元素进行修改,不算做对常量的修改,不会报错( 因为地址没有发生变化 )
<script> const arr = [1,2,3] console.log(arr); // [1, 2, 3] arr.push(4); console.log(arr); // [1, 2, 3, 4] </script>
[3] 变量和对象的解构赋值 // 频繁使用对象方法、数组元素,就可以使用解构赋值形式
什么是解构赋值?ES6 允许按照一定模式,从数组和对象中提取值,然后对变量进行赋值,这被称为“解构赋值”。
<script>
// 数组的解构赋值
let [a,b,c] = ['大哥','二哥','三哥'];
console.log(a); // 大哥
console.log(b); // 二哥
console.log(c); // 三哥
// 对象的解构赋值
let {name,run} = {
name:"小明",
age:10,
run(){
console.log('跑步');
}
}
console.log(name); // 小明
run(); // 跑步
</script>
[4] 模板字符串:声明自带格式的字符串 // 当遇到字符串和变量拼接的时候,可以使用模板字符串
模板字符串,是加强版的字符串,用反引号(``)表示,其特点为:
- 自带格式,字符串中可用出现换行
- 变量拼接,可用使用 ${ x } 的形式引用变量
<script> let str1 = `字符串`; let str2 = `字 符 串`; console.log(str1); // 字符串 console.log(str2); // (自带格式) let str3 = `这是一个${str1}`; console.log(str3); // 这是一个字符串 </script>
[5] 简化对象和函数写法
ES6 允许在大括号里,直接写入变量和函数,作为对象的属性和方法,这样的写法更为简洁。
<script>
let name = "小明";
let sing = function(){
console.log('唱歌');
}
let obj = {
// 完整写法
// name:name,
// sing:sing,
// 简化写法
name,
sing,
age:10,
// 方法声明(简化)
run(){
console.log('跑步');
}
}
console.log(obj.name); // 小明
obj.sing(); // 唱歌
obj.run(); // 跑步
</script>
[6] 箭头函数:简化函数写法
ES6 允许使用箭头( => )来定义函数,箭头函数提供了一种更为简洁的函数书写方式,多用于匿名函数的定义。
<script>
function func1() {
console.log('方法1');
}
func1(); // 方法1
// ES6写法
let func2 = () => {
console.log('方法2')
}
func2(); // 方法2
</script>
其特点为:
- 箭头函数不能作为构造函数实例化;
- 不能使用 arguments;
- 如果形参只有一个,小括号可以省略;
- 如果函数体只有一条语句,花括号也可以省略,函数的返回值为该条语句的执行结果;
<script> let func3 = function (a, b) { return a + b; } console.log(func3(10, 20)); // 30 // ES6 写法 let func4 = (a, b) => { return a + b; } console.log(func4(20, 50)); // 70 </script>
- 箭头函数的 this 是静态的,始终指向声明时所在作用域下的 this 值;
<body> <div id="sec1" style="width:100px;height: 100px;background: blue;"></div> <div id="sec2" style="width:100px;height: 100px;background: blue;"></div> <script> // 效果:点击 div#sec,2秒后颜色变为粉色(pink) let sec1 = document.getElementById('sec1'); let sec2 = document.getElementById('sec2'); // 传统写法 sec1.addEventListener('click',function(){ let that = this; setTimeout(function(){ // 报错 // this.style.background = "pink"; // Uncaught TypeError: Cannot set properties of undefined (setting 'background') // 解决 that.style.background = "pink"; },2000) }) // ES6 写法 sec2.addEventListener('click',function(){ setTimeout(() => { this.style.background = "red"; },2000) }) </script> </body>
[7] ES6 中函数参数的默认值:给函数的参数设置默认值
<script>
// 默认值
function func(a,b,c = 10){
return a + b + c;
}
console.log(func(1,2)); // 13
// 与解构赋值配合使用
function connect({host="127.0.0.1",username,password,port}){
console.log(host); // 127.0.0.1
console.log(username); // root
console.log(password); // root
console.log(port); // 8080
}
connect({
username:'root',
password:'root',
port:8080
})
</script>
[8] rest 参数:获取实参( 实际上是函数的剩余参数 ) // ES6 rest 参数
<script>
let func1 = function () {
console.log(arguments) // Arguments(3) ['大哥', '二哥', '三哥', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Rest 参数简化了使用 arguments 获取多余参数的方法
var args = Array.prototype.slice.call(arguments);
console.log(args); // ['大哥', '二哥', '三哥']
}
func1("大哥", "二哥", "三哥");
// ES6引入了rest参数( ...args ),rest参数必须放在最后,否则会报错
let func2 = function (...args) {
console.log(args); // ['大哥', '二哥', '三哥']
}
func2("大哥", "二哥", "三哥");
let func3 = function (a,b,...args) {
console.log(a); // 大哥
console.log(args); // ['三哥']
}
func3("大哥", "二哥", "三哥");
</script>
[9] 扩展运算符:将一个数组转化为用逗号分隔的参数序列
扩展运算符( ... ),好比 rest 参数的逆运算,将一个数组转化为用逗号分隔的参数序列,对数组进行解包。
<script> let arr = ['大哥','二哥','三哥']; let func = function (a, b, ...args) { console.log(a); console.log(args); } func(...arr); // 功能:数组合并 let arr1 = [1,2]; let arr2 = [3,4]; // 传统方式 let arr3 = arr1.concat(arr2); console.log(arr3); // [1,2,3,4] // 使用扩展运算符 let arr4 = [...arr1,...arr2]; console.log(arr4); // [1,2,3,4] </script> <script> let func = function(){ console.log(arguments); // 伪数组 // Arguments(3) ['大哥', '二哥', '三哥', callee: ƒ, Symbol(Symbol.iterator): ƒ] // 将伪数组转化为真正的数组 console.log([...arguments]); // ['大哥', '二哥', '三哥'] } func('大哥','二哥','三哥'); </script>
[10] Symbol:表示独一无二的值 // Symbol 内置值的使用( 了解 )
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JS 的第 7 种数据类型,一种类似于字符串的数据类型
<script>
// 创建Symbol - 使用函数 Symbol() 创建
// 方式1:
let s1 = Symbol(); // 说明:这是一个随机的值
console.log(s1,typeof s1); // Symbol() 'symbol'
let s2 = Symbol('只是一个标识'); // Symbol(只是一个标识)
console.log(s2);
let s3 = Symbol('只是一个标识'); // Symbol(只是一个标识)
console.log(s3 == s2); // false
// 方式2:
let s4 = Symbol.for('只是一个标识');
console.log(s4,typeof s4); // Symbol(只是一个标识) 'symbol'
let s5 = Symbol.for('只是一个标识');
console.log(s5 == s4); // true
</script>
其特点为:
- Symbol 的值是唯一的,用来解决命名冲突的问题;
<script> // Symbol 的主要应用场景是给对象添加独一无二的属性和方法 const game = { name:'游戏名', up(){ console.log('向上') }, down(){ console.log('向下') } } // 把它作为一个唯一的值,用来解决命名冲突的问题 let method = { up:Symbol(), down:Symbol() } game[method.up] = function(){ console.log('upup') } game[method.down] = function(){ console.log('down') } game.up(); // 向上 game[method.up](); // upup </script>
- Symbol 的值不能与其他数据进行计算;
<script> // Symbol 的值不能与其他数据类型进行计算 let s6 = Symbol(); // console.log(s6 + 100); // Uncaught TypeError: Cannot convert a Symbol value to a number // console.log(s6 + 'test'); // Uncaught TypeError: Cannot convert a Symbol value to a string </script>
- Symbol 定义的对象属性不能使用 for ... in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名;
- Symbol 的内置值:除了使用自己定义的 Symbol 值外,ES6 还提供了 11 个 内置的 Symbol 值,指向语言内部使用的方法,可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行
说明:Symbol 内置值的使用,都是作为某个对象类型的属性去使用
<script> class Person{ static [Symbol.hasInstance](param){ console.log(param); // {name: '对象'},可以作为一个参数被传递进来 console.log('进行类型检测了') } } let obj = {name:'对象'}; // 当进行类型检测的时候就会被触发 obj instanceof Person; // 进行类型检测了 </script>
[11] 迭代器:用来遍历集合、数组等
遍历器( Iterator )就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
ES6 创造了一种新的遍历命令 for ... of 循环,Iterator 接口主要供 for ... of 消费;
原生具备 Iterator 接口的数据( 可以使用 for ... of 遍历 )有:
- Array
- Arguments
- Set
- Map
- String
- TypeArray
- NodeList
工作原理:
<script> const arr = ['唐僧','孙悟空','猪八戒','沙僧']; for(let v of arr){ console.log(v); // 依次打印:唐僧 孙悟空 猪八戒 沙僧 } for(let i in arr){ console.log(i); // 依次打印:0 1 2 3 } let iterator = arr[Symbol.iterator](); console.log(iterator.next()); // {value: '唐僧', done: false} console.log(iterator.next()); // {value: '孙悟空', done: false} console.log(iterator.next()); // {value: '猪八戒', done: false} console.log(iterator.next()); // {value: '沙僧', done: false} console.log(iterator.next()); // {value: undefined, done: true} </script>
- 创建一个指针对象,指向当前数据结构的起始位置;
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员;
- 接下来不断调用 next 方法,指针一直向后移动,直到指向最后一个成员;
- 每调用 next 方法,返回一个包含 value 和 done 属性的对象。
应用场景:需要自定义遍历数据的时候,要想到迭代器。
<script> let obj = { name: '一班', member: ['小明', '小红', '小白'], // 自定义遍历对象 [Symbol.iterator]: function () { let index = 0; let that = this; return { next: function () { if (index < that.member.length) { const result = { value: that.member[index], done: false } index++; return result; }else{ return {value:undefined,done:true} } } } } } // 自定义遍历对象 — 要求:输出member中的元素 for (let v of obj) { console.log(v); } </script>
[12] 生成器:一种异步编程解决方案
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完成不同。
<script>
// 传统函数
function gen1(){
console.log(111);
console.log(222);
console.log(333);
}
gen1(); // 一次打印
// 生成器函数:在function和函数名之间加一个 *
function * gen2(){
console.log(111);
console.log(222);
console.log(333);
}
// gen(); // 无法调用
// 其返回值实际是一个迭代器
let iterator2 = gen2();
iterator2.next(); // 可以调用
// 同时,可以搭配yield使用:yield 函数代码的分隔符
function * gen3(){
console.log(111);
yield '片段1'
console.log(222);
yield '片段2'
console.log(333);
yield '片段3'
}
let iterator3 = gen3();
console.log(iterator3.next()) // 执行片段1部分:111 {value: '片段1', done: false}
// ...
// 因为是一个迭代器,因此可以使用 for of 遍历
for(let v of gen3()){
console.log(v)
}
</script>
生成器函数的参数传递:
<script> function* gen(arg) { console.log(arg); let one = yield 111; console.log(one); let two = yield 222; console.log(two); let three = yield 333; console.log(three); } let iterator = gen("AAA"); console.log(iterator.next()); // 会执行yield 111; // next()方法是可以传入参数的,传入的参数作为第一条(上一条)语句yield 111的返回 console.log(iterator.next("BBB")); // 会执行yield 222; console.log(iterator.next("CCC")); // 会执行yield 333; console.log(iterator.next("DDD")); // 继续往后走,未定义; </script>// 代码示例:
<script> // 异步编程 文件操作 网络操作(ajax,request) 数据库操作 // 需求:1s后控制台输出111 再过2s后控制台输出222 再过3s后控制台输出333 // 一种做法:回调地狱 // setTimeout(()=>{ // console.log(111); // setTimeout(()=>{ // console.log(222); // setTimeout(()=>{ // console.log(333); // },3000) // },2000) // },1000) // 另一种做法 function one(){ setTimeout(() => { console.log(111); iterator.next(); }, 1000) } function two() { setTimeout(() => { console.log(222); iterator.next(); }, 1000) } function three() { setTimeout(() => { console.log(333); iterator.next(); }, 1000) } function* gen() { yield one(); yield two(); yield three(); } // 调用生成器函数 let iterator = gen(); iterator.next(); </script>// 代码示例:
<script> // 模拟获取: 用户数据 订单数据 商品数据 function getUsers() { setTimeout(() => { let data = "用户数据"; // 第二次调用next,传入参数,作为第一个的返回值 iterator.next(data); // 这里将data传入 }, 1000); } function getOrders() { setTimeout(() => { let data = "订单数据"; iterator.next(data); // 这里将data传入 }, 1000); } function getGoods() { setTimeout(() => { let data = "商品数据"; iterator.next(data); // 这里将data传入 }, 1000); } function* gen() { let users = yield getUsers(); console.log(users); let orders = yield getOrders(); console.log(orders); let goods = yield getGoods(); console.log(goods); } let iterator = gen(); iterator.next(); </script>
[13] Promise:非常强大的一步编程的新解决方案
Promise 是 ES6 引入的异步编程的新解决方案。
语法上 Promise 是一个构造函数,用来封装异步操作并可以获取成功或者失败的结果。
- Promise 构造函数: Promise (excutor) {}
<script> // Promise 对象的三种状态:初始化、成功、失败 // 初始化:实例化Promise对象 let p = new Promise(function(resolve,reject){ let data = 123; // resolve(data); // 当使用resolve返回时,为成功状态,可以使用then方法的第一个函数接收数据 reject('出错了!') // 当使用reject返回时,为失败状态,可以使用then方法的第二个函数接收,或者使用.catch方法接收 }) // then 方法 p.then(function(value){ console.log(value) },function(reason){ console.log(reason) // 出错了! }) // catch 方法,一个语法糖,用起来更简洁一些 p.catch(function(reason){ console.log(reason) // // 出错了! }) </script>
代码示例:使用 Promise 封装读取文件
let fs = require('fs'); // Nodejs 常规写法 fs.readFile('./index.html', (err, data) => { if (err) { return; } console.log(data.toString()) }) // Nodejs 使用 Primise 封装写法 let p = new Promise((resolve, reject) => { fs.readFile('./index.html', (err, data) => { if (err) { reject(err) } resolve(data) }) }) p.then(value => { console.log(value.toString()) }).catch((err) => { console.log(err) })
- Promise.prototype.then 方法的链式调用
<script> // 创建 Promise 对象 const p = new Promise((resolve, reject) => { setTimeout(() => { resolve("用户数据"); }, 1000); }); // 调用then方法,then方法的返回结果是promise对象, // 对象的状态有回调函数的结果决定; const result = p.then(value => { console.log(value); // 1、如果回调函数中返回的结果是 非promise 类型的数据, // 状态为成功,返回值为对象的成功值resolved // [[PromiseStatus]]:"resolved" // [[PromiseValue]]:123 // return 123; // 2、如果...是promise类型的数据 // 此Promise对象的状态决定上面Promise对象p的状态 // return new Promise((resolve,reject)=>{ // // resolve("ok"); // resolved // reject("ok"); // rejected // }); // 3、抛出错误 // throw new Error("失败啦!"); // 状态:rejected // value:失败啦! }, reason => { console.error(reason); }) // 链式调用 // then里面两个函数参数,可以只写一个 p.then(value=>{},reason=>{}).then(value=>{},reason=>{}); console.log(result); </script>代码示例:
// 1、引入 fs 模块 const fs = require("fs"); // 2、调用方法,读取文件 - 回调地狱写法 // fs.readFile("resources/text.txt", (err, data1) => { // fs.readFile("resources/test1.txt", (err, data2) => { // fs.readFile("resources/test2.txt", (err, data3) => { // let result = data1 + data2 + data3; // console.log(result); // }); // }); // }); fs.readFile("resources/test2.txt", (err, data3) => { let result = data1 + data2 + data3; console.log(result); }); // 3、使用Promise实现 - Promise 写法 const p = new Promise((resolve, reject) => { fs.readFile("resources/text.txt", (err, data) => { resolve(data); }); }); p.then(value => { return new Promise((resolve, reject) => { fs.readFile("resources/test1.txt", (err, data) => { resolve([value, data]); }); }) }).then(value => { return new Promise((resolve, reject) => { fs.readFile("resources/test2.txt", (err, data) => { // 存入数组 value.push(data); resolve(value); }); }) }).then(value => { console.log(value.join("\r\n")); })
[14] Set 集合:类似数组,但元素不重复的集合
ES6 提供了一种新的数据结构 Set( 集合 ),它类似于数组,但成员的值都是唯一的( 没有重复 )。
集合实现了 Iterator 接口,因此,可以使用 “ 扩展运算符 ” 和 “ for ... of ”。
集合的属性和方法有:
- size 属性,返回集合的元素个数;
- add() 方法,增加一个新元素,返回当前集合;
- delete() 方法,删除一个元素,返回 boolean 值;
- has() 方法,检测集合中是否包含某个元素,返回 boolean 值;
- clear() 方法,清空集合,返回 undefined;
<script> let s = new Set(); console.log(s,typeof s); // Set(0) {size: 0} 'object' let s1 = new Set(['大哥','二哥','三哥','三哥','大哥']) // 自动去重 console.log(s1); // Set(3) {'大哥', '二哥', '三哥'} // 返回集合的元素个数 console.log(s1.size); // 3 // add() 增加一个新元素,返回当前集合 console.log(s1.add('大姐')); // Set(4) {'大哥', '二哥', '三哥', '大姐'} // delete() 删除一个元素,返回boolean值 console.log(s1.delete('二哥')); // true console.log(s1); // Set(3) {'大哥', '三哥', '大姐'} console.log(s1.delete('二弟')) // false // has() 判断集合中是否包含某个元素,返回boolean值 console.log(s1.has('大姐')); // true console.log(s1.has('三弟')); // false // clear() 清空集合,返回undefined console.log(s1.clear()) // undefined console.log(s1) // Set(0) {size: 0} </script>
代码示例:
<script> let arr = [1,2,3,4,5,6,5,4,3,2,1]; // 数组去重 let s = new Set(arr); console.log(s); // Set(6) {1, 2, 3, 4, 5, 6} // 转化为数组 arr = [...s]; console.log(arr); // [1, 2, 3, 4, 5, 6] // 取两个数组的交集 let arr1 = [3,4,5,6,7,8,3,4,5] let s1 = new Set(arr1); let result1 = []; arr.filter(item => { if(s1.has(item)){ result1.push(item); } }) console.log(result1); // [3, 4, 5, 6] // 取两个数组的并集 let result2 = [...new Set([...arr,...arr1])]; console.log(result2); // [1, 2, 3, 4, 5, 6, 7, 8] </script>
[15] Map 集合:键值对集合 — 升级版的“对象”
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。
但是,“键”的范围并不局限于字符串,各种类型的值( 包括对象 )都可以作为键。
Map 集合也实现了 Iterator 接口,所以,可以使用 “ 扩展运算符 ” 和 “ for ... of ”。
Map 的属性和方法:
- size 属性,返回 Map 集合的元素个数;
- set() 方法,增加一个新元素,返回当前 Map;
- delete() 方法,删除一个元素,返回 boolean 值;
- get() 方法,返回键名对象对应的键值;
- has() 方法,检测 Map 集合中是否包含某个元素,返回 boolean 值;
- clear() 方法,清空集合,返回 undefined;
<script> let m1 = new Map(); // 创建一个空集合 console.log(m1,typeof m1); // Map(0) {size: 0} 'object' let m2 = new Map([ ['name','名字'], ['slogan','宣传标语'] ]) console.log(m2); // Map(2) {'name' => '名字', 'slogan' => '宣传标语'} console.log(m2.size) // 2 m2.set('address','地址') console.log(m2) // Map(3) {'name' => '名字', 'slogan' => '宣传标语', 'address' => '地址'} // 删除 m2.delete('name'); console.log(m2); // Map(2) {'slogan' => '宣传标语', 'address' => '地址'} console.log(m2.get('slogan')) // 宣传标语 console.log(m2.has('address')) // true console.log(m2.clear()); // undefined console.log(m2) // Map(0) {size: 0} </script>
[16] Class 类:像 Java 实体类一样声明 JS 类
ES6 提供了更接近传统语言的写法,引入了 class( 类 )这个概念,作为对象的模板。
通过 class 关键字,可以定义类。
基本上,ES6 的 class 可以看作是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更加面向对象编程的语法而已。
<script>
// ES5 构造函数写法
function Tel(brand,price){
this.brand = brand;
this.price = price;
}
Tel.prototype.call = function(){
console.log('拨打电话')
}
let xiaomi = new Tel('小米',3000);
console.log(xiaomi.brand,xiaomi.price); // 小米 3000
xiaomi.call(); // 拨打电话
// class 写法
class Phone {
// 构造函数
constructor(brand,price){
this.brand = brand;
this.price = price;
}
call(){
console.log('打电话');
}
}
let huawei = new Phone('华为',5000);
console.log(huawei.brand,huawei.price); // 华为 5000
huawei.call(); // 打电话
</script>
- static 定义静态属性和方法
<script> class Phone{ static name = '名字' static change(){ console.log('方法执行了') } call(){ console.log('打电话') } } let xiaomi = new Phone(); console.log(xiaomi.name) // undefined Phone.change(); // 方法执行了 // xiaomi.change(); // xiaomi.change is not a function xiaomi.call(); // 打电话 </script>
- class 类继承 & 对父类方法进行重写
<script> class Phone{ constructor(brand,price){ this.brand = brand; this.price = price; } call(){ console.log('打电话') } } class SmartPhone extends Phone{ constructor(brand,price,color){ super(brand,price); this.color = color; } // 对父类方法进行重写 call(){ console.log('视频通话') } } let huawei = new SmartPhone('华为',5000,'黑色'); console.log(huawei); // SmartPhone {brand: '华为', price: 5000, color: '黑色'} huawei.call(); // 视频通话 </script>
- class 中的 getter 和 setter 设置
<script> class Phone{ get price(){ console.log('价格被读取了') return 1000 } set price(args){ console.log('价格被设置了') this._price = args; // this.price = args; // 不要这样写,因为给this.price赋值的时候,会调用 set price,这样会导致无限递归直到栈溢出。 } } let xiaomi = new Phone(); console.log(xiaomi.price); // 价格被读取了 1000 xiaomi.price = 2999; // 价格被设置了 console.log(xiaomi._price); // 2999 </script>
[17] 数值扩展:增加一些数值相关的方法等
- Number.EPSILON
Number.EPSILON 是 JavaScript 表示的最小精度;
EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16;
<script> // Number.EPSILON console.log(Number.EPSILON); // 2.220446049250313e-16 // 数值相加 console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.1 + 0.2 == 0.3); // false // 实际上,我们可以这样认为:如果两个值的精度相差小于 Number.EPSILON,则这两个值相等 function equal(a,b){ return Math.abs(a - b) < Number.EPSILON } console.log(equal(0.1 + 0.2,0.3)); // true </script>
- 二进制和八进制:ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
<script> // 二进制和八进制 console.log(0b1010); // 二进制 10 console.log(0o777); // 八进制 511 console.log(100); // 十进制 100 console.log(0xff); // 十六进制 255 </script>
- Number.isFinite() 用来检查一个数值是否为有限的
<script> // 判断一个数值是否为有限,如果是有限数字,返回true,否则返回false // Number.inFinite 如果检测值不是Number类型,则返回false // 也就是说,只有数值类型的值,且是有穷的,才会返回true console.log(Number.isFinite(100)); // true console.log(Number.isFinite(100/0)); // false console.log(Number.isFinite(Infinity)); // false </script>
- Number.isNaN() 用来检查一个值是否为 NaN
<script> // 检测一个数值是否为 NAN // Number.isNaN 如果检测值不是 Number 类型,直接返回false // 也就是说,只有数值类型的值,且是NaN,才会返回true // 具体可以看百科中,返回 NaN 的运算 console.log(Number.isNaN(NaN)) // true console.log(Number.isNaN(0/0)) // true console.log(Number.isNaN(123)); // false console.log(Number.isNaN('测试')) // false </script>
- Number.parseInt() 与 Number.parseFloat()
ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变;
<script> // Number.parseInt() 与 Number.parseFloat() console.log(Number.parseInt('1234.23sdfsfd')); // 1234 console.log(Number.parseFloat('1234.23sdfsfd')) // 1234.23 </script>
- Math.trunc 用于去除一个数的小数部分,返回整数部分
<script> // Math.trunc 用于去除一个数的小数部分,返回整数部分 console.log(Math.trunc('1234.23sdfsfd')); // NaN console.log(Math.trunc(1234.23)); // 1234 </script>
- Number.isInteger() 用来判断一个数值是否为整数
<script> // Number.isInteger() 用来判断一个数值是否为整数 console.log(Number.isInteger(123)); // true console.log(Number.isInteger(123.12)); // false console.log(Number.isInteger('123')) // false </script>
[18] 对象扩展:增加一些对象相关的方法等
ES6 新增了一些 Object 对象的方法:
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
<script> // Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN) console.log(Object.is(123,123)); // true console.log(Object.is(123,'123')); // false // 但不同的是, console.log(Object.is(NaN,NaN)); // true // NaN与任何数值做===比较都是false,跟他自己也如此 console.log(NaN === NaN); // false </script>
- Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
<script> // Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象 const obj1 = { http:'http://127.0.0.1', port:8080 } const obj2 = { http:'http://127.0.0.1', port:8081 } // 如果前后都有,后面的会覆盖前面的 Object.assign(obj1,obj2); console.log(obj1); // {http: 'http://127.0.0.1', port: 8081} console.log(obj2); // {http: 'http://127.0.0.1', port: 8081} // 如果前边没有后边有,会添加 Object.assign(obj1,{ http:'http://localhost', port:9000, username:'root', password:'root' }) console.log(obj1); // {http: 'http://localhost', port: 9000, username: 'root', password: 'root'} </script>
- proto、setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型
<script> // __proto__、setPrototypeOf、 getPrototypeOf 可以直接设置对象的原型 // 这种方式,并不推荐使用 const school = { name:'学校名' } const city = { area:['北京','杭州'] } // 设置school的原型为city Object.setPrototypeOf(school,city); console.log(school) // 获取原型对象 console.log(Object.getPrototypeOf(school)); // {area: Array(2)} </script>
[19] 模块化 ?!:简单来说就是,将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来
[20] Babel 对 ES6 模块化代码转换( 为了适配浏览器,将更新的 ES 规范转换为 ES5 规范 )
Babel 是一个 JavaScript 编译器,能够将新的ES规范语法转换成 ES5 的语法。
因为不是所有的浏览器都支持最新的 ES 规范,所以,一般项目中都需要使用 Babel 进行转换
步骤:使用 Babel 转换 JS 代码 — 打包成一个文件 — 使用时引入即可
安装工具:
- babel-cli( 命令行工具 )
- babel-preset-env( ES转换工具 )
- browserify( 打包工具, 项目中使用的是 webpack )
- 初始化项目
nom init -y
- 安装
npm i babel-cli babel-preset-env browserify -D
- 使用 babel 转换
npx babel js(js目录) -d dist/js(转化后的js目录) --presets=babel-preset-env
- 打包
npx browserify dist/js/app.js -o dist/bundle.js
- 在使用时引入 bundle.js
<script src="./js/bundle.js" type="module"></script>
ES7 新特性
- Array.prototype.includes 用来检测数组中是否包含某元素,返回值为布尔值,语法:arr.includes(元素值)
<script> // 判断数组中是否包含某个元素 let arr = [1,2,3,4,5]; console.log(arr.includes(1)); // true console.log(arr.includes(6)); // false </script>
- 指数运算符 ** ,即:幂运算的简化写法,例如:2的10次方:2**10
<script> console.log(2 ** 10); // 1024 console.log(Math.pow(2,10)); // 1024 </script>
ES8 新特性
- async 函数 + await 表达式:异步函数
async 函数,其返回值为 promise 对象;而,promise 对象的结果由 async 函数执行的返回值决定
<script> async function func(){ // return 123; // Promise {<fulfilled>: 123} // 若报错,则返回的Promise对象也是错误的 // throw new Error('出错啦'); // Promise {<rejected>: Error: 出错啦 // 如果返回的是Promise对象,那么返回的结果就是Promise对象的结果 return new Promise((resolve,reject)=>{ resolve('成功啦'); // Promise {<pending>} 成功 }) } let result = func(); // console.log(result) // 调用 then 方法 result.then(value => { console.log(value) // 成功啦 },reason => { console.log(reason) }) </script>await 函数,await 必须写在 async 函数中;await 右侧的表达式一般为 promise 对象;await 返回的是 promise 成功的值;如果 await 的 promise 失败了,就会抛出异常,需要通过 try ... catch 捕获处理。
<script> // async 函数 + await 表达式:异步函数 // 函数调用,返回一个promise对象 function func(){ return new Promise((resolve,reject) => { let data = '成功啦'; resolve(data); }) } // 或者,创建一个 promise 对象 let p = new Promise((resolve,reject) => { resolve('成功执行') }) async function fn(){ let res1 = await func(); console.log(res1); // 成功啦 let res2 = await p; console.log(res2); // 成功执行 } fn(); </script>
- 对象方法扩展
<script> // Object.values()方法:返回一个给定对象的所有可枚举属性值的数组; // Object.entries()方法:返回一个给定对象自身可遍历属性 [key,value] 的数组; // Object.getOwnPropertyDescriptors() 该方法:返回指定对象所有自身属性的描述对象; let obj = { name:'名称', age:18 } console.log(Object.keys(obj)); // ['name', 'age'] // 键 console.log(Object.values(obj)); // ['名称', 18] // 值 console.log(Object.entries(obj)); // [['name', '名称'],['age', 18]] console.log(Object.getOwnPropertyDescriptors(obj)); // 说明:一个详细的描述对象 </script>
ES9 新特性
- 在对象中使Rest参数与spread扩展运算符
<script> // 在对象中使用扩展运算符 function connect({host,port,...user}){ console.log(host); // http://127.0.0.1 console.log(port); // 8080 console.log(user); // {username: 'root', password: 'root'} } connect({ host:'http://127.0.0.1', port:8080, username:'root', password:'root' }) </script><script> // 对象中的rest参数 const skill1 = { q:'技能1' } const skill2 = { w:'技能2' } const skill3 = { e:'技能3' } const skill4 = { r:'技能4' } // 对象合并 const skill = {...skill1,...skill2,...skill3,...skill4} console.log(skill); // {q: '技能1', w: '技能2', e: '技能3', r: '技能4'} </script>
正则扩展:简化和增强正则匹配
ES10 新特性
Object.fromEntries 将二维数组或者map转换成对象trimStart 和 trimEnd 去除字符串前后的空白字符Array.prototype.flat 与 flatMap 将多维数组降维Symbol.prototype.description 获取Symbol的字符串描述
ES11 新特性
String.prototype.matchAll 用来得到正则批量匹配的结果类的私有属性:私有属性外部不可访问直接Promise.allSettled 获取多个promise执行的结果集可选链操作符:简化对象存在的判断逻辑动态 import 导入:动态导入模块,什么时候使用什么时候导入BigInt:大整型- globalThis 对象:始终指向全局对象 window
<script> // Window {window: Window, self: Window, document: document, name: '', location: Location, …} console.log(globalThis); console.log(window); </script>
