ES6总览

ECMAScript,是由 ECMA 国际标准化组织 制定的一套脚本语言的标准化规范,随着技术的发展,每年都会更新ECMAScript 的标准内容。

  • 📅 2011 年发布 ECMAScript5.1,成为 ISO 国际标准,从而推动所有浏览器都支持。
  • 📅 2015 年 发布 ECMAScript 6,简称 ES6、ECMAScript 2015。从此以后每年都有更新,如ES2016、ES2017,都泛称为**ES2015+**,或者ES6。

从时间上也能看出来,ES2015 是一次重要更新,标志着现代化浏览器的进步,主流的浏览器基本都已原生支持ES6。只是一些古老的浏览器、老设备,如IE、老的Android机器还是存在兼容性问题。于是出现了Babel(及Polyfill),在编译的时候把ES6语法转码到ES5,从而兼容更多浏览器。

新特性 说明 示例
**let** 定义局部变量,块级作用域 let index =1
**const** 定义不可修改常量,作用域同let const max =100
解构赋值 从对象、数组分解数据,赋值给变量let [a,b,c] = [1,2,3] let { name, age, sex } = person;
...剩余参数 将函数剩余的参数放到一个数组了,就可以支持无限个参数了 function fn1(first, ...args)
...展开运算符 把数组、对象的成员展开到当前环境 let obj={...nobj,age:10};
?. || 可选链运算符,不为空(null 或 undefined)时才执行 page=res?.data?.size ||1;
函数
默认参数值 在参数定上赋值默认值值 function fn(param = 'hello')
箭头函数=>{} 简化函数定义,函数体内无this(取决去定义的位置) (para)=>{ 函数体}
name属性 函数新增name属性 (function foo() {}).name
string 字符串扩展
includes(str) 判断是否包含指定的字符串
startsWith(str) 判断是否以指定字符串开头
endsWith(str) 判断是否以指定字符串结尾
repeat(count) 返回重复指定次数的字符串
padStart(),padEnd() 补全字符串到指定长度 "123".padStart(4,'0') //0123
trimStart()、trimEnd() 消除字符串前后的空格
matchAll()
replaceAll(old, new) 替换所有匹配到的字符串,replace只替换第一个
模板字符 支持多行字符串+变量嵌入 Hello ${name}, how are you ${time}?
Number扩展
Number.isNaN() 判断是否等于NaN,window.isNaN是判断是否非数值
Number.isInteger() 是否为整数
Number.parseFloat() 解析浮点数,从第一个非空字符解析获取数字,识别第一个小数点
Number.parseInt() 解析整数,从第一个非空字符解析获取数字,支持进制参数
Math.trunc(i) 去除小数部分
BigInt 数据类型 大整数,只表示整数,没有位数限制,**n**结尾 const a = 2172141653n;
Array扩展
Array.from() 创建一个新的,浅拷贝的数组实例 Array.from(arrayLike,mapFn?)
Array.of() 根据可变参数创建数组:Array.of(v0,v1,…vN)
copyWithin(to,s,e) 浅复制数组中的内容,数组内部的复制、粘贴 copyWithin(target,start=0,end=this.length)
find(func) 查找符合条件的元素值,类似方法findIndex()只返回索引
includes(v) 判断是否包含指定的值
Object扩展
简写属性 变量名作为属性名的简写方式 {name:'sam', address}
keys()/values() 还有entries(),返回对象、数组的可遍历键、值、键值对
获取属性描述信息 Object.getOwnPropertyDescriptor(obj, pname)
Object.is(v1,v2) 判断两个值是否为同一个值,支持值类型、引用类型
Object.assign(t,…f) 合并多个obj对象到target并返回,复制一级属性的值,不能作为深拷贝 Object.assign(target , …objs)
proto 对象的原型,官方推荐的是通过下面的方法来处理
get/setPrototypeOf 返回指定对象的原型,还有设置原型 Object.setPrototypeOf() Object.getPrototypeOf(obj)
Map{[key, value]} 键值对,顺序插入,支持任何类型作为建、值 WeakMap为弱引用Map
Set{key} 、WeakSet 不重复的集合,值的相等判断采用零值相等算法 基于===比较,NaN与自身相对,-0、+0相等
Proxy 对象代理器,拦截对象的get、set行为 new Proxy(target, handler);
Iterator 、for…of 迭代器,一种提供for…of遍历的机制,算是一种接口约定 next()依次遍历
Promise 异步编程,以及他的语法糖async 、await
Class 类,终于有类了。IE:不认识!
Module 模块,模块化编程,export、import

