一幅长文细学JavaScript(五)——ES6-ES11新特性

5 ES版本

摘要

ES5的先天不足致使ES后续版本的发展,这也是前端人员绕不开的一个点。如果我们想要在工作和面试中轻松解决问题,那么了解ES6-ES11是必不可少的。

在本文中,我将采用一种更加通俗的方式来讲述这一块知识点,而不是照搬书上概念。

声明:在使用本文的代码时,为了避免文章冗长,我只附上了script标签内的代码供演示,如有需要详细代码可以前往代码仓库获取。学习ES6及以上新特性时,我希望你能对node.js和Vue有一定了解后再来学习,这样能帮助你减轻许多负担!

作者:来自ArimaMisaki创作

文章目录

  • 5 ES版本
    • 5.1 概述
        • 5.1.1 JavaScript版本
        • 5.1.2 ECMAScript版本
    • 5.2 ES6特性
      • 5.2.1 let关键字
      • 5.2.2 const 常量
      • 5.2.3 变量解构赋值
      • 5.2.4 模板字符串
      • 5.2.5 对象简化写法
      • 5.2.6 箭头函数
      • 5.2.7 形参初始化
      • 5.2.8 获取实参
      • 5.2.9 扩展运算符
      • 5.2.10 Symbol
        • 5.2.10.1 Symbol概述及创建
        • 5.2.10.2 Symbol的使用场景
        • 5.2.10.3 Symbol相关的操作
        • 5.2.10.4 内置的Symbol值
      • 5.2.11 Promise
        • 5.2.11.1 Promise概述
        • 5.2.11.2 then
        • 5.2.11.3 catch
        • 5.2.11.4 finally
        • 5.2.11.5 简化声明Promise对象
        • 5.2.11.6 Promise.all
        • 5.2.11.7 Promise的应用
      • 5.2.12 迭代器
      • 5.2.13 生成器
        • 5.2.13.1 生成器的基本使用
        • 5.2.13.2 生成器传参详解
        • 5.2.13.3 生成器的使用场景
      • 5.2.14 集合Set
      • 5.2.15 映射表Map
      • 5.2.16 类
        • 5.2.16.1 面向对象
        • 5.2.16.2 类
        • 5.2.16.3 继承
        • 5.2.16.4 方法重写
        • 5.2.16.5 注意事项
      • 5.2.17 数值扩展
      • 5.2.18 对象方法扩展
      • 5.2.19 模块化
      • 5.2.20 Proxy
    • 5.3 ES7特性
    • 5.4 ES8特性
      • 5.4.1 async函数
      • 5.4.2 await表达式
      • 5.4.3 async和await的应用
        • 5.4.3.1 应用一:结合node.js来读取文件信息
        • 5.4.3.2 应用二:发送Ajax请求
      • 5.4.4 对象方法扩展
    • 5.5 ES9特性
      • 5.5.1 扩展运算符
      • 5.5.2 命名捕获分组
      • 5.5.3 反向断言
    • 5.7 ES11
      • 5.7.1 私有属性
      • 5.7.2 Promise.allSettled
      • 5.7.3 String.prototype.matchAll
      • 5.7.4 可选链操作符
      • 5.7.5 动态Import
      • 5.7.6 BigInt
      • 5.7.7 绝对全局变量
    • 5.8 编程风范
      • 5.8.1 let取代var
      • 5.8.2 全局常量
      • 5.5.3 解构赋值
      • 5.8.4 字符串
      • 5.8.5 浅拷贝
      • 5.8.6 Map
      • 5.8.7 Class

5.1 概述

5.1.1 JavaScript版本

说明:JavaScript 由 Brendan Eich 于 1995 年发明,并于 1997 年成为 ECMA 标准。我们在最开始学习时提到的名词ECMAScript 是该语言的官方名称。


5.1.2 ECMAScript版本

说明:ECMAScript 通常缩写为 ES,所有浏览器都完全支持ES3,所有现代浏览器如谷歌、火狐、Edge、Safari、Opera都完全支持ES5。

版本官方名称描述
1ECMAScript 1 (1997)第一版
2ECMAScript 2 (1998)只改变编辑方式
3ECMAScript 3 (1999)添加了正则表达式;添加了 try/catch
4ECMAScript 4从未发布过
5ECMAScript 5 (2009)添加了“严格模式”。 添加了 JSON 支持。 添加了 String.trim()。 添加了 Array.isArray()。 添加了数组迭代方法
5.1ECMAScript 5.1 (2011)编辑改变
6ECMAScript 2015添加了 let 和 const; 添加了默认参数值;添加了 Array.find();添加了 Array.findIndex()
7ECMAScript 2016添加了指数运算符(**)。 添加了 Array.prototype.includes
8ECMAScript 2017添加了字符串填充;添加了新的 Object 属性;添加了异步功能;添加了共享内存。
9ECMAScript 2018添加了 rest / spread 属性;添加了异步迭代;添加了 Promise.finally() 增加 RegExp

5.2 ES6特性

5.2.1 let关键字

<script>let a;let b,c,d;let e = 100;let f = 521,g = 'iloveyou',h = [];//1 变量名不能重复声明// let star = "ArimaMisaki";// let star = "憨瓜";//2 var不受作用域限制,而let受作用域限制,详见2.9.1{let girl = "loving girl";}console.log(girl);//3 let变量没有声明前不得使用,语法靠近C、C++console.log(song);// var song = '爱你';let song = '爱你';//4 不影响作用域链,虽然let不在作用域中却能使用{let school = "CSDN学院";function fn() {console.log(school);}fn();}</script>

5.2.2 const 常量

说明:与let和var相反,const常用于声明一个常量。const沿袭了C++的关键字的确很舒服,但总感觉js因此变得像个缝合怪。

