在以前咱们比较一个数组中是否包含某个元素的经常使用作法是:javascript
if (arr.indexOf(el) !== -1) { ... }
或者java
if (~arr.indexOf(el)) { ... } //此处~ 为取反,即-1取反结果为0,0取反为-1,1取反会-2,2取反为-3
indexOf 结果返回是个数字,咱们必需要和其余数字进行比较,才能见到获得是否"存在",即-1 表示不存在,大于-1 表示存在。node
因为 indexOf 在查找元素的比对过程当中,使用的是严格相等"===",好比webpack
[1,2,3].indexOf('1') //就会找不到。由于1和'1'并不相等。
一样在当数组中含有 NaN 是,也找不到,即git
[NaN].indexOf(NaN) //找不到,由于NaN===NaN为false。
因而可知 indexOf 并不能完美的找出是否包含某个元素值。github
因此咱们便有了下面的方式web
arr.includes(el);
includes 结果直接返回 boolean,true 表示存在,false 表示不存在,很是直观。includes 是经过,类型和值是否“一致”,而不是“严格相等”正则表达式
[1,2,3].includes('1') //false [1,2,3].includes(1) //true [1,2,NaN].includes(NaN) //true
因此再之后的使用中,
indexOf 应倾向于寻找 元素的位置,可能后续还要经过这个位置进行其余计算的场景,而 includes 应倾向于寻找 元素是否存在,只须要知道存在或者不存在,其余没必要知道chrome
既然数组有 indexOf,字符串也有 indexOf,那么一样
数组有 includes,字符串也有 includesjson
"1234567".includes("12") //true
在之前,js 要求取 m 的 n 次方,得这么写
Math.pow(m,n)
好比 2 的 3 次方
Math.pow(2,3) //8
而如今只须要 指数操做符"**",即两个星号便可
2 ** 3 //表示2的3次方 3 ** 2 //表示3的2次方
因为- + _ /,都有 -=,+=, _=, /=
一样 也有 "="
let a = 2; a **= 2; // a = a ** 2
将 object 的 key 或者 value 转成一个能够遍历的数组。
Object.values({id:1,name:2}); // [1,2] Object.keys({id:1,name:2}); // ["id", "name"] Object.entries({id:1,name:2}); // [["id", 1], ["name", 2]]
//padStart "abc".padStart(10); // " abc" 在原字符串"abc"的头部填充空白,以使整个字符串的长度变为10 "abc".padStart(10, "foo"); // "foofoofabc" 在原字符串"abc"的头部填充"foo",以使整个字符串的长度变为10 "abc".padStart(6, "123465"); // "123abc" 在原字符串"abc"的头部填充"123465",以使整个字符串的长度变为10,若是中途够了,就不进行填充了 "abc".padStart(8, "0"); // "00000abc" "abc".padStart(1); // "abc"
//padEnd "abc".padEnd(10); // "abc " 同padStart,只不过填充到末尾 "abc".padEnd(10, "foo"); // "abcfoofoof" 同padStart,只不过填充到末尾 "abc".padEnd(6, "123456"); // "abc123" 同padStart,只不过填充到末尾 "abc".padEnd(1); // "abc" 同padStart,只不过填充到末尾
//trimStart " abc ".trimStart(); // "abc " " abc ".trimEnd(); // " abc" " abc ".trim(); // "abc"
获取对象的全部自身属性的描述符。包括 configurable(可配置),enumerable(可枚举),writable(可写)
Object.getOwnPropertyDescriptors({ id: 1, name: 2 }); /* { "id": { "value": 1, "writable": true, "enumerable": true, "configurable": true }, "name": { "value": 2, "writable": true, "enumerable": true, "configurable": true } } */
在以前的函数是这样的
function clownPuppiesEverywhere(param1, param2) {} //或者这样 function clownPuppiesEverywhere(param1, param2) {}
param2 后面若是加逗号会报错,而如今不会报错,它是被容许的
function clownPuppiesEverywhere(param1, param2) {} //或者这样 function clownPuppiesEverywhere(param1, param2) {}
这样若是咱们有新参数,只须要日后加就好了,没必要往上一行末尾加逗号(真懒,加个逗号又不费事,鸡肋特性)
将异步变为同步神器。不再须要像 promise 那样回调了
async function getList(){ //用async指明当前方法中有异步,可是当作同步执行 await getId(); //若是getId是个promise,则只有状态为resolve时,才进入下一行。若是是普通函数,则默认是resolved let result = await getName(); //还能够有返回值。其值为resolved(xx)中的参数 }
await 的并行处理
//由于多个await都是依次日后台发送。若是这几个请求以前没有前后关联顺序,则彻底不必。能够作成并行发送 async getName() { let str = ""; await Promise.all([this.getPromise(),this.getPromise(),this.getPromise()]); return str; }
太复杂,我也没看懂
若是你想查看,这是详细
文档
在正则表达式中,“.” 点,表明 匹配除换行符以外的任何单个字符。
以下:
/.n/.test('an'); //. 匹配到了a /.n/.test('1n'); //. 匹配到了1 /.n/.test('nn'); //. 匹配到了n /.n/.test('\nn'); //.不会匹配 \n 返回false
假若有个需求,须要匹配任何字符。那怎么办.....
有个骚操做。
/foo[^]bar/.test('foo\nbar'); //返回true
用 [^] 替换掉. 它表示匹配包含n 在内的任意字符。
可这也太秀了,正常人都想不到这么写。这么写出来,别人也看不懂。
因此便有了下面的写法:
/foo.bar/s.test('foo\nbar'); //返回true
正则的后面加个 s 标志,你能够将 s 理解为 stop,终止的意思,即表示除了其余的,还能匹配终止符 n,换句话说如今这个. 啥都能匹配到
咱们能够总结下,到此为止,正则表达式标志符有:
g 全局搜索。 i 不区分大小写搜索。 m 多行搜索。 s 容许 . 匹配换行符。 u 使用unicode码的模式进行匹配。 y 执行“粘性”搜索,匹配从目标字符串的当前位置开始,可使用y标志。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x; // 1 y; // 2 z; // { a: 3, b: 4 } let n = { x, y, ...z }; n; // { x: 1, y: 2, a: 3, b: 4 }
或者
let [x,y,...z] = [ 1,2,3,4 ]; x; // 1 y; // 2 z; // [3, 4] let n = [ x, y, ...z ]; n; // [1, 2, 3, 4]
...x 始终表明这是个展开式,而 x 表明原式(对象,或者数组),
相似于高数中的泰勒展开式, ...x 就是那个展开式,x 就是那个原式。
好比:
function getName({a,b,...others}){ console.log(others) } getName({a:1,b:2,c:3,d:4})
因为...others 是展开式 c:3,d:4,那么 others 天然就是原式{c: 3, d: 4}
在 promise 结束时,不管结果是 resovled 或者是 rejected,finally 都会执行指定的回调函数。
这避免了一样的语句须要在 then()和 catch()中各写一次的状况。
因此 finally 中最适合须要进行"清空"的操做。好比 request 请求完成或失败后,都要将 loading 设置为 false。或者关闭打开的文件流对象,或者进行日志记录等。
finally()方法和 then()同样,执行后,也返回一个 Promise。
因此通常流程是
new Promise((resolved, rejected)=>{ //... //return resolved(res); 若是执行成功 //return rejected(); 若是执行失败 //... }).then(res=>{ //resolved的回调, res的值为resolved传入的参数 }).catch(err=>{ //rejected的回调。err为rejected传入的失败的缘由 }).finally(()=>{ //因为不关注resolved仍是rejected。因此没有参数。 })
咱们常常会经过 for....of 进行迭代,以下:
let arr = [1,2,3]; for(let num of arr) { console.log(num); }
输出 1,2,3
咱们能够想象下,加入 arr 中的每一项不是普通类型的对象,而是 promise 呢?
//分别建立三个promise对象 let p1 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(1) },1000) }); let p2 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(2) },1000) }); let p3 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(3) },1000) }); let arr = [p1,p2,p3]; for(let num of arr) { console.log(num); }
那执行结果会怎么样呢?
你将会在控制台直接看到下面三个结果。
> Promise {<pending>} > Promise {<pending>} > Promise {<pending>}
也就是说 arr 在迭代时,分别将 p1,p2,p3 输出控制台了,而 p1,p2,p3 是谁,固然是 promise 啊,只不过还没执行 resolved 而已,处于 pending 状态。
若是咱们想在 arr 迭代时,依次输出 p1,p2,p3 的 resolved 或者 rejected 结果呢?
咱们可使用下面的方式
//分别建立三个promise对象 let p1 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(1) },1000) }); let p2 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(2) },1000) }); let p3 = new Promise((resolved,rejected)=>{ setTimeout(()=>{ resolved(3) },1000) }); let arr = [p1,p2,p3]; for await(let num of arr) { console.log(num); }
在 for 后面,左圆括号前面,加个 await。
这样在每次 arr 迭代时,等待 num 的结果为 resolved 或者 rejected 时,才进入 for 的语句块。
结果输出为: 1 2 3
咱们能够想一想在没有 for-await-of 以前,是怎么用的?
let num = null; let arr = [p1,p2,p3]; num = await arr[0]; console.log(num); let num = await arr[1]; console.log(num); let num = await arr[2]; console.log(num);
是否是?可是若是 arr 中几个这么写还行,可若是多了呢,显然不如 for await of 方便。
try { throw new Error("出错了") } catch (err) { console.log(err.message); }
咱们一般会在 catch 中经过 err 来获取或者打印错误信息。
可是有些场景下咱们不须要 catch 中的 err,信息,只是捕获错误,而后作些其余的事。此时 err 参数被闲置,unused,未被使用。
针对这种状况,es 规范容许你省略 err 参数。
try { throw new Error("出错了") } catch () { console.log('321'); }
==Uncaught SyntaxError: Unexpected token ')'==
报错了,纳尼?
注意不是上面这种省略,而应该是下面的省略。
try { throw new Error("出错了") } catch{ console.log('321'); }
catch 后面直接跟{便可。
坑我给大家填上了,别在掉进去了。
因为 json 能够支持 "u2028"(行分隔符)和 "u2029"(段落分隔符)两个特殊字符。
咱们能够看看这两个字符长什么样
方方正正的,一个写着 LSEP,一个写着 PSEP.
建立个 json,包含 u2028.
没问题,能够解析。
可是这两个字符以前在 js 中是不能写的。因此 ECMA 在标准中实现了他们,以便在 js 中也能正常使用。
symbol 是一种基本数据类型。 每一个从 Symbol()返回的 symbol 值都是惟一的。一个 symbol 值能做为对象属性的标识符;这是该数据类型仅有的目的。
咱们能够经过下面方式获取 symbol 的描述。
Symbol('desc').description //desc
之前因为 function 继承了从 object 来的 toString 方法;因此 toString 为"[object Object]"
function getName(){ return "hello"; }; getName.toString() // "[object Object]"
而修订后的 function toString 覆盖了从 object 继承来的 toString 方法。返回一个包含定义函数的源文本段。
let obj = { id: 1, name: "hello", }; let entries = Object.entries(obj); // [["id",1],["name","hello"]]
一样咱们可使用 fromEntries 还原 obj
let obj = Object.fromEntries([ ["id", 1], ["name", "hello"], ]); // {id: 1, name: "hello"}
具体可参考 mdn 连接
let regexp = /te/; let str = 'test1test2'; str.match(regexp) // ['te'] 匹配到第一个位置就中止,并返回匹配到的字符串
加个 g 标志
let regexp = /te/g; let str = 'test1test2'; str.match(regexp) //["te", "te"] 返回全部匹配到的字符串
matchAll 会返回一个包含全部匹配正则表达式的结果及分组捕获组的迭代器。且正则表达式必须含有标志 g,不然报错
let regexp = /te/g; let str = 'test1test2'; str.matchAll(regexp) // RegExpStringIterator{} 但会迭代器。
能够经过解构方式取出迭代器。
let regexp = /te/g; let str = 'test1test2'; let arr = [...str.matchAll(regexp) ] // arr[0] // {0: "te", groups: undefined, index: 0, input: "test1test2", length: 1} arr[1] // {0: "te", groups: undefined, index: 5, input: "test1test2", length: 1}
这样咱们不只能获取到 匹配的字符串,仍是经过 index 知道匹配的位置。
在 js 中,引入模块的方式一般是:
import _ from "lodash"
在代码运行以前,在编译阶段,就已经绑定引入本地做用域。
可是若是咱们想在运行阶段动态的引入呢?
答案是没有办法。
若是你使用的是 webpack,则可使用 webpack 的 require("xxx") 动态的将模块引入。
因此 ECMA 直接在 js 核心库中,实现了这个 api。即便不适用 webpack,也能直接在浏览器上运行 import().
在 chrome 控制台咱们能够直接使用。
同时发现,import()返回的是个 promise 对象,
因此咱们能够这么用
import("/static/js/app.js").then(res=>{ //xxx }) 或者 await import("/static/js/app.js"); //后续代码
在 javascript 中,interget 的最大数为 2^53
Math.pow(2,53) // 9007199254740992
若是加 1,发现没变。
Math.pow(2,53)+1 // 9007199254740992
假如要表示 比 Math.pow(2,53) 还要大的数值怎么办?
可使用 BigInt 类型来表示
const theBiggestInt = 9007199254740991n; //后面追加 n,表示这是个bigInt类型 const alsoHuge = BigInt(9007199254740991); //直接经过构造器建立。
在 bigInt 中,最小的 1,用 1n 来表示,以下面的运算
1n+1n==2n 2n-1n == 1n 2n * 2n == 4n 4n / 2n == 2n 9007199254740992n +1n==9007199254740993n // //2^53加1 结果为
特殊状况
1n+1n==2 //true 0n == 0 //true //可是下面为false 1n+1n==2 //false 0n === 0 //false
特别的
1n+1 会直接报错 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
在 promise 中的组合器有下面几种:
const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 3000, "1oo"); }); const promise2 = new Promise((resolve, reject) => { setTimeout(reject, 1000, "foo"); }); const promises = [promise1, promise2]; Promise.allSettled(promises).then((results) => { //results [{"status":"fulfilled","value":"1oo"},{"status":"rejected","reason":"foo"}] });
在之前,从不一样的 JavaScript 环境中获取全局对象须要不一样的语句。在 Web 中,能够经过 window、self 或者 frames 取到全局对象,可是在 Web Workers 中,只有 self 能够。在 Node.js 中,它们都没法获取,必须使用 global。
globalThis 提供了一个标准的方式来获取不一样环境下的全局 this 对象(也就是全局对象自身)
这样在浏览器中, globalThis 就是 window 或者 self,
在 Web Workers 中, globalThis 就是 self,
在 nodejs 中,globalThis 就是 global。
快去把你项目中的 window,self,global 等替换成 globalThis 吧
for...in 语句以任意顺序遍历一个对象的除 Symbol 之外的可枚举属性。
在对象中使用 for....in 并无什么问题
for(let item in {id:1,name:'123'}){ console.log(item) //依次输出 id, name }
不建议 for....in 和数组搭配一块儿使用。为何呢,请看下面的例子:
for(let item in ['a','b','c']){ console.log("-===",item) //依次输出 '0' '1' '2', //注意 '0' '1' '2'并不是下标,而是枚举位置 }
咱们再给数组添对象添加个可枚举属性
let arr = [1,2,3]; Object.defineProperty(arr, "getName", { value: "hello", enumerable: true }); //等同于 let arr = [1,2,3]; Array.prototype.getName = "hello"; //紧接着咱们执行下面 for(let item in arr){ console.log(item) //依次输出 '0' '1' '2' 'getName' }
发现没?? 是不出错了,若是咱们项目中,在 util 的模块中,给 Array.prototype 上增长了一些 polyfill,那么咱们使用 for....in 遍历数组,就必然出错。
固然咱们能够经过其余方式避免。
好比
for(let i=0;i<arr.length;i++){ // } //或者 arr.forEach((value,index)=>{ // })
在之前咱们进行条件判断时:
let obj = { "circle": { "x": 0, "y": 0 }, "radius": 50 }; if(obj && obj.circle && obj.circle.x){ //而后才敢拿着 obj.circle.x 进行操做。不然假如obj为null,obj.circle 就会直接报错。 }
而如今不用这么麻烦
let obj = { "radius": 50 }; if(obj?.circle?.x){ // obj.circle.x 进行操做 }
?.表示是否有这个属性,若是没有(undifined),或者有,但值为 null。则直接短路不会再继续向下。从而保护了代码不被出错。
let obj = { "circle": { "y": 0 }, "radius": 50 }; if(obj?.circle?.x){ }else{ //不会报错。在执行到 ?.circle时获得undefiend,直接短路。返回undefiend, 进入 else }
一样,数组也能够
let arr = ['a','b']; arr?.[1] // 'b'
最后注意,可选链不能用于赋值操做
let arr = ['a','b']; arr?.[1] = 'c' //报错 Uncaught SyntaxError: Invalid left-hand side in assignment
它是个逻辑操做符,相似于 ||
|| 操做符,只有当左侧为假值(0, '', NaN, false, null,undefined)是才返回右侧的值.
0 || 'A' //A '' || 'A' //A NaN || 'A' //A false || 'A' //A null || 'A' //A undefined || 'A' //A
而 ??只有左侧为空值(只有 null 和 undefined)时才
0 ?? 'A'; // 0 '' ?? 'A' // '' NaN ?? 'A' //NaN false ?? 'A' //false null ?? 'A' //A undefined ?? 'A' //A
具体查看
文档
参考 ECMA 规范:
https://github.com/tc39/proposals/blob/master/finished-proposals.md