ECMAScript6常用部分

220 阅读6分钟

一、基础概念

导入导出(CommonJS规范和ES6规范)
(1) CommonJS模块是运行时加载,ES6模块是编译时输出
(2) CommonJS模块输出的是一个值的复制,ES6模块输出的是值的引用
(3CommonJS加载的是整个模块,即将所有的方法全部加载进来,ES6可以单独加载其中的某个方法
(4) CommonJSthis指向当前模块,ES6this指向undefined
(5) CommonJS默认非严格模式,ES6的模块自动采用严格模式

ES6动态导入可以使用预获取(prefetch)、预加载(preload)加载技术。
  在浏览器中表现为:<link rel="preload" href="/main.js?t=3000" as="script">
  在js中表现问:import(/* webpackPreload */ './**.js').then(()=>{});
ES5--严格模式
消除js语法不严谨之处、消除代码运行不安全之处、提高编译器效率、为新版本js做铺垫
进入严格模式:use strict; 
将严格模式放在当前作用域顶部,声明之前不允许有任何代码; 
全局环境下,表示全局都采用严格模式;
let和const
都用来申明块级作用域(声明的变量在当前{}有效)的变量,不会申明提前; 
ES6中通过const定义常量,常量通常用大写字母定义,多个单词之间用_分隔; 
const声明的变量只允许一次赋值(基本数据类型)操作,引用情况下不能修改引用对象,但是可以修改对象属性; 
letconst在变量声明预编译阶段存在TDZ暂时性死区(var 不存在); 
letconst在声明的变量不会绑定到window中(wiindow.a);
箭头函数-消除原本函数的二义性
ES6新增函数语法糖(指令序列) - 简化函数写法;
  当形参只有一个时,可以省略();
  当内容只有一条语句且为return时,可以省略return和{},;
  当返回一个对象时,需要加 ( ) ; 
脱离了面向对象的范畴,
  this在箭头函数中,指向静态(固定、父级环境),指向被调用者当前定义时的环境;
  箭头函数没有原型( prototype 指向 undefind );
消除原本函数的二义性(函数可以直接执行,也可以创建实例)。
使用 class 创建类-消除原本函数的二义性
新旧方法
旧:	
  class Car {
 		 constructor() {
   	 	this.color = 'blue';
    	this.age = 2;
  		}
		}

ES2022新:	
  class Car {
  	color = 'blue';
  	age = 2;
   	#firstName = 'Joseph';        // 私有变量 使用in来判断某个对象是否拥有某个私有属性
   	hasColor() {
   	 return #firstName in this;   //console.log(car.hasColor()); // true
  	}
	}
解构赋值-结构相同
解构赋值-结构相同:

从数组和对象中提取值,对变量进行赋值;
本质是左右相等的一种匹配模式进行赋值;
它要求赋值符左右两边的结构相同;
解构将自动拆解,不停调用 Object.iterator().next 方法,获取可迭代对象的值,对应赋值;
展开运算符:...   结构时出现在变量身上为收缩,无法取值时获取空数组;
变量解构时,需要变量与属性名一致;
  let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
  baz // "aaa"

交换变量 :[a,b]=[b,a]    
解构赋值-结构不同:

只要一个数据结构身上,具有[Symbol.iterator]这样一个属性,且值是一个函数体,可以返回一个迭代器的话,我们就称这个数据结构是可迭代的;
人为的为对象打造一个迭代器出来,也就是让对象的隐式原型可以继承到迭代器属性:

Object.prototype[Symbol.iterator] = function(){
    // 使用 Object.values(this) 方法获取对象的所有值,并返回这些值的迭代器对象
    return Object.values(this)[Symbol.iterator]()
}

var [a, b] = {a: 1, b: 2}
console.log(a,b);  
空值合并运算符( ?? )
逻辑操作符,当左侧的操作数为 null或者undefined时,返回其右侧操作数,否则返回左侧操作数。
  const foo = undefined ?? "foo"   =>   foo
  const bar = null ?? "bar"        =>   bar
  const str = '' ?? "bar"          =>   ''
模板字符串(template string)
是一种字符串格式,用 `` 表示;可以作为普通字符串使用,也可以定义多行字符串;
1、可以保留字符串原有格式,类似于标签<pre></pre>;
2、允许字符串换行;
3、变量拼接更加便利:`abc${}`;

获取全局对象 globalThis
从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 window、self 取到全局对象,在 Node.js 中,它们都无法获取,必须使用 global。
在松散模式下,可以在函数中返回 this 来获取全局对象,但是在严格模式和模块环境下,this 会返回 undefined。

以前获取全局thisvar getGlobal = function () {
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
  };

  var globals = getGlobal();

  if (typeof globals.setTimeout !== 'function') {
    // 此环境中没有 setTimeout 方法!
  }

现在获取全局thisif (typeof globalThis.setTimeout !== 'function') {
    //  此环境中没有 setTimeout 方法!
  }

二、数据类型

set数据结构
ES6新增,类似于数组,有序结构,不同于数组的是,成员唯一(不可重复);
引用类型数据,有构造函数:let set = new set([]);
数组的去重:
    var arr2 = [... new Set(arr)];         //...为展开运算;Set为ES6新增数据结构,不能有相同元素;
转为数组:
    let arr = Array.from(set); 
方法:
set.protopyte.add():向数组中添加元素,出现在set结尾处,如果元素存在则忽略;返回值set对象;
set.protopyte.delete():用于删除set中的指定元素,返回值是布尔值,表示是否删除成功;
set.protopyte.size():返回set元元素数量;
set.protopyte.has():判断set中是否具有某个值,返回值是布尔值;
set.protopyte.clear():清空、删除所有元素;
map数据结构
类似于对象的哈希结构,提供了值-值得对应;
map的键可以是任意数据类型;
let map = new map();
设置对象的值:map.set(key,value);
获取对象的值:map.get(key);
判断成员是否存在:map.has(key);
删除对象的值:map.delete(key);
清空:map.clear();
基本数据类型-symbol
ES中第六种数据类型-解决对象属性命名重复问题,但是普通方法无法遍历(symbol方法可以遍历取出);
没有构造函数,需要symbol类型时用symbol函数创建;
let uname = symbol(username);
let o ={};
    o[uname] = 'zhangsan';
    console.log(o[uname]);
Iterator和for...of遍历
Iterator 的作用有三个:
  一是为各种数据结构,提供一个统一的、简便的访问接口;
  二是使得数据结构的成员能够按某种次序排列;
  三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
Iterator 的遍历过程是这样的:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
  for...of:能遍历的内容必须有Iterator接口;
  遍历数组:
  let set = new set([1,2,3])
  for (let value of set){
    console.log(value);
  }
使用for...of遍历map获得数组(第一个元素为key,第二个元素为value):
  解构赋值:for(let[key,value] of map){
     console.log(key,value);
  }
原生具备 Iterator 接口的数据结构如下:Array、Map、SetString、TypedArray、函数的 arguments 对象、NodeList 对象。
如何使得任何 JavaScript 对象都具有迭代能力
Object.prototype[Symbol.iterator] = function(){
    // 使用 Object.values(this) 方法获取对象的所有值,并返回这些值的迭代器对象
    return Object.values(this)[Symbol.iterator]()
}

三、异步方案

Gnerator
ES6新增遍历器对象;异步编程解决方案,让异步代码同步执行;一般情况配合co(co.js)模块使用;本质是一个自执行器;
定义一个Gnerator函数需要在函数名和关键字中添加 * ;
function * hello(){}
Generator 函数是一个普通函数,但是有两个特征:
一是,function关键字与函数名之间有一个星号;
二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
调用Generator函数时,表示创建一个遍历器对象,内部包含一个next函数,将函数拆分成多个部分执行,每次执行next函数将运行到下一个yield语句为止;
每次执行next会返回一个对象,返回值包含两个属性:yield表示产出内容,done表示简历是否结束;
promise
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 fulfilled(resolved)),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