<script>//声明常量、必须有初始值、推荐大写const NAME = "ArimaMisaki";//常量不可二次赋值// NAME = "憨瓜";//块级作用域{const PARTY = "单身派对";}// console.log(PARTY);//const作用于数组等对象的修改,不算做对常量的修改const TESM = ['UZI','MLXG','Ming','Letme'];TESM.push('Meiko');</script>

5.2.3 变量解构赋值

说明:在以前,我们只能通过为变量一一赋值;现在,我们允许使用模式匹配,用于从数据结构中提取值。

解构赋值的含义:解析某一数据结构,将我们想要的东西提取出来,赋给变量或常量。

模式匹配:如果你想要从数组中提取值,则你用于提取的结构必须是个数组;如果你想要从对象中提取值,则你用于提取的结构必须是个对象。

<script>/*====== 1.数组的结构赋值 ======*/const arr1 = [1,2,3,4];// 如果你想要从数组中提取值,则你用于提取的结构必须是个数组const [a,b,c,d] = arr1;console.log(a); //1console.log(b); //2console.log(c); //3console.log(d); //4const arr2 = [1,[2,3,4],5];// 如果不想提取,可以用逗号跳过const [a1,[,,b1],c1] = arr2;console.log(a1); //1console.log(b1); //4console.log(c1); //5const arr3 = [];// 解构赋值可以给出默认值,默认值只有在数组成员严格等于(===)undefined时才会生效const [a2,b2,c2 = 1] = arr3;console.log(a2); //undefinedconsole.log(b2); //undefinedconsole.log(c2); //1function fn1(a,b){console.log(arguments);// 解构赋值可以是数组提取伪数组const [a4,b4] = arguments;console.log(a4);console.log(b4);}fn1(1,2)/*====== 2.对象的结构赋值 ======*/const person = {uname: 'ArimaMisaki',age: 13,say:function(){console.log("会说话");}}// 如果你想要从对象中提取值,则你用于提取的结构必须是个对象const {uname:name,age:age,say:sayfn} = person; //对应写法// const {uname,age,sayfn} = person; //老写法console.log(name,age);sayfn();</script>

5.2.4 模板字符串

说明模板字符串是一种字符串的新写法,他通常用作拼接字符串

<script>//字符串的新声明方式:模板字符串let str = `字符串`;console.log(str,typeof str);//模板字符串允许换行写法,原来的String不支持let str2 = `<ul><li>ArimaMisaki</li><li>错过</li><li>了你</li></ul>`console.log(str2);//变量拼接let lovest = 'ArimaMisaki';let out = `${lovest}好想你`;console.log(out);</script>

5.2.5 对象简化写法

