ES6
1. let / var 区别
- let与var都用来声明变量,let只在代码块内有效
- for循环 设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
- var 与 let 不能相互重复声明赋值
- 不能再函数内部重新声明参数
- 块级作用域代替了立即执行函数表达式
- const 声明只读常量,值不能改变,如果为对象则地址不能变也可以改变内部值但是不能重新只想新地址
{
var b = 1;
let a = 2;
}
console.log(b);//1
console.log(b);//a undefined
for(let i = 0;i<10;i++){
console.log(i);//1...10
}
console.log(i);//i not defined
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
//不允许在相同作用域内,重复声明同一个变量
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
function f1() {
let n = 5;
if (true) {
n = 10;
}
console.log(n); // 10
}
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
// IIFE 写法
(function () {
var tmp = ...;
...
}());
// 块级作用域写法
{
let tmp = ...;
...
}
const foo = {};
foo.prop = 123;
foo.prop // 123
foo = {}; // error foo is read-only
2. 变量的解构赋值
从数组或对象中提取, 对变量赋值 为解构
- 解构声明变量新方式
- 解构允许默认值
- 对象的解构
- 函数的解构赋值
- 解构用途
//以前
let name = 'wyc',age=18,hobby='girl';
//现在
let [name,age,hobby] = ['wyc',19,'girl'];
//只有严格等于undefined默认值才会生效
let [name = 'default name'] = [undefined];
console.log(name);//default name
let [age = 'default age'] = [''];
let [hobby = 'default hobby'] = [null];
console.log(age);//
console.log(hobby);// null
let { name,age } = {name:'wyc',age:18};
console.log(name,age);//wyc 18
//变量名必须与属性名同名才能取到值
let { xxx } = {name:'wyc',age:18};
console.log(xxx);//undefined
//将已有对象赋值给新变量
const { log } = console;
log('abc');//abc
//将已有对象赋值给变量,该变量必须是对象已有的属性
const { aaa } = console;
aaa('abc');//aaa is not a function
let {length : len} = 'hello';
len // 5
//字符串解构赋值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
function test([a,b])
return a + b;
}
let abc = test([1,2]
console.log(abc);//3
// 1. 交换变量的值
let a = 111;
let b = 222;
[a, b] = [b, a];
console.log(a,b);//222 111
//2. 从函数返回多个值
function test(){
return [1,3,5];
}
let [a,b,c] = test();
console.log(a,b,c);//1 3 5
function testa(){
return {
aa:1,
bb:2
};
}
let {aa,bb} = testa();
console.log(aa,bb);//1 2
//3. 函数参数定义
// 无序
function fa([x,y,z]){
return [x,y,z];
}
console.log(fa([1,2,3]));//[1,2,3]
// 有序
function fb({x,y,z}){
return {x,y,z};
}
console.log(fb({x:1,y:2,z:3}));//{x:1,y:2,z:3
//4. 提取json
let jsonData = {id:111,name:"jack",age:18};
let {id,name,age:number} = jsonData;
console.log(id,name,number);//111 jack 18
//5. 函数默认值
let testmrz = function(url,{async=true,beforeSend=function(){}} = {}){};
//指定模块方法
const { SourceMapConsumer, SourceNode } = require("source-map");
3. ES6新增函数的扩展
- 函数参数默认值
- 与解构一块使用
- 默认值应用
- rest 参数
- 箭头函数
- 属性的getter/setter
- 模块
- Set和Map
- Promise
- Iterator
- Generator
- reduce()
- async
- Class
- Module语法
//默认值必须为undefined的情况下才会采用
function test(a,b='default b'){
console.log(a,b);
}
test(1,undefined);//1 default b
test(1,'');//1
test(1,333);//1 333
//与解构一块使用
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 报错
//双重默认值则可以省略改参数
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
// 指定参数不能省略
function myError(){
throw new Error('Missing parameter');
}
function test(name = myError()){
return '333';
}
test();//throw new Error('Missing parameter');
test('aaa');
//1. 替代arguments对象
function add(...values){
console.log(Array.from(arguments).sort()); //[ 1, 2, 3, 4, 5 ]
console.log(values); //[ 1, 2, 3, 4, 5 ]
}
add(1, 2, 3, 4, 5);
//2. 必须是最后一个参数
function addb(a,b,...c){}
//3. 用于解构赋值
let obja = {age:18,name:'wyc',hobby:'girl'};
let { age , ...others} = obja;
console.log(age);//18
console.log(others);//{ name: 'wyc', hobby: 'girl' }
//4. 展开可迭代对象
let aa = [1,2]; let bb = [3,4];
aa.push(...bb)
console.log(aa);//[ 1, 2, 3, 4 ]
//注意点:
//1.箭头函数没有自己的this对象,
//2.不能当作构造函数也就是不能用new,
//3.不能使用arguments,可以使用rest参数代替
var test = abc => abc; // var test = function(abc){ return abc;};
//省略参数
var test = () => 3; // var test = function(){ return 3; }
//返回值需要进行逻辑运算
var test = (num1,num2) => { return num1 + num2;}
//如果返回的的为对象,需要在对象外面加上括号()
var test = () => ({age:11,name:'xxx'});// var test = function(){ return {age:11,name:'xxx'}; }
//与解构结合使用
const test = ({age,name}) => age + '' + name; // const test = function(person){ return person.age + '' + person.name; }
//属性的getter/setter
const car = {
price:100,
get price(){return this._price;}, //getter
set price(value){
if(value < 5){
throw new Error('不能小于5')
}
this._price = value;
}
}
//CommonJS 模块输出一组变量
function testa(){
return 3;
}
function testb(){}
module.exports = {testa,testb};
module.exports = {
testFirst:testa,
testLast:testb
}
//模块的单例模式
//single.js
// function singlea(){
// this.foo = 'hello';
// }
// if(!globalThis._foo){
// global._foo = new singlea();
// }
// module.exports = global._foo;
//修改使用symbol后外部无法引用无法修改
// single.js
const FOO_KEY = Symbol('foo');
function singlea(){
this.foo = 'hello';
}
if(!globalThis._foo){
global[FOO_KEY] = new singlea();
}
module.exports = global[FOO_KEY];
//加载上面
const a = require('./single.js');
console.log(a.foo);
//Set 类似数组 但是成员唯一 不可为空
const s = new Set();
[1,2,3,4,5,5,3,2].forEach(x => s.add(x));
console.log(s); //{1, 2, 3, 4, 5}
//1. 可用于去除数组重复
const example = new Set([1,1,2,2,3,3,4,5,6]);
console.log([...example]); //[ 1, 2, 3, 4, 5, 6 ]
//2. 用于去除字符串重复字符
console.log([...new Set('abcddeffggehijk')].join(''));//abcdefghijk
//3. Array.from将set转换数组
console.log(Array.from(new Set([1,2,3,4,5]))); //[1, 2, 3, 4, 5]
//4. WeakSet 与 Set类似 但是只能存对象,弱引用 随时消失
const sss = new WeakSet();
sss.add({age:1});
console.log(sss);
//5. Map
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()];
// [1, 2, 3]
[...map.values()];
// ['one', 'two', 'three']
[...map.entries()];
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map];
// [[1,'one'], [2, 'two'], [3, 'three']]
//过滤
const map1 = new Map(
[...map].filter(([k, v]) => k < 3)
);
//Promise 异步编成解决方案
/**
* 1. 对象状态不受外接影响
* 有三种状态: pending 进行中 /fulfilled 已成功 /rejected 已失败
* resolved 已定型
* 2. 缺点 无法取消 ,如果不设置回调函数 内部错误不会反映到外部
* 3. 是一个构造函数 可以new
*/
/**
*
* Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
* resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
* reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
* then方法作用是为 Promise 实例添加状态改变时的回调函数, 可以接受两个回调函数作为参数
* 第一个回调函数是Promise对象的状态变为resolved时调用
* 第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供
*/
promise.then(function(value) {
// success
}, function(error) {
// failure
});
//异步加载图片的例子
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
// Promise对象实现的 Ajax 操作的例子
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
//then 方式
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
//finally
promise
.then(result => { })
.catch(error => { })
.finally(() => { });
//promise.all()
/**
* 用于将多个 Promise 实例,包装成一个新的 Promise 实例
* Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例
* (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
* (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
*
*/
const p = Promise.all([p1, p2, p3]);
//primise.race()
/**
* 将多个 Promise 实例,包装成一个新的 Promise 实例
* 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
*
*/
//promise.allSettled()
/**
* 确定一组异步操作是否都结束了
*
* 三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator()才会执行
*/
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
// Promise.resolve() 返回一个新的 Promise 对象,状态为resolved
// 将现有对象转化为Promise对象
const jsPromise = Promise.resolve($.ajax('/xxx/xxxx.json'));
//等价于
const jsPromiseaa = new Promise(resolve => resolve($.ajax('/xxx/xxxx.json')));
// Promise.reject() 返回一个新的 Promise 实例,该实例的状态为rejected。
const err = Promise.reject("出错了");
// 同步执行或异步执行统一封装
//1. promise.catch()
Promise.try(() => myfunc.get({id:222}))
.then(value => console.log(value))
.catch(err => console.log(err));
//2. async
const f = () => console.log('now');
(async () => f())().then(value => {}).catch(e =>{});
//Symbol.intarator
/**
* 原生具备 Iterator 接口的数据结构
* Array Map Set String TypedArray 函数的 arguments 对象 NodeList 对象
* Iterator 接口主要供for...of
* 每一次调用next方法 返回一个包含value和done两个属性的对象
*
*
*/
let arr = [1,2,3,4,5];
let iter = arr[Symbol.iterator]();
console.log(iter.next());//{ value: 1, done: false }
console.log(iter.next());//{ value: 2, done: false }
console.log(iter.next());//{ value: 3, done: false }
console.log(iter.next());//{ value: 4, done: false }
console.log(iter.next());//{ value: 5, done: false }
console.log(iter.next());//{ value: undefined, done: true }
for (var i of arr){
console.log(i); // 1, 2, 3,4,5
}
for (let a in arr) {
console.log(a); // 0 1 2 3 4
}
// 给对象添加iterator
function Obj(value) {
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = { next: next };
var current = this;
function next() {
if (current) {
var value = current.value;
current = current.next;
return { done: false, value: value };
}
return { done: true };
}
return iterator;
}
//Generator
/**
*
*
*
* Generator 函数特点:
* 1. function关键字与函数名之间有一个星号
* 2. 函数体内部使用yield表达式,定义不同的内部状态
* 3. yield表达式只能用在 Generator 函数里面
* 4. yield表达式如果用在另一个表达式之中,必须放在圆括号里面 function* demo(){ console.log('xxx' + yield); };
* 5. Generator.prototype.return() 返回给定的值并终结遍历
*
*/
function* testGenerator(){
console.log('aa');
yield 'waite';
console.log('bb');
yield 'ing';
console.log('cc');
yield 'ending';
console.log('dd');
}
// 调用函数并不会执行,必须调用遍历器对象的next方法 使指针指向下一个状态
var tg = testGenerator();
console.log(tg.next());
console.log(tg.next());
console.log(tg.next());
console.log(tg.next());
// aa
// { value: 'waite', done: false }
// bb
// { value: 'ing', done: false }
// cc
// { value: 'ending', done: false }
// dd
// { value: undefined, done: true }
//通过next方法 向内部注入值
function* testa(){
console.log('begin');
console.log('abc:' + `${yield}`);
console.log('def:' + `${yield}`);
return 'result';
}
let ta = testa();
ta.next();//begin
ta.next('222');//abc:222
ta.next('333');//def:333
//通过 Generator 函数部署 Ajax 操作
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
//reduce
/**
* arr.reduce(callback,[initialValue]) 返回计算结果
* callback: 包含四个参数
* 1.previousValue 上次调用回调返回的值
* 2.currentValue 当前元素
* 3.index 当前元素索引
* 4.array 调用reduce的数组
* initialValue: 传递给函数的初始值
*/
var arr = ['a','b','c','d'];
var res = arr.reduce(function(prev,cur,index,arr){
console.log(prev,cur,index,arr);
// a b 1 [ 'a', 'b', 'c', 'd' ]
// ab c 2 [ 'a', 'b', 'c', 'd' ]
// abc d 3 [ 'a', 'b', 'c', 'd' ]
return prev+cur;
})
var res = arr.reduce(function(prev,cur,index,arr){
console.log(prev,cur,index,arr);
// a b 1 [ 'a', 'b', 'c', 'd' ]
// ab c 2 [ 'a', 'b', 'c', 'd' ]
// abc d 3 [ 'a', 'b', 'c', 'd' ]
return prev+cur;
},0)
console.log(res); // abcd
// 求和
arr = [1,2,3,4];
res = arr.reduce((x,y)=>x+y);
console.log(res); //10
//计算数组中每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let nameNum = names.reduce((pre,cur)=>{
if(cur in pre){
pre[cur]++
}else{
pre[cur] = 1
}
return pre
},{})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
//数组去重
let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
if(!pre.includes(cur)){
return pre.concat(cur); //concat 用于连接两个或多个数组 不会更改现有数组,而是返回一个新数组,其中包含已连接数组的值
}else{
return pre
}
},[])
console.log(newArr);// [1, 2, 3, 4]
//Async
/**
* async:
* 异步执行函数
* 1. async表示函数里有异步操作
* 2. async函数的返回值是Promise对象
* 3. 可以使用then方法添加回调函数,函数执行时 遇到await就会先返回,等异步操作完成后再继续执行
* 4. async函数内部错误,会返回Promise的reject,被catch方法回调接收
* 5. await 后是一个Promise对象,返回对象的结果,如果不是Promise对象则直接返回值
*/
async function testa(name){
const aaa = await getAsyncXXX(name);
const bbb = await getAsyncBBB(aaa);
return bbb;
}
testa('abc').then(function(result){console.log(result)});
async function f(){
throw new Error('我错了');
}
f().then(
v => console.log('resolve',v),
e=> console.log('reject',e)
);//reject Error: 我错了
f()
.then(v=>console.log(v))
.catch(e=>console.log(e));
// Class
/**
* ES6的类 通过构造函数使用prototype的一种写法
* 1. 类内部所有定义的方法,都是不可枚举的
* 2. constructor() 方法是类默认方法 new生成对象实例时自动调用,必须有该方法,无显示定义默认空
* 3. 类内部可以使用 get set 对属性存取进行操作
*
*/
class Company1{
constructor(name,address){
this.name = name;
this.address = address;
}
sayHello(){
console.log('hello');
}
}
//等同于
function Company(name,address){
this.name = name;
this.address = address;
}
Company.prototype.sayHello = function(){
console.log('hello');
}
//类的所有方法都定义在prototype上
Company.prototype = {
sayHello(){},
sayno(){}
}
//通过assign一次想类添加多个方法
Object.assign(Company.prototype,{
othera(){},
otherb(){}
})
Object.keys(Company.prototype);
var aa = Object.getOwnPropertyNames(Company.prototype);
console.log(aa);//[ 'sayHello', 'sayno', 'othera', 'otherb' ]
//立即执行的类表达式
let person = new class {
constructor(name){
this.name = name;
}
say(){console.log(this.name);}
}('aa');
person.say();//aa
//静态方法不会被实例继承,只能直接通过类进行调用
//静态方法可以与实例方法重名
class testa{
static cm(){
return 'aa';
}
}
testa.cm();//aa
//私有属性 ES2022添加
//在属性名之前使用#表示
//私有方法 同理, 只能在类内部调用,外部无法调用
//可以使用in 再类内部判断是否是私有属性
class TestB{
#name = 'aaa';
get val(){
return this.#name;
}
}
//继承
/**
* 1. 子类必须在constructor中调用super,否则报错
* 2. 子类无法继承父类私有方法和类
* 3.
*/
class TestA{
constructor(){
this.aaf = 333;
}
}
class TestB extends TestA{
constructor(){
super();
}
}
//module
/**
* 严格模式主要有以下限制:
* 变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with语句
不能对只读属性赋值,否则报错
不能使用前缀 0 表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop,会报错,只能删除属性delete global[prop]
eval不会在它的外层作用域引入变量
eval和arguments不能被重新赋值
arguments不会自动反映函数参数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局对象
不能使用fn.caller和fn.arguments获取函数调用的堆栈
增加了保留字(比如protected、static和interface)
*/
/**
* module: 模块
* 1. 主要有两个命令构成 : export 和 import 。export 命令用于导出模块的变量,import 命令用于加载模块的变量。
* 2. 模块为独立的文件,模块内部的所有变量外部无法获取,如果需要外部读取使用export导出变
* 3. import 输入的变量都是只读的, 但是修改对象的属性是可以的
* 4. import命令具有提升效果,会提升到整个模块的头部,首先执行,静态执行,所以不能使用表达式和变量
* 5. 2020中加入了动态import写法
*/
//aaa.js
var first = 'first';
var second = 'second';
var num = 1111;
function foo(){}
export { first , second , num as third ,foo};
//bbb.js
import {first, second,num,foo} from './aaa.js';
//也可以整体加载
import * as mytest from './aaa.js';
// 在一条import语句中,同时输入默认方法和其他接口
import _, { each, forEach } from 'lodash';
//动态引入
//import()返回 Promise 对象 需要使用then()方法指定处理函
async function test(){
const aa = document.querySelector('#aa'); //document.write(await import('/foo.html')); //returns a promise //aw
if(aa){
const bb = await import('./xxx.js');
bb.show();
}
}
//或者
async function test(){
import('./xxx/js').then(module => {
//.....
})
}
//实例1
async function main() {
const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main();
//script 脚本加载
/**
* defer是“渲染完再执行”
* async是“下载完就执行”
*/
// <script src="https://codegeex.cn" defer></script>
// <script src="https://codegeexxx.cn" async></script>
//浏览器加载ES6 模块
/**
* <script type="module" src="./foo.js"></script>
* 1. 浏览器对于带有type="module"的<script>,都是异步加载
* 等到整个页面渲染完,再执行模块脚本 等同于 defer属性
* 2. 也可以使用async,下载完立即执行,中断渲染执行完毕后再恢复渲染
* <script type="module" src="./foo.js" async></script>
* 3. 允许内嵌在网页中,语法行为与加载外部脚本完全一致
* <script type="module">
import utils from "./utils.js";
// other code
</script>
*4. 利用顶层的this等于undefined这个语法点,可以侦测当前代码是否在 ES6 模块之中
* const isNotModuleScript = this !== undefined;
*/
/**
* JS两种模块:
* 1. ES6模块
* 2. CommonJS 模块(基于Node.js的一套标准库) (基于Chrome V8的客户端)
* 两种不兼容
* 2.1 CommonJS:
* 使用的是 require() 和 module.export
* Node.js 要求ES6模块采用 .mjs后缀名
* 如果不改后缀名,可以在项目package.json中 指定 type字段为module
* 如果又想使用CommonJS 则后缀名改为.cjs 或者指定 type字段为commonjs
* CommonJS 模块的顶层this指向当前模块
*
* 1.1 ES6 Module:
* 使用的是 import 和 export
* ES6 模块之中,顶层的this指向undefined
*
*/
//import命令加载模块
// ./node_modules/es-module-package/package.json
{
"type": "module",
"main": "./src/index.js"
}
import { something } from 'es-module-package';
// 实际加载的是 ./node_modules/es-module-package/src/index.js
// package.json 的 exports 字段
// exports 优先级高于main
/**
* 1. 子目录别名
* {"exports":{ "./submodule":"/src/submodule.js" } }
* 指定src/submodule.js别名为submodule,然后就可以从别名加载这个文件
* import submodule from 'es-module-package/submodule';
* // 加载 ./node_modules/es-module-package/src/submodule.js
*
* 2. main 别名
* {
* "exports":{
* ".":"./main.js"
* }
* }
* //等于
* {
* "exports":"./main.js"
* }
* 3. 条件加载
* // 利用.这个别名,可以为 ES6 模块和 CommonJS 指定不同的入口
* {
* "type": "module",
* "exports": {
* ".": {
* "require": "./main.cjs",
* "default": "./main.js"
* }
* }
* }
*
*/

浙公网安备 33010602011771号