promise.then()
  它的作用是为 Promise 实例添加状态改变时的回调函数;如果该对象状态变为resolved(resolved统一只指fulfilled状态),则会调用then()方法指定的回调函数。

promise.catch()
  Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数;
  如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。

promise.finally()
  finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的;使用场景:不管成功或者失败,都需要执行的代码(loading关闭)

Promise.all()
  Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  const p = Promise.all([p1, p2, p3])。
async/await函数
function isAsyncFunction(func) {
  return typeof func === 'function' && func.constructor.name === 'AsyncFunction';
}

function isAsyncFunction(func) {
  return Object.prototype.toString.call(func) === '[object AsyncFunction]';
}

function isAsyncFunction(func) {
  return func.toString().includes('async');
}
控制大规模请求实例

Promise.all()

const urls = ["url1", "url2", ... ,"url100"]; 
const maxConcurrentNum = 10; // 最大并发数 
// 数组分块,chunk表示每批次数量,返回数组二维数组 
function chunk(arr, chunk) { 
  let result = []; 
  for (let i = 0, len = arr.length; i < len; i += chunk) { 
    result.push(arr.slice(i, i + chunk)); 
   } 
   return result; 
 }

// 异步请求方法 
function fetchUrl(url) { 
  return new Promise((resolve, reject) => { 
    fetch(url) 
      .then(res => resolve(res)) 
      .catch(err => reject(err)); 
     }); 
   }

// 对url数组进行分块处理
const chunkedUrls = chunk(urls, maxConcurrentNum);

(async function () {
  try {
    for (let urls of chunkedUrls) {
      const promises = urls.map(url => fetchUrl(url));
      // 等待所有promises完成执行,并将结果存入results数组中
      const results = await Promise.all(promises);
      console.log('results:', results);
    }
  } catch (err) {
   console.error(err);
  }
})();

Promise.race()

const promiselist = [];
for (let i = 0; i < 100; i++) {
  const promise = fetch(`https://example.com/data${i}.json`);
  promiselist.push(promise);
}
Promise.race(promiselist)
  .then(response => {
    // handle the fastest response here
  })
  .catch(error => {
    console.error(error);
  });