<script>let name = "ArimaMisaki"let change = function(){console.log('我们可以改变你');}const school = {// 1.可以不用写键值对了name,change,// 2.可以不用写function了improve(){console.log("我们可以提高你的技能");}}console.log(school);</script>

5.2.6 箭头函数

<script>//ES6允许使用箭头(=>)来定义函数let addFn = (a,b) => {return a+b;}console.log(addFn(2,3));//箭头函数的this是静态的window.name = '华为';const company = {name:"小米"}function getName(){console.log(this.Name);}let getName2 = () => {console.log(this.Name);}getName.call(company);getName2.call(company);//箭头函数不能作为构造函数来实例化对象// let Person = (name,age) => {//     this.name = name;//     this.age = age;// }// let me = new Person('ArimaMisaki',30);// console.log(me);//3 箭头函数不能使用arguments变量// let fn = () => {//     console.log(arguments);// }// fn(1,2,3);//4 箭头函数的简写//(1) 省略小括号,当形参有且只有一个let add = n => {return n+n;}console.log(add(9));//(2) 省略花括号,当代码体只有一条语句时let pow = n =>  n*n;console.log(pow(8));</script>

5.2.7 形参初始化

<script>//可以给形参赋初始值let add = (a,b,c = 3) => {return a+b+c;}console.log(add(1,2));//可以和解构赋值结合使用function connect({host = "127.0.0.1",username,password,port}){console.log(host);console.log(username);console.log(password);console.log(port);}connect({host:'localhost',username:'root',password:'root',port:3306})</script>

5.2.8 获取实参

<script>//ES5 获取实参的方式// function date(){//     console.log(arguments);// }// date('1','2','3');//ES6 获取实参的方式// function date(...args){//     console.log(args);// }// date('1','2','3');//rest所使用的...args参数必须放参数列表的最后面function fn(a,b,...args){console.log(a);console.log(b);console.log(args);}fn(1,2,3,4,5,6);</script>

5.2.9 扩展运算符

说明:扩展运算符并非是指ES6扩展的运算符,而是指ES6的扩展运算符,它是…运算符的称呼,也叫扩张运算符。扩展运算符的用法十分广泛,通过下面的演示,我们可以更好的了解它的作用。

<script>//1 扩展的...运算符可以将数组转换为序列const number = ['1','2','3'];function print(){console.log(arguments);}print(number);//如果这样的话,arguments获取的是数组实参对象print(...number);//如果这样的话,arguments获取的是数组中每一个元素的序列//相当于print('1','2','3')//2 扩展运算符的应用const number1 = ['1','2','3'];const number2 = ['4','5','6'];//(1)用于数组的合并const mergeNumber = [...number1,...number2];console.log(mergeNumber);//(2)用于克隆数组const copyNumber = [...number1];console.log(copyNumber); //(3)将伪数组转为数组const divs = document.querySelectorAll("div");console.log(divs);const divArr = [...divs];console.log(divArr);</script>

5.2.10 Symbol

5.2.10.1 Symbol概述及创建

说明:继js六大数据类型后,Symbol成为第七个数据类型,其表示的值是一个独一无二的值,常用作对象的私有变量。

<script>//新数据类型let s = Symbol();// console.log(s,typeof s);//创建Symbol 方法一:let s2 = Symbol('ArimaMisaki');let s3 = Symbol('ArimaMisaki');console.log(s2 === s3);//不同,每个Symbol有唯一标识,括号内不过是值罢了//方法二:使用Symbol.for()let s4 = Symbol.for('SB');let s5 = Symbol.for('SB');//如果使用for方法创建,则Symbol创建前会在注册表中搜寻看有无重复值,如果有则直接使用不创建第二个//Symbol.for创建的Symbol具有全局登记特性console.log(s4 === s5);//Symbol不能和其他类型做运算let result = s + 100;console.log(result);// 应用:Symbol常用作对象的私有变量let s6 = Symbol('s6');console.log(s6);let obj = {};obj[s6] = 'ArimaMisaki'// 使用Symbol定义的对象中的变量,取值要用中括号</script>

5.2.10.2 Symbol的使用场景

说明:在一个对象中如果书写过多的属性,在不一一检查的情况下,我们极有可能将对象的属性的key写重复了。但是Symbol可以很好的避免这个问题,即使描述一样,每个Symbol也是独一无二的。

<script>// 应用场景1:已经有同事写出某个对象,但是对象属性过多let game = {gamename:'原神',play:()=>{console.log('玩游戏');}}let methods = {gamename:Symbol(),play:Symbol()}game[methods.gamename] = 'Genshin Impact'game[methods.play] = ()=>{console.log('玩原神');}// 应用场景2:自己写对象,但是属性名很多let school = {[Symbol("name")]:"ArimaMisaki",[Symbol("say")]:()=>{console.log("会说话");}}</script>

5.2.10.3 Symbol相关的操作

<script>// 1.Symbol.prototype.description 用于返回Symbol的描述const sym = Symbol('ArimaMisaki')console.log(sym.description);// 2.Object.defineProperty()可以将对象的属性名指定为Symbol值const mySymbol = Symbol();// 添加属性的三种写法// 写法一:// let a = {};// a[mySymbol] = 'Hello!';// // 写法二:// let a = {//     [mySymbol]:'Hello!'// };// // 写法三:// let a = {};// Object.defineProperties(a,mySymbol,{value:'Hello!'});// 3.Symbol.keyFor()可以返回一个已经登记过的Symbol的keylet s1 = Symbol("foo")console.log(Symbol.keyFor(s1));let s2 = Symbol("foo")console.log(Symbol.keyFor(s2));</script>

5.2.10.4 内置的Symbol值

说明:内置的Symbol值用于扩展对象的功能,我们这里挑两个重要的讲,其他的需要去查手册即可。

<script>// 1.对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法class Person{static [Symbol.hasInstance](){console.log("用来检测类型");}}let o = {};console.log(o instanceof Person);//用来检测类型 false// 2.对象的SymbolinConcatSpreadable可以控制该对象是否可以展开let arr1 = [1,2,3]let arr2 = [4,5,6]arr2[Symbol.isConcatSpreadable] = false;let arr = arr1.concat(arr2)console.log(arr); //(4) [1, 2, 3, Array(3)]</script>

5.2.11 Promise

5.2.11.1 Promise概述

引入:如果我们想要实现一种功能,点击一个东西后执行相应的动作,而后再点击再执行相应的动作,那么我们需要设计函数的嵌套,函数的嵌套如果太深,就会造成回调地域

说明:Promise 是异步编程的一种解决方案,比传统的解决方案(使用回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise对象可看做是一个容器,里面装有异步结果。Promise本意为承诺,他承诺异步结果的状态不会受到外界的影响。Promise对象还可以返回异步操作的结果。

Promise有3种状态,一开始是pending(未完成),执行resolve后就变成fulfilled(resolved,已成功)。执行reject后就变成rejected(失败)。

Promise对象可以传入两个参数resolve和reject来控制异步的状态,通过p.then()来控制成功或失败后执行什么样的函数。若构造器中同时使用了resolve和reject,则看哪个状态最先发生,Promise对象一旦确定了状态就不会发生改变。

<script>const p = new Promise((resolve,reject)=>{// resolve控制成功状态// 下列代码代表1秒后异步任务状态转为成功setTimeout(()=>{let data = '数据';resolve(data);},1000);// reject控制失败状态// 下列代码代表3秒后异步任务状态为失败setTimeout(()=>{let err = '数据读取失败';reject(err);},3000);});p.then((value)=>{//成功调这个函数console.log(value);},(reason)=>{//失败调这个函数console.log(reason);})</script>

5.2.11.2 then

说明:调用promise对象提供的then方法可以传入成功态和失败态的promise对象接下来要做的事,并且返回一个默认成功态promise对象。

<script>const p = new Promise((resolve,reject)=>{let data = '数据';resolve(data);});// 调用p.then()返回是一个promise对象,且默认返回的是一个成功状态的promise对象// 如果调用return来返回一个数据对象,则该对象会被自动包装为promise对象,并让resolve返回// 如果想要返回一个失败状态的promise对象,自己return一个指向reject的promise对象即可console.log(p.then());p.then((value)=>{//成功调这个函数console.log(value);},(reason)=>{//失败调这个函数console.log(reason);}).then((value)=>{console.log(value);},(reason)=>{console.log(reson);})</script>

5.2.11.3 catch

引入:在实际应用中,调用then执行失败的结果很少见,故在then的两个参数中,我们一般只传入成功后promise对象执行的对应函数。

说明promise.catch方法用于执行失败态promise执行的对应函数,其本质是promise.then(null,err=>{})

<script>const p = new Promise((resolve, reject) => {// resolve('调用成功');reject('调用失败');});p.then((successMessage) => {console.log(successMessage);})p.catch((err) => {console.log(err);})</script>

5.2.11.4 finally

说明:promise.finally()用于执行最终操作。不管是成功态也好失败态也好,finally中的参数所代表的函数都会执行。但finally很少用,我们不必深究。


5.2.11.5 简化声明Promise对象

说明:Promise构造函数提供了Promise.resolve()Promise.reject()来代表成功态和失败态的promise对象。

<script>// 传统写法const p = new Promise((resolve, reject) => {resolve('调用成功');// reject('调用失败');});p.then((successMessage)=>{console.log(successMessage);})// Promise.resolve():是成功态Promise的简写形式const p2 = Promise.resolve('调用成功');p2.then((successMessage)=>{console.log(successMessage);})// Promise.reject():是失败态的Promise的简写形式const p3 = Promise.reject('调用失败');p3.catch((err)=>{console.log(err);})</script>

5.2.11.6 Promise.all

说明:Permise.all()可以传入一个Promise对象数组作为参数,如果数组中的promise对象都是成功态,那么promise.all()可以返回一个成功态的promise对象;若数组中有一个promise对象是失败态,那么最终promise.all()返回一个失败态的promise对象。

<script>const p1 = Promise.resolve('调用成功');const p2 = Promise.reject('调用失败');const p = Promise.all([p1,p2]);p.then((success)=>{console.log(success);},(err)=>{console.log(err);})</script>

5.2.11.7 Promise的应用

说明:如果一口气将所有的资源加载到网页上, 那么在网页结构含有较多图片的情况下,加载速度会不尽人意,用户体验差。为此,我们可以加载少部分资源,其他资源异步加载。

    <style>.image{width: 300px;}</style><body><div><img src="" alt="加载失败" class="image"></div><script>const loadImgAsymc = url =>{return new Promise((resolve,reject)=>{const img = new Image();img.onload=()=>{resolve(img);};img.onerror = ()=>{reject(new Error(`Could not load Image at ${url}`))}img.src = url})}let image = document.querySelector('.image')loadImgAsymc('./img_112.jpg').then(img=>{setTimeout(()=>{image.src = img.src},2000)})</script>
</body>

5.2.12 迭代器

说明:迭代器是一种接口,为Js的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。ES6创建了一种新的遍历命令for…of循环,具备迭代器接口的数据结构均可使用该命令。

具备迭代器的数据结构

  • array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • NodeList

迭代器原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一个调用对象的next方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用next方法就返回包含value和done属性的对象

须知:ES6规定默认的Iterator接口部署在数据结构的Symbol.iterator属性中。换而言之,只要数据结构中存在该属性,那就可以使用for…of方法对其进行遍历。

<script>const xiyou = ['唐僧','孙悟空','猪八戒','沙僧']// 使用for...of遍历数组for(let v of xiyou){console.log(v);}</script>

5.2.13 生成器

5.2.13.1 生成器的基本使用

说明:Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

我试图讲的更仔细点:执行生成器函数可以返回一个迭代器对象。在生成器函数中可以定义多条yield语句,当我们调用next()时,迭代器会走完第一条yield语句,此时第一个状态结束,迭代器停止。

<script>function* gen(){console.log('第一个状态');yield 111;console.log('第二个状态');yield 222;console.log('第三个状态');yield 333;}let iterator = gen();console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());</script>

image-20220817111858530


5.2.13.2 生成器传参详解

说明:生成器的传参有点反人类。让我们解释一下:如果不经过讲解,你肯定以为下面的代码中,let x = yield '2’的意思是将yield '2’赋给x,可实际并非如此。

yield语句用于停止迭代器的运行,next()用于启动迭代器的运行。从首次next开始,先输出start,而后来到yield,此时迭代器停止。在第二次启动next时,我们将参数20传入迭代器,此时该参数赋给了x并且启动迭代器,故下面打印’one:20’。同理可得继续下去的结果。

<script>function* add(){console.log('start');let x = yield '2';console.log('one:'+x);let y = yield '3';console.log('two'+y);}const f1 = add();console.log(f1.next());console.log(f1.next(20));console.log(f1.next(30));</script>

5.2.13.3 生成器的使用场景

说明:有些数据结构没有迭代器,通过生成器我们可以给这些数据结构提供遍历功能。


5.2.14 集合Set

<script>// 1.声明setlet s = new Set();let s2 = new Set(['a','a','b','c'])//满足高中数学集合的三大特性// 2.常用方法console.log(s2.size());//查看元素个数console.log(s2.add('e'))//新增元素console.log(s2.delete('e'));//删除元素console.log(s2.has('a'));//判断存在性s2.clear()//清空数据</script>

5.2.15 映射表Map

<script>// 1.声明Maplet m = new Map()// 2.添加元素m.set('name','ArimaMisaki')m.set('say',()=>{console.log('说话');})// 3.删除元素m.delete('name')// 4.get依键寻值console.log(m.get('say'));</script>

5.2.16 类

5.2.16.1 面向对象

说明:在ES6之前JS并没有类,创建对象可以使用构造器来创建,但是在ES6后,出现了类。


5.2.16.2 类

说明:类封装一类事物共有的特征,通过类可以实例化对象。如封装车类,实例化宝马这种品牌的车。

<script>// 1.创建一个Star类class Star {// 类中的构造函数constructor(uname) { this.uname = uname//对象私有方法this.say = ()=>{console.log(this.uname+'会说话');}}// 对象共有方法sing(){console.log('明星会唱歌');}}// 2.实例化对象let ldh =  new Star('刘德华')console.log(ldh.uname);ldh.say()ldh.sing()</script>

5.2.16.3 继承

extend说明:在ES6之前需要使用复杂的方式来实现继承,在ES6中使用extend即可继承类。

super说明:super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。

<script>class Father {constructor(x, y) {this.x = xthis.y = y}sum() {console.log(this.x + this.y);}}class Son extends Father {constructor(x, y) {// this.x = x// this.y = ysuper(x, y) //调用父类构造函数}}let son = new Son(1, 2)//需使用super才可执行son.sum()</script>

5.2.16.4 方法重写

说明:子类可以重写父类的方法。重写完成后,子类调用该方法按照就近原则寻找。如果想要使用父类的方法,可以考虑使用super。

<script>class Father{say(){console.log('我是爸爸');}}class Son extends Father{say(){console.log('我是儿子');}FatherSay(){//调用父类的函数super.say()}}let son = new Son()son.say()son.FatherSay()</script>

5.2.16.5 注意事项

提示:在定义子类的私有方法时,需要将super置于this之前。

<script>class Father {constructor(x, y) {this.x = xthis.y = y}sum() {console.log(this.x + this.y);}}class Son extends Father {constructor(x, y) {super(x, y)this.x = ythis.y = y}substract() {console.log(this.x - this.y);}}let son = new Son(4, 3)son.sum()son.substract()</script>

5.2.17 数值扩展

<script>// 1.Number.EPSILON用于解决浮点数精度问题,其表示JS表示的最小精度console.log(0.1 + 0.2 === 0.3);//false,具有机器误差function equal(a, b) {if(Math.abs(a-b) < Number.EPSILON){return true}else{return false}}console.log(equal(0.1 + 0.2, 0.3));console.log('==============');// 2.进制的表示let b = 0b1010 //二进制let o = 0o777 //八进制let d = 100 //十进制let x = 0xff //十六进制console.log(b);// 10console.log('==============');// 3.Number.isFinite 检测一个数是否是有穷数console.log(Number.isFinite(100));//falseconsole.log(Number.isFinite(100/0));//trueconsole.log(Number.isFinite(Infinity));console.log('==============');// 4.Number.isNaN 检测一个数值是否为NaNconsole.log(Number.isNaN(123));//falseconsole.log('==============');// 5.Number.parseInt 转型console.log(Number.parseInt(3.06));//3console.log('==============');// 6.Number.isInteger 判断是否为整数console.log(Number.isInteger(3.5));//falseconsole.log('==============');// 7.Math.trunc 将数字的小数部分抹掉console.log(Math.trunc(3.666));//3console.log('==============');// 8.Math.sign 判断一个数是正数负数还是0console.log(Math.sign(100));console.log(Math.sign(0));console.log(Math.sign(-100));</script>

5.2.18 对象方法扩展

<script>// 1.Object.is 判断两个值是否完全相等console.log(Object.is(120,120));// 和全等号的区别console.log(Object.is(NaN,NaN));console.log(NaN === NaN);// 2.Object.assign(被覆盖对象,覆改对象) 对象合并const person1 = {name:'zs',age:12}const person2 = {name:'ls',age:13}console.log(Object.assign(person1,person2));// 属性必须两者都有才可覆改// 3.Object.setPrototypeOf设置原型const school = {name:'hstc'}const scities = {xiaoqu:['潮州']}Object.setPrototypeOf(school,cities)console.log(school);</script>

5.2.19 模块化

说明:模块化是指将一个大的程序文件拆分成多个小的文件,然后将小文件组合起来。

提示:可以将import的代码单独写在一个文件,而后使用src导入。

<script type = "module">//1.import 内容 [as 别名] from 文件位置import * as m1 from "./Demo5_2_19.js"console.log(m1);console.log(m1.school);console.log(m1.teach);// 2.解构赋值 import {内容 as 别名} from 文件位置import {school,teach} from "./Demo5_2_19.js"console.log(school);</script>
/* 1.分别暴露 */
export let school = 'hstc'export var teach = () =>{console.log("开发技能");
}/* 2.统一暴露 */
let school = 'hstc'
let teach = ()=>{console.log("开发技能");
}
export{school,findJob}/* 3.默认暴露 */
export default{school:'hstc',teach:()=>{console.log("开发技能");}
}

5.2.20 Proxy

引入:在ES5中,如果我们创建了一个对象,并且访问其属性,那么即使一个属性在对象中不存在也能访问,只是值为undefined。

说明:让我们看阮一峰的ES6入门对Proxy的解释——Proxy 可以理解成,在目标对象之前架设一层拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

一言蔽之:Proxy就是一个拦截器,他拥有类似于Java里面的get,set方法,但get和set方法主要作用并不是提供访问和修改数据的方法,而是拦截外界访问和修改数据。

格式:let proxy = new Proxy(拦截对象,拦截行为对象)

拦截行为

  • get(target,propKey):拦截对象属性的读取
  • set(target,propKey):拦截对象属性的设置

提示:如果不设置拦截行为对象,那么proxy代理器相当于没有设置,proxy依然和拦截对象没有区别。

<script>let person = {name:'ArimaMisaki',age:19}// 1.get用于拦截对目标对象属性的访问,其中target为目标对象,key为访问属性// 下面的代码意为:创建了person的代理对象,该对象以person为目标,当外界调用.属性时,key为该属性let proxy = new Proxy(person,{get:(target,key)=>{// 如果不写return,那么相当于没有设置代理,默认返回undefinedreturn 35}})console.log(proxy.name);// 2.我们可以拦截属性的访问let proxy1 = new Proxy(person,{get:(target,key)=>{if(key === 'name'){return "禁止访问该属性"}}})console.log(proxy1.name);// 3.set同理,用于拦截对目标对象属性的修改let proxy2 = new Proxy(person,{set:(target,key)=>{if(key === 'name'){return "禁止修改该属性"}}})proxy2.name = "傻蛋"console.log(proxy2.name);</script>

5.3 ES7特性

<script>// 1.indexof 判断是否存在const mingzhu = ['西游记','红楼梦','三国演义','水浒传']console.log(mingzhu.includes('西游记'));console.log(mingzhu.includes('金瓶梅'));// 2.** 幂运算console.log(2 ** 2); //相当于Math.pow(2,2)</script>

5.4 ES8特性

5.4.1 async函数

说明:如果在函数中添加async修饰字,那么此时会有几种情况:

  • 默认该函数返回成功态的promise对象,即使你不写return
  • 如果指定返回一个失败态的promise对象,则async失去了意义,因为async就是为了返回一个成功态promise对象而生
  • 如果指定抛出一个异常,则返回的是一个失败态的promise对象

也就是说,async函数的返回值为promise对象,promise对象的状态依return结果而定,但默认是成功态。


5.4.2 await表达式

说明:await修饰字常用于表达式中,且后跟一个promise对象,表示接收其成功态promise对象的结果,相当于then方法中resolve的结果。

<script>// 1.then写法const p = new Promise((resolve,reject)=>{resolve("生成一个成功态对象")})p.then((msg)=>{console.log(msg);})// 2.await写法const p2 = new Promise((resolve,reject)=>{resolve("生成一个成功态对象")})async function fn(){let result = await p2console.log(result);}fn()</script>

5.4.3 async和await的应用

5.4.3.1 应用一:结合node.js来读取文件信息

const fs = require('fs');const readtext = () => {return new Promise((resolve, reject) => {fs.readFile('./Demo5_4_3_1.txt','utf-8', (err, dataStr) => {if (err) reject(err)else resolve(dataStr);})})
}async function main(){let text = await readtext();console.log(text);
}
main();

5.4.3.2 应用二:发送Ajax请求

<script>let sendAjax = (url) => {return new Promise((resolve, reject) => {const x = new XMLHttpRequest();x.open('GET', url);x.send();x.onreadystatechange = () => {if (x.readyState === 4 && x.status === 400) {resolve(x.response);} else {reject(x.status);}}})}async function main(){await sendAjax('https://api.apiopen.top/getJoke');console.log(result);}main()</script>

5.4.4 对象方法扩展

<script>const person = {name:"ArimaMisaki",age:12}// 1.获取对象所有的键console.log(Object.keys(person));// 2.获取对象所有的值console.log(Object.values(person));// 3.获取对象中的每条键值对console.log(Object.entries(person));const map = new Map(Object.entries(person));console.log(map);//可用于创建map// 4.获取对象属性的描述性对象console.log(Object.getOwnPropertyDescriptors(person));</script>

5.5 ES9特性

5.5.1 扩展运算符

说明:在ES6中我们曾经学过…运算符,它有诸多用途,如合并数组、复制数组、伪数组转换为数组等作用。在ES9中,…运算符再次被赋予了强大的特性,它可以将一个对象的多个属性塞到一个变量中。

<script>const person = {name:"ArimaMisaki",age:13,money:100}const person1 = {...person};console.log(person1);</script>

5.5.2 命名捕获分组

说明:命名捕获分组是正则表达式在ES9的一个扩展功能。当我们使用一条正则表达式提取字符串内容时,我们可能想要使用一条正则提取多个字符串部分,命名捕获分组允许我们对这些部分进行命名,格式为?<groupname>

<script>/* 老式写法 */let str = '<a href="https://www.baidu.com">百度</a>'// 1.提取url和标签文本const reg = /<a href="(.*)">(.*)<\/a>/// 2.执行const result = reg.exec(str);console.log(result[1]);console.log(result[2]);/* 分组写法 */ // 1.提取url和标签文本并分组const reg2 = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;// 2.执行const result2 = reg2.exec(str)console.log(result2.groups.url);console.log(result2.groups.text);</script>

5.5.3 反向断言

说明:如果我们想要通过正则提取一个片段,若根据片段的后半部分来提取则为正向断言,根据片段的前半部分来提取则为反向断言

<script>// 声明一个字符串let str = '5201314我爱你999男神';// 1.正向断言:根据字符串的后面部分来确定字符串片段const reg = /\d+(?=男)/;const result = reg.exec(str);console.log(result);// 2.反向断言:根据字符串的前面部分来确定字符串片段const reg1 = /(?<=么)\d+/;const result1 = reg.exec(str);console.log(result);</script>

5.7 ES11

5.7.1 私有属性

说明:ES11允许使用井号#来定义私有属性,私有属性无法被类外访问。

<script>class Person {name;#age;#weightconstructor(name,age,weight){this.name = name;this.#age = age;this.#weight = weight}getter(){console.log(this.name);console.log(this.#age);console.log(this.#weight);}}const girl = new Person('晓红',18,'45kg');console.log(girl.name); console.log(girl.#age); console.log(girl.#weight); girl.getter()</script>

5.7.2 Promise.allSettled

说明:ES11中的Promise.allSetted()和ES6的Promise.all()方法类似,用于处理批量异步任务。但不同的是,前者接收promise数组,但返回值永远都是成功态的promise对象;后者也接收promise数组,但返回值需要依靠promise数组的状态而定。

<script>//声明两个promise对象const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve("商品数据 - 1");}, 1000);});const p2 = new Promise((resolve, reject) => {setTimeout(() => {// resolve("商品数据 - 2");reject("出错啦!");}, 1000);});//调用 allsettled 方法const result1 = Promise.allSettled([p1, p2]);console.log(result1);</script>

5.7.3 String.prototype.matchAll

说明:matchAll方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器,如果我们想要看到所有匹配的结果,可以使用for...of

<script>let str =`<ul><li><a>肖生克的救赎</a><p>上映日期: 1994-09-10</p></li><li><a>阿甘正传</a><p>上映日期: 1994-07-06</p></li>
</ul>`;//声明正则const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;//调用方法const result = str.matchAll(reg);for (let v of result) {console.log(v);}</script>

5.7.4 可选链操作符

说明:对函数进行传参时,我们的参数很有可能是对象或者数组,当我们想要在函数内部使用对象属性或数组元素时,我们总希望判断它们是否存在。判断的方式通常是使用&&确定多重条件。这样虽然能够解决问题,但总被人家嫌弃使用&&层层判断写出来的语句可能会过长。

为此,在ES11中采取了可选链操作符?.

<script>let main = (config)=>{// 我们需要判断参数传进来了没// 1.传统写法// const dbHost = config && config.db && config.db.username// 2.可选链操作符写法const dbHost = config?.db?.username}main({db:{host:'192.168.1.1',username:'root'}})</script>

5.7.5 动态Import

说明:在ES6中,我们使用import可以导入包,但这么做会导致网页加载时耗费大量的时间在导包上,我们需要的是:当我们使用这个包时,我们再动态地导入。

ES11提供了动态导包机制,使用import(包路径)可以返回一个promise对象,通过then我们可以导入对应包中module暴露的部分。

const btn = document.getElementById('btn')
btn.onclick = ()=>{// 动态导包import('./hello.js').then(module =>{module.hello();})
}

5.7.6 BigInt

说明:继ES5的number,boolean,undefined,null,string,object之后,ES6出现了Symbol,在ES11中又引入了BigInt作为第八个数据类型。BigInt用于一些较大数值的操作。

格式:let a = 12n

<script>// 1.bigint的声明let a = 12nconsole.log(a,typeof a);// 2.bigint转换let b = 123// 不可强转浮点数console.log(BigInt(b),typeof BigInt(b));</script>

5.7.7 绝对全局变量

说明:我们在构造函数中常常使用this代指使用该构造函数生成的对象。在ES11中,引入了globalThis来代指window对象。

<script>console.log(globalThis);</script>

5.8 编程风范

5.8.1 let取代var

记得我们曾经说过,使用var来声明变量会有以下几个问题:

  1. 不赋初值也可以使用,值默认为undefined
  2. 存在变量提升

使用let可以避免以上的问题,且无副作用。


5.8.2 全局常量

在一个数据为常量时,我们应该尽可能使用const而不是let,因为const底层存在优化机制,且可以很好的告诉代码的阅读者,该数据为常量不可修改。


5.5.3 解构赋值

在对数组成员或者是对象成员进行赋值时,应该尽量使用解构赋值。


5.8.4 字符串

如果想要动态地修改字符串,我们应该使用模板字符串,反之我们应该使用引号字符串来提高安全性。


5.8.5 浅拷贝

如果想要使用浅拷贝来拷贝一个数组,应该优先使用扩展运算符进行拷贝。


5.8.6 Map

只有在模拟现实对象时我们才会使用Object,但如果只是需要key:value的数据结构来存放数据,优先使用Map,因为Map里面存在迭代器方便遍历。


5.8.7 Class

在构造函数生成对象和Class生成对象之间,我们总是选择Class,因为其不具有prototype等繁琐的知识点。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398435.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一幅长文细学Vue(一)——Webpack打包工具

1 项目开发工具 摘要 ​ 在本文中&#xff0c;我们会详细讨论webpack是如何打包发布项目&#xff0c;不过对于Vue来说&#xff0c;Vite可以做到和webpack一样的功能。 声明&#xff1a;如果想要看懂此文章&#xff0c;需具备node.js中npm的知识。 作者&#xff1a;来自ArimaMis…

I00005 打印直角三角形字符图案

曾经的计算机&#xff0c;没有显示屏&#xff0c;人们操作计算机时&#xff0c;用打印机记录执行的操作命令。 后来有了显示屏&#xff0c;不过最初的显示屏是字符频幕&#xff0c;输出只能是字符。 即使是今日&#xff0c;计算机已经进入多窗口图形界面时代&#xff0c;有时程…

动态数组使用

1 #include<stdio.h>2 #include<stdlib.h>3 4 int main()5 {6 int i;7 int n; //用于记录输入的整数的个数 8 int *p; //用于指向动态数组的存储空间 9 int sum0,average; //用于记录输入的整数的和与平均值 10 11 scanf("%d"…

Linux下安装Redis及搭建主从

Linux下安装Redis 首先在官网下载对应版本的redis包&#xff0c;这里本人使用的是redis-4.0.8.tar.gz。然后在服务器中存放redis包的路径下执行tar –vxf redis-4.0.8.tar.gz&#xff08;这里对应下载的包&#xff09;&#xff0c;解压redis后,cd 进入 redis-4.0.8&#xff08;…

图解安装CentOS 6.6

以下是在虚拟机上安装CentOS 6.6的过程。一、安装文件:CentOS-6.6-x86_64-bin-DVD.iso二、安装步骤# 虚拟机的配置这里省略&#xff0c;在百度上有很多帖子可以参考。开启虚拟机进入安装界面&#xff0c;如下图所示选择第一个选项&#xff0c;按Enter安装程序加载完后&#xff…

一幅长文细学Vue(三)——组件基础(上)

3 组件基础&#xff08;上&#xff09; 摘要 ​ 在本文中&#xff0c;我们将学习什么是SPA&#xff0c;以及Vue中组件的基本使用。 声明&#xff1a;为了文章的清爽性&#xff0c;在文章内部的代码演示中只会附上部分演示代码&#xff0c;main.js文件的代码通常不贴出&#xff…

综合练习:词频统计

下载一首英文的歌词或文章 将所有,.&#xff1f;&#xff01;’:等分隔符全部替换为空格 将所有大写转换为小写 生成单词列表 fopen(news.txt,r) newsf.read() f.close() sep,.!"?: for c in sep:newsnews.replace(c, )wordListnews.lower().split()for w in wordList:pr…

linux中fcntl()、lockf、flock的区别

fcntl()、lockf、flock的区别 ——lvyilong316 这三个函数的作用都是给文件加锁&#xff0c;那它们有什么区别呢&#xff1f;首先flock和fcntl是系统调用&#xff0c;而lockf是库函数。lockf实际上是fcntl的封装&#xff0c;所以lockf和fcntl的底层实现是一样的&#xff0c;对文…

iOS开发拓展篇—音频处理(音乐播放器6)

一、图片处理 说明&#xff1a; Aspect表示按照原来的宽高比进行缩放。 Aspectfit表示按照原来的宽高比缩放&#xff0c;要求看到全部图片&#xff0c;后果是不能完全覆盖窗口&#xff0c;会留有空白。 Aspectfill表示按照原来的宽高比缩放&#xff0c;但只能看到部分图片。引发…

词频统计预处理之综合练习

下载一首英文的歌词或文章 news , 生成词频统计 sep,.;:"" for c in sep:newsnews.replace(c, )wordlistnews.lower().split()wordDict{} for w in wordlist:wordDict[w]wordDict.get(w,0)1wordSetset(wordlist) for w in wordSet:wordDict[w]wordlist.count(w)f…

一幅长文细学Vue(五)——组件高级(上)

5 组件高级&#xff08;上&#xff09; 摘要 ​ 在本文中&#xff0c;我们会详细讨论watch侦听器的基本使用&#xff0c;并且了解vue中常用的生命周期函数、实现组件之间的数据共享&#xff0c;最后学习如何在vue3.x项目中全局配置axios。 声明&#xff1a;为了文章的清爽性&am…

流行的编程语言及其趋势

转&#xff1a;StackOverflow上的编程趋势 http://www.csdn.net/article/2013-07-08/2816144-StackOverflow-Programming-Trends 摘要&#xff1a;相信每个程序员都知道StackOverflow问答网站&#xff0c;本文作者dodgy_coder通过StackExchange Data Explorer Query收集了该网站…

CentOS7.0 安装 tomcat-9.0

2019独角兽企业重金招聘Python工程师标准>>> 1、解压 # tar -zxvf apache-tomcat-9.0.0.M4.tar.gz -C /opt/usr/local 改个名字好以后操作&#xff1a; # mv apache-tomcat-9.0.0.M4.tar.gz tomcat 2、启动&停止 # /opt/usr/local/tomcat/bin/start…

一幅长文细学华为MRS大数据开发(四)——HBase

4 HBase 摘要&#xff1a;HBase是一种非关系型数据库&#xff0c;它是基于谷歌BigTable的开源实现&#xff0c;和BigTable一样&#xff0c;支持大规模海量数据的存储&#xff0c;对于分布式并发数据处理的效率极高&#xff0c;易于扩展且支持动态伸缩&#xff0c;适用于廉价设备…

AOP技术基础

1、引言2、AOP技术基础 3、Java平台AOP技术研究4、.Net平台AOP技术研究2.1 AOP技术起源 AOP技术的诞生并不算晚&#xff0c;早在1990年开始&#xff0c;来自Xerox Palo Alto Research Lab&#xff08;即PARC&#xff09;的研究人员就对面向对象思想的局限性进行了分析。他们研…

TCP 协议的三次握手、四次分手

详细描述了 TCP 协议的连接和关闭的整个过程。解释了为什么 TCP 协议是面向连接的、可靠的数据传输协议。 TCP 在互联网上之间的通信交流&#xff0c;一般是基于 TCP (Transmission Control Protocol&#xff0c;传输控制协议) 或者 UDP (User Datagram Protocol&#xff0c;用…

[Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包)

4182: Shopping Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 374 Solved: 130[Submit][Status][Discuss]Description 马上就是小苗的生日了&#xff0c;为了给小苗准备礼物&#xff0c;小葱兴冲冲地来到了商店街。商店街有n个商店&#xff0c;并且它们之间的道路构成了一…

Tiny框架2.0版火热推出

方法论 方法论决定了可以达到的高度 方法论&#xff0c;就是人们认识世界、改造世界的根本方法。 它是人们用什么样的方式、方法来观察事物和处理问题。概括地说&#xff0c;世界观主要解决世界“是什么”的问题&#xff0c;方法论主要解决“怎么办”的问题。 方法论是一种以解…

Java--Socket通信

下面内容是Java开发内容的高级知识点&#xff0c;需要对Java中的面向对象、IO、多线程、以及网络相关知识有一定的基础。(知识永远都有深度&#xff0c;本章节长期更新内容) 1、网络基础知识 网络通信的条件&#xff1a;1、两个通信的端都要有各自的IP地址作为唯一标识&#xf…

一幅长文细学华为MRS大数据开发(五)——MapReduce和Yarn

5 MapReduce和Yarn 摘要&#xff1a;本文中主要讲述大数据领域中最著名的批处理和离线处理计算框架——MapReduce&#xff0c;包括MapReduce的原理、流程、使用场景&#xff0c;以及Hadoop集群中负责统一的资源管理和调度的组件——Yarn。 作者&#xff1a;来自ArimaMisaki创作…