…展开运算符

和剩余参数的语法形式相同,作用比较相似,只是顺序是相反的。剩余参数是合并,将多个参数合并到一个数组中。而扩展运算符是拆分展开,把数组、对象的可遍历成员展开(拷贝)到当前环境。

  1. //剩余参数args,add方法就可以支持无限参数了
  2. function add(...args) {
  3. let sum = 0;
  4. args.forEach(v => sum += v);
  5. return sum;
  6. }
  7. const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  8. console.log(add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
  9. //展开为多个参数,展开运算
  10. console.log(...arr);
  • 展开为多个参数:console.log(...arr)
  • 合并/复制数组:let arr3 =[...arr1, ...arr2]
  • 展开为对象属性:可用于合并对象,把对象的属性、方法合并(拷贝)出来。算是一种代码混入,不过只能“混入”自己具备的属性、方法,不包括原型链上的。
    1. let address = { country: 'china' };
    2. address.sendExpress = function () { }
    3. let family = { father: 'sam', mother: 'lisa' }
    4. //合并对象,复用代码
    5. let user = { age: 100, ...address, ...family }

Module模块

ES6的module模块,可以把复杂的JS拆分为不同的模块,实现模块化封装。一个模块就是一个独立的文件,一个脚本就是一个模块。模块可以相互引用,但必须通过exprotimport关键字来申明导出的接口、引入的接口。因为一个模块(文件)内部是一个独立的作用域,与外界无关。

  • 🔸静态编译:模块的exprotimport都是在编译时执行,会优先执行。属于静态加载,不是运行态的。
  • 🔸严格模式:模块始终是严格模式"use strict",无需申明。
    • 变量必须先申明再使用,没了变量提升。
    • thisundefined,模块的顶级this指向undefined,而不是我们熟悉的全局window对象。
  • 🔸模块作用域,模块的作用域只在模块内,外面无法访问。包括一个<script type="module">内部JS代码块也是如此。
    • 只有通过exprotimport申明的才可以外部访问,这有很好的保护作用。
  • 🔸延迟执行:模块脚本是延迟的,效果类似 defer ,同其他资源一样并行加载。等HTML解析完成才会(顺序)执行,所以需注意顺序。支持async,异步脚本准备好后会立即执行。
  • 在HTML中使用模块,需要 <script **type**="**module**"> 来申明,告诉浏览器这里要被当做模块来对待。包括内联代码、外部引用。

    1. <script type="module" src="./foo.js"></script>
    2. <script type="module">
    3. console.log(1) //后输出
    4. </script>
    5. <script async type="module">
    6. console.log(2) //async 先输出
    7. </script>

    export

    export 申明导出可以给外部访问的接口,可以是任意类型的变量、方法、类,支持任意多个,如export const max = 100;

  • export 的本质是通过接口名与模块内部变量之间,建立了一一对应的关系。

  • export 可以在任何位置使用,一般推荐在最后的地方统一导出,比较清晰。注意由于模块是静态编译执行,只能放到顶级位置,不能在块级作用域里面。
  • as 重命名
  • 导入、导出合并使用,导出另一模块导入的接口,做一个中间商,实现了类似继承的效果。 ```javascript export let userInfo = { name: ‘sam’, age: 100, } let book = { id: ‘001’, price: 100, } function sayHi(name) { console.log(‘Hi!’, name); } export const MAX = 100; //统一导出,as重命名 export { book, sayHi as sayHello }

////// 导入、导出合并使用,把导入的转手导出 /////// export { max, book } from ‘./js/m-user.js’ export * from ‘./js/m-user.js’

  1. <a name="oIVpW"></a>
  2. ## import
  3. **import **从一个模块加载(导入)变量、函数、类:`import { max } from './js/m-user.js'`
  4. - {**接收**}:用一个花括号包裹,接收的变量名称必须和导出时一致。
  5. - 可以用as重命名。
  6. - **url**文件:导入的文件url支持相对路径、绝对路径。也可以只是模块文件名,这时需要配置模块查找方式了。?怎么配置?❓
  7. - **Singleton **模式:`import`模块的代码只会执行一次,同一个url文件只会第一次导入时执行代码。后续任何地方`import`都不会执行模块代码了,也就是说,`import`语句是 Singleton 模式的。
  8. - **只读-共享**:模块导入的接口的是只读的,不能修改。当然引用对象的属性值是可以修改的,不建议这么干,**注意**模块是共享的,导出的是一个引用,修改后其他方也会生效。
  9. - `import`具有提升效果,可以先消费,后导入,因为import是编译阶段执行的。
  10. - `*`一次性整体加载模块中所有接口,到一个as指定的变量上,此时没有花括号。建议按需导出,不用`*`,这样有利于构建工具的优化,剔除没用到的代码。
  11. ```javascript
  12. <script type="module" src="../js/f2.js"></script>
  13. <script type="module">
  14. import { MAX } from './js/m-user.js';
  15. import { book, sayHello as sayHi } from './js/m-user.js';
  16. //整体加载,*一次全部导入到一个引用上
  17. import * as uall from './js/m-user.js';
  18. console.log(MAX, book);
  19. sayHi();
  20. </script>

export default

import加载的时候必须指定模块中的名字,有一种不需要指定名字的方法——设置默认导出的变量:export **default** MAX;

  • 导入时可以随意命名,此时不需要花括号。
  • default只能用一次。
  • 本质上就是输出一个叫做default的接口,把一个变量赋值给default。so,也可以显示的使用default。 ```javascript //** m-user.js *// //设置默认导出接口 const MAX =1000; export default MAX; //只能用一次 export { sayHi as default }

//** test.html *// //导出默认接口 import M from ‘./js/m-user.js’; import {default as MX} from ‘./js/m-user.js’;

  1. <a name="LNyra"></a>
  2. ## import()"函数"
  3. **import() **用于运行时动态加载一个模块,也支持加载非模块的脚本,可以用在任何地方。他是异步的,返回一个`Promise`对象,可以接`then()`,或者用`await`命令。功能类似Node.js 中的`require()`,区别就是`require()`是同步的。
  4. ```javascript
  5. <!-- 动态加载不需要设置type="module" -->
  6. <script>
  7. import('./js/m-user.js').then(m => {
  8. //获得module,所有导出的接口都在m上
  9. console.log(m.book)
  10. m.sayHello("baby");
  11. });
  12. async function dosth() {
  13. let obj = await import('./js/m-user.js');
  14. let { book } = await import('./js/m-user.js');
  15. console.log("book:", obj.book, book);
  16. }
  17. dosth();
  18. </script>

require 和 import 的区别?

require 是 CommonJS 模块的语法,CommonJS 是Node.js 的模块机制,简称CJS。CommonJS 与ES6 的模块并不兼容,两者的作用类似,但用法还是有些差异的。

比较 CommonJS ES6 模块
运行环境 Node.js 浏览器
导出语法 module.exports = { obj } export { obj }
加载语法 require() import、import()
异步加载? 同步加载 异步加载
输出的什么? 导入的是一个值的拷贝,独享 导入的是一个引用,大家共享
  1. //// cm.js ////
  2. function sayHi(name) {
  3. console.log('Hi!', name);
  4. }
  5. module.exports = {
  6. MAX: 100,
  7. sayHi
  8. }
  9. //// main.js ////
  10. const cm = require('./cm.js')

参考资料