手写promise
function myPromise(excutor) {
    // 1、执行结构
    let self = this;
    self.status = 'pending';
    self.value = null; // 成功结果
    self.reason = null;// 失败原因
    // 6、添加缓存数组
    self.onFulfilledCallbacks = [];
    self.onRejectedCallbacks = [];

    // 4、判断状态作相应处理
    function resolve(value) {
        if (self.status === 'pending') {
            self.value = value; // 保存成功结果
            self.status = 'fulfilled';
            // 8、状态改变依次取出callback
            self.onFulfilledCallbacks.forEach(item => item(value))
        }
    };
    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason; // 保存失败原因
            self.status = 'rejected';
            // 8、状态改变依次取出callback
            self.onRejectedCallbacks.forEach(item => item(reason))
        }
    };

    // 3、执行
    try {
        excutor(resolve, reject)
    } catch (error) {
        reject(error)
    };


}
// 2、then方法
myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    // 5、状态改变调用then方法
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) { resolve(data) };
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err };

    // 7、添加callback
    // if (self.status === 'pending') {
    // self.onFulfilledCallbacks.push(onFulfilled);
    // self.onRejectedCallbacks.push(onRejected);
    // }
    if (self.status === 'fulfilled') {
        return new myPromise((resolve, reject) => {
            try {
                let res = onFulfilled(self.value);
                // 判断传入的值是不是Promise ,是就.then 不是就抛出结果
                res instanceof myPromise ? res.then(resolve, reject) : resolve(x);
            }
            catch (err) {
                reject(err)
            }
        })
    };
    if (self.status === 'rejected') {
        return new myPromise((resolve, reject) => {
            try {
                let res = onRejected(self.value);
                // 判断传入的值是不是Promise ,是就.then 不是就抛出结果
                res instanceof myPromise ? res.then(resolve, reject) : resolve(x);
            }
            catch (err) {
                reject(err)
            }
        })
    };
    if (self.status === 'pending') {
        return new myPromise((resolve, reject) => {
            self.onFulfilledCallbacks.push(() => {
                let res = onRejected(self.value);
                // 判断传入的值是不是Promise ,是就.then 不是就抛出结果
                res instanceof myPromise ? res.then(resolve, reject) : resolve(x);
            });
            self.onRejectedCallbacks.push(() => {
                let res = onRejected(self.value);
                // 判断传入的值是不是Promise ,是就.then 不是就抛出结果
                res instanceof myPromise ? res.then(resolve, reject) : resolve(x);
            });
        })
    }
}
myPromise.prototype.catch = function (fn) {
    return this.then(null, fn)
}

四、对象的 Proxy的基本使用

作用:简单的来说,监听对一个对象的操作,用于拦截对象的基本方法。

对象的基本方法
get 捕捉器(常用)
 const obj = {
    name: "copyer",
    age: 12,
 };
 const objProxy = new Proxy(obj, {
     /**
      * @param {*} target :目标对象
      * @param {*} key : 键值
      * @param {*} receiver :代理对象(后面会专门讲解)
      */
     get: function (target, key, receiver) {
       console.log("get捕捉器");
       return target[key];
     },
 });
 console.log(objProxy.name); // copyer
set 捕捉器(常用)
  const obj = {
   name: "copyer",
   age: 12,
 };
 const objProxy = new Proxy(obj, {
   /**
    * @param {*} target : 目标对象
    * @param {*} key :键值
    * @param {*} newValue :新增
    * @param {*} receiver :代理对象
    */
   set: function (target, key, newValue, receiver) {
     console.log("set捕捉器");
     target[key] = newValue;
   },
 });
 objProxy.age = 23;
 console.log(obj.age); // 23
has 捕捉器(常用)
  const obj = {
   name: "copyer",
   age: 12,
 };
 const objProxy = new Proxy(obj, {
   has: function (target, key) {
     console.log("has捕捉器");
     return Object.keys(target).includes(key);
   },
 });
 console.log("name" in objProxy);
deleteProperty 捕捉器(常用)
  const obj = {
   name: "copyer",
   age: 12,
 };
 const objProxy = new Proxy(obj, {
   deleteProperty: function (target, key) {
     console.log("deleteProperty捕捉器");
     return delete target[key];
   },
 });
 console.log(delete objProxy.name); // true
defineProperty( [[DefineOwnProperty]] )
[[DefineOwnProperty]] 是一个内部方法,不在JavaScript代码中直接调用,而是作为 Object.defineProperty 的底层实现。
Object.definePropertyJavaScript的一个内置方法,用于在一个对象上定义一个新的属性或更改现有属性的特性,同时不会触发属性的存取器(getter/setter)。

let obj = {};
// 定义一个只读属性
Object.defineProperty(obj, 'readOnlyProp', {
    value: 'This is a read-only property.',
    writable: false, // 不可写
    enumerable: true, // 可枚举
    configurable: false // 不可配置
});