首页
统计
赞助
留言
关于
更多
友链
壁纸
直播
Search
1
Joe — 一款个人类型Typecho主题
33,050 阅读
2
Joe开源目录程序系统
12,105 阅读
3
Typecho自定义后台编辑器功能
8,474 阅读
4
Joe主题实现自动更新
7,655 阅读
5
Joe主题自定义搭配色教程
4,538 阅读
WEB前端
CSS
React
Vue 2.0
Vue 3.0
JavaScript
TypeScript
Typecho
小程序
苹果CMS
其他
生活
登录
Search
https://78.al
累计撰写
76
篇文章
累计收到
3,776
条评论
首页
栏目
WEB前端
CSS
React
Vue 2.0
Vue 3.0
JavaScript
TypeScript
Typecho
小程序
苹果CMS
其他
生活
页面
统计
赞助
留言
关于
友链
壁纸
直播
搜索到
23
篇与
JavaScript
的结果
2022-07-13
JS压缩图片并保留图片元信息
JS实现图片压缩比较简单,但是图片经过压缩后,压缩后的图片的元信息(拍摄时间、设备、地点)等会丢失掉,如果在特殊场景中需要使用这些元信息的话,就会出现问题了,因此需要将未压缩前的图片元信息填充至压缩后的图片中,以下是实现代码// 封装一个获取变量的数据类型函数 const getType = (data: unknown): string => { const toStingResult = Object.prototype.toString.call(data); const type = toStingResult.replace(/^\[object (\w+)\]$/, "$1"); return type.toLowerCase(); }; // 封装一个将 Base64 的字符串转换成 Blob 流的函数 const dataURLtoBlob = (dataURL: string): Blob | null => { const dataType = getType(dataURL); if (dataType !== "string") return null; const arr = dataURL.split(","); if (!arr[0] || !arr[1]) return null; const code = window.atob(arr[1]); const mimeExpRes = arr[0].match(/:(.*?);/); if (!mimeExpRes) return null; let len = code.length; const mime = mimeExpRes[1]; if (!mime) return null; const ia = new Uint8Array(len); while (len--) ia[len] = code.charCodeAt(len); return new Blob([ia], { type: mime }); }; // 利用规律编码格式把里面的标记以及值等分割开来,传原图片的 ArrayBuffer 进来 const getSegments = (arrayBuffer: ArrayBuffer): number[][] => { if (!arrayBuffer.byteLength) return []; let head = 0; let length, endPoint, seg; const segments = []; const arr = [].slice.call(new Uint8Array(arrayBuffer), 0); while (1) { if (arr[head] === 0xff && arr[head + 1] === 0xda) break; if (arr[head] === 0xff && arr[head + 1] === 0xd8) { head += 2; } else { length = arr[head + 2] * 256 + arr[head + 3]; endPoint = head + length + 2; seg = arr.slice(head, endPoint); head = endPoint; segments.push(seg); } if (head > arr.length) break; } return segments; }; // 传入上面 getSegments 的返回值,取出EXIF图片元信息 const getEXIF = (segments: number[][]): Array<number> => { if (!segments.length) return []; let seg: Array<number> = []; for (let i = 0; i < segments.length; i++) { const item = segments[i]; if (item[0] === 0xff && item[1] === 0xe1) { seg = seg.concat(item); } } return seg; }; // 将 getEXIF 获取的元信息,插入到压缩后的图片的 Blob 中,传 压缩图片后的 Blob 流 const insertEXIF = (blob: Blob, exif: number[]): Promise<Blob> => { return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => { const arr = [].slice.call(new Uint8Array(fileReader.result as ArrayBuffer), 0); if (arr[2] !== 0xff || arr[3] !== 0xe0) { return reject(new Error("Couldn't find APP0 marker from blob data")); } const length = arr[4] * 256 + arr[5]; const newImage = [0xff, 0xd8].concat(exif, arr.slice(4 + length)); const uint8Array = new Uint8Array(newImage); const newBlob = new Blob([uint8Array], { type: "image/jpeg" }); resolve(newBlob); }; fileReader.readAsArrayBuffer(blob); }); }; // 压缩图片逻辑 const compressImage = (file: File, quality: number): Promise<Blob | null> => { return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => { const img = new Image(); img.src = fileReader.result as string; img.onload = () => { const { width, height } = img; const canvas = window.document.createElement("canvas"); const ctx = <CanvasRenderingContext2D>canvas.getContext("2d"); canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); const fileData = canvas.toDataURL("image/jpeg", quality); const fileBlob = dataURLtoBlob(fileData); resolve(fileBlob); }; img.onerror = (err) => reject(err); }; fileReader.onerror = (err) => reject(err); fileReader.readAsDataURL(file); }); }; /** * @description: 完整的压缩图片,最终对外暴露的函数 * @param {File} file * @param {number} quality 0 - 1 * @return {Promise<File>} */ export default (file: File, quality = 0.5): Promise<File> => { return new Promise((resolve, reject) => { const dataType = getType(file); if (dataType !== "file") return reject(new Error(`Expected parameter type is file, You passed in ${dataType}`)); if (file.type.indexOf("image") === -1) return resolve(file); // 压缩图片 compressImage(file, quality) .then((compressdBlob) => { if (!compressdBlob) return resolve(file); const fileReader = new FileReader(); fileReader.onload = () => { // 获取图片元信息 const segments = getSegments(fileReader.result as ArrayBuffer); const exif = getEXIF(segments); // 没有元数据的时候, 直接抛出压缩后的图片 if (!exif.length) return resolve(new File([compressdBlob], file.name, { type: file.type, lastModified: file.lastModified })); // 有元数据的时候, 将元信息合并到压缩图片里 insertEXIF(compressdBlob, exif) .then((newBlob) => resolve(new File([newBlob], file.name, { type: file.type, lastModified: file.lastModified }))) .catch(() => resolve(file)); }; fileReader.onerror = () => resolve(file); fileReader.readAsArrayBuffer(file); }) .catch(() => resolve(file)); }); };
2022年07月13日
2,050 阅读
13 评论
19 点赞
2022-07-13
记录部分安卓手机 input type="file"的onchange不能触发问题
今天写了一个文件上传,发现在部分安卓手机上触发不了onchange事件,代码如下:<input type="file" id="input" /> <script> // 在部分安卓手机不触发 input.onchange = () => { alert(123) } </script>经过不断测试后发现,在标签上补上accept="image/*"后可解决该问题,修复后的代码如下:<input type="file" accept="image/*" id="input" /> <script> input.onchange = () => { alert(123) } </script>
2022年07月13日
982 阅读
4 评论
12 点赞
2021-06-28
原生JS模拟虚拟dom,虚拟dom转真实dom
<div id="root" class="aaa"> <div class="title">11</div> <div class="title">222</div> <ul> <li>111</li> <li>222</li> <li>333</li> </ul> </div> <script type="text/javascript"> class VNode { constructor(name, attrs, value, type) { this.name = name this.attrs = attrs this.value = value this.type = type this.children = [] } addChildren(node) { this.children.push(node) } } function create(node) { let _vnode = null if (node.nodeType === 1) { const attributes = [...node.attributes] const attrs = attributes.reduce((prev, curr) => { prev[curr.name] = curr.value return prev }, {}); _vnode = new VNode(node.nodeName.toLowerCase(), attrs, null, node.nodeType) for (let item of node.childNodes) { _vnode.addChildren(create(item)) } } else if (node.nodeType === 3) { _vnode = new VNode(node.nodeName.toLowerCase(), null, node.nodeValue, node.nodeType) } return _vnode } let vnode = create(document.querySelector("#root")) console.log(vnode); function parse(node) { let nodeEl = null if (node.type === 1) { nodeEl = document.createElement(node.name) for (let key in node.attrs) nodeEl.setAttribute(key, node.attrs[key]) node.children.forEach(item => { nodeEl.appendChild(parse(item)) }); } else { nodeEl = document.createTextNode(node.value) } return nodeEl } let dom = parse(vnode) console.log(dom); </script>
2021年06月28日
759 阅读
4 评论
6 点赞
2021-06-28
chrome闭包断点
首先呢,必须搞清楚闭包这个概念:闭包其实是一个特殊的对象,他由两部分组成,一个是执行上下文(代号A),以及在该执行上下文中创建的函数(代号B),当B执行时,如果访问了A中变量对象的变量,那么闭包就产生了。下面这个例子会产生闭包function add(x) { return function _add(y){ return x + y } } var sum = add(2)(3)上面这个例子毫无疑问肯定是 5那么使用chrome浏览器单步调试一下将断点打到 var sum = add(2)(3) 这里Brealpoints 表示当前打的断点是什么CallStack 表示当前函数调用栈Scope 表示当前作用域Scope 里面的 Local 表示当前活动对象Scope 里面的 Closure 表示闭包图上一直点击红色箭头所指按钮表示单点调试,代码一步一步执行,等执行到return那里的时候,右侧 Scope 里会多出一个 Closure 属性,括号内的add表示当前闭包函数是add,闭包产生的值是 x = 2有时候调试会发现没有这个 Closure 属性,例如下面这个例子const obj = { name: '我是需要被绑定改变this指向的对象' } function fn() { } Function.prototype.bind = function (sbbbbb) { const bind = Symbol(); sbbbbb[bind] = this return function () { sbbbbb[bind](); } } fn.bind(obj);这个例子是 bind 改变 this 指向的实现方式,将断点打向 fn.bind(obj) 接着看动态图在动画的过程中,当走到 sbbbbb[bind]() 的时候,Local 里面会多出一个 Return value: ƒ () 的属性,展开这个属性,会发现有个 [[Scopes]] 的属性,接着展开 [[Scopes]],里面有个 Closure (Function.bind) {sbbbbb: {…}, bind: Symbol()} 就表示当前产生的闭包
2021年06月28日
910 阅读
7 评论
7 点赞
2021-06-23
原型继承和 Class 继承
⾸先先来讲下 class ,其实在 JS 中并不存在类, class 只是语法糖,本质还是函数class Person {} Person instanceof Function // true组合继承function Parent(value) { this.val = value } Parent.prototype.getValue = function() { console.log(this.val) } function Child(value) { Parent.call(this, value) } Child.prototype = new Parent() const child = new Child(1) child.getValue() // 1 child instanceof Parent // true{callout color="#6db9f3"}以上继承的⽅式核⼼是在⼦类的构造函数中通过 Parent.call(this) 继承⽗类的属性,然后改变⼦类的原型为 new Parent() 来继承⽗类的函数。这种继承⽅式优点在于构造函数可以传参,不会与⽗类引⽤属性共享,可以复⽤⽗类的函数,但是也存在⼀个缺点就是在继承⽗类函数的时候调⽤了⽗类构造函数,导致⼦类的原型上多了不需要的⽗类属性,存在内存上的浪费{/callout}寄⽣组合继承这种继承⽅式对组合继承进⾏了优化,组合继承缺点在于继承⽗类函数时调⽤了构造函数,我们只需要优化掉这点就⾏了function Parent(value) { this.val = value } Parent.prototype.getValue = function() { console.log(this.val) } function Child(value) { Parent.call(this, value) } Child.prototype = Object.create(Parent.prototype, { constructor: { value: Child, enumerable: false, writable: true, configurable: true } }) const child = new Child(1) child.getValue() // 1 child instanceof Parent // trueclass继承class Parent { constructor(value) { this.val = value } getValue() { console.log(this.val) } } class Child extends Parent { constructor(value) { super(value) this.val = value } } let child = new Child(1) child.getValue() // 1 child instanceof Parent // true
2021年06月23日
425 阅读
3 评论
16 点赞
2021-06-16
DOM同级传递事件
大家到现在所了解到的事件,基本都离不开浏览器的行为。比如点击鼠标、按下键盘等等,这些都可以被浏览器感知到,进而帮助我们转换成一个“信号”触发对应处理函数。但是还有一些行为,是浏览器感知不到的。比如说看这样一段 html<div class="a"></div> <div class="b"></div> <div class="c"></div>如果我们仅仅想监听 a 这个元素上的点击行为,我们可以用 addEventListener 来安装监听函数var a = document.getElementById('a') document.addEventListener('click',function(){ console.log('a') })但是,如果现在想实现这样一种效果在点击A之后,B 和 C 都能感知到 A 被点击了,并且做出相应的行为——就像这个点击事件是点在 B 和 C 上一样。我们知道,借助时间捕获和冒泡的特性,我们是可以实现父子元素之间的行为联动的。但是此处,A、B、C三者位于同一层级,他们怎么相互感知对方身上发生了什么事情呢?“A被点击了”这件事情,可以作为一个事件来派发出去,由 B 和 C 来监听这个事件,并执行各自身上安装的对应的处理函数。在这个思路里,“A被点击了”这个动作挺特别,特别就特别在浏览器不认识它。因为浏览器不认识它,所以浏览器不肯”帮忙“,不会帮咱去感知和派发这个动作。不过没关系,感知和派发,咱都可以自己来实现首先大家需要了解的是,自定义事件的创建。比如说咱们要创建一个本来不存在的"clickA"事件,来表示 A 被点击了,可以这么写<div class="a"></div> <div class="b"></div> <div class="c"></div> <script> /* 获取元素 */ const a = document.querySelector(".a") const b = document.querySelector(".b") const c = document.querySelector(".c") /* 创建一个自定义事件 */ const aEvent = new Event("sb250") a.addEventListener("click", function (e) { console.log('a'); /* 派发给b和c */ b.dispatchEvent(aEvent) c.dispatchEvent(aEvent) }) /* b监听 */ b.addEventListener("sb250", function (e) { console.log('b') }) c.addEventListener("sb250", function (e) { console.log('c') }) </script>可以看到,浏览器控制台能正确的打印 a b c,因此实现了对a点击事件的监听
2021年06月16日
253 阅读
1 评论
5 点赞
2021-06-11
手撸call apply bind
如果自己去实现call apply bind,看上去挺复杂,写起来其实就几行代码因为call和apply一样,只是传参不一样,所以我就只写一个call实现call(其实只有2行代码)/* 随便定义一个对象,待会将函数内的this指向指向倒这个对象 */ const obj = { name: '我是需要被绑定改变this指向的对象' } /* 需要改变this指向的函数,没有使用call时,this指向window */ function fn(arg) { console.log('fn---------', this); console.log('fn---------', arg); } /* * * 重写call方法 * target 需要把this改变到哪个目标 * args 传递进来的参数 */ Function.prototype.call = function (target, ...args) { /* 这里的this就指向上面的fn函数 */ console.log(this); /* 随便定义一个变量,用于在目标对象里存fn函数,这里使用symbol更好 */ target['sb250'] = this /* 这样target目标上就有个sb250的属性,并且属性值就是fn函数 */ console.log(target); /* 调用这个sb250,并把参数传入进去就实现this改变了 */ target['sb250'](args) /* 防止给target上多出多余没用的参数,在将sb250删除掉 */ delete target['sb250'] } fn.call(obj, '我是傻逼')实现bind因为bind的调用方式,是返回一个新函数,在调用一次,例如:fn.bind(null)(options),所以需要用到高阶函数/* 随便定义一个对象,待会将函数内的this指向指向倒这个对象 */ const obj = { name: '我是需要被绑定改变this指向的对象' } /* 需要改变this指向的函数,没有使用call时,this指向window */ function fn(arg) { console.log('fn---------', this); console.log('fn---------', arg); } /* * * 重写call方法 * target 需要把this改变到哪个目标 * args 传递进来的参数 */ Function.prototype.bind = function (target) { target['sb250'] = this /* 这里使用高阶函数接收参数 */ return (...args) => { target['sb250'](args) delete target['sb250'] } } fn.bind(obj)('我是大傻逼!!!')
2021年06月11日
409 阅读
2 评论
7 点赞
2021-05-29
class中函数的this指向
定义一个基础的类class Person { constructor(name = '杜恒') { this.name = name } speak() { console.log(this); } }将上面的类实例出一个对象p,并调用p的speak方法const p = new Person() p.speak() // Person {name: "杜恒"}上面的打印结果显示由类构造出的实例对象,因此this会指向由类构造出的实例对象尝试将p实例对象身上的speak方法赋值给另一个变量进行调用const test = p.speak test() // undefined打印undefind,因此上面的方法可以改写成如下const test = function () { "use strict" console.log(this); } test() // undefined由此可以得出,在class中,定义的方法,class会默认在函数体内开启严格模式,严格控制this的指向
2021年05月29日
221 阅读
6 评论
3 点赞
2021-05-18
ES9正则拓展
正则命名捕获分组// 利用正则取出href的值,和显示的文本 const str = '<a href="http://baidu.com">百度一下,你就凉了</a>' const reg = /<a href="(.*)">(.*)<\/a>/ const res = reg.exec(str) console.log(res)上面的取值方法,需要通过索引值进行获取,如果正则发生变动,则索引会跟随发生改变,下面利用正则命名分组进行提取// 利用正则取出href的值,和显示的文本 const str = '<a href="http://baidu.com">百度一下,你就凉了</a>' const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/ const res = reg.exec(str) console.log(res)取出的值会放在groups对象内,此时直接通过对象获取,并且正则发生变化,取值方式不变正向断言 & 反向断言// 通过正向断言取出数字555 const str = "我那么多遗憾,那么多期盼555你知道吗" const reg = /\d+(?=你知道)/ const res = reg.exec(str) console.log(res) // 通过反向断言取出数字555 const str = "我那么多遗憾,那么多期盼555你知道吗" const reg = /(?<=期盼)\d+/ const res = reg.exec(str) console.log(res)dotAll模式在正则中,. 表示匹配除换行以外的任意字符,如果想让.也匹配换行,那么需要将正则改成dotAll模式,元字符为s。例如:// 将电影名称和时间提取成数组 const str = ` <ul> <li> <a>肖生客的救赎</a> <p>2021/05/18</p> </li> <li> <a>希望样与灰太狼</a> <p>2021/05/20</p> </li> <ul> ` // 加上s修饰符,.将会匹配换行 const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>.*?<\/li>/gs let res = null; let arr = [] while(res = reg.exec(str)) { arr.push({ title: res[1], time: res[2] }) } console.log(arr)matchAll// 将电影名称和时间提取成数组 const str = ` <ul> <li> <a>肖生客的救赎</a> <p>2021/05/18</p> </li> <li> <a>希望样与灰太狼</a> <p>2021/05/20</p> </li> <ul> ` const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>.*?<\/li>/gs // 返回一个可迭代对象 const res = str.matchAll(reg) console.log([...res])
2021年05月18日
337 阅读
2 评论
2 点赞
2021-05-14
ES6模块化
暴露模块分别暴露形式 m1.jsexport const a = 1; export function fn() { console.log(1) }统一暴露形式 m2.jsconst a = 1; function fn() { console.log(1) } export { a, fn }默认暴露形式 m3.jsexport default { a: 1, fn() { console.log(1) } }{dotted startColor="#ff6c6c" endColor="#1989fa"/}引入模块<script type="module"> // 1. 通用引入方式 import * as m1 from "./m1.js" import * as m2 from "./m2.js" import * as m3 from "./m3.js" // 2. 解构赋值形式 import { a, fn } from "./m1.js" import { a as m2_a, fn as m2_fn } from "./m2.js" import { default as m3 } from "./m3.js" // 3. 简便形式,只针对于默认暴露形式!!! import a from "./m3.js" </script>
2021年05月14日
203 阅读
3 评论
0 点赞
2021-05-14
IP正则
/^((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))$/.test('0.0.0.0')
2021年05月14日
336 阅读
6 评论
9 点赞
2021-04-11
ES6
模板字符串带标签的模板的字符串const name = 'tom' const gender = false // 通过标签函数,获取模板字符串传递进来的值,可以将这个结果进行处理,返回出去 function tagFn(str, name, gender) { console.log(str) console.log(name) console.log(gender) return '111' } const res = tagFn`hello, my name is ${name}, my sex is${gender}`字符串拓展方法判断字符串是否包含某个字符const msg = 'hello world' msg.includes('world') // true判断字符串是否以某个字符开头,或以某个字符结尾const msg = 'hello world' msg.startsWith('h') // true msg.endsWith('d') // trueReflect提供一套统一操作对象的API,例如,像下面操作一个对象const obj = { name: 'hi', age: 18 } // 判断某个属性是否在对象里 console.log('name' in obj) // 删除对象某个属性 console.log(delete obj.name) // 将对象的属性名提取成数组 console.log(Object.keys(obj))从上面的实现方法可以看出,操作一个对象,用的方法很乱,一下用 in 一下用 delete ,一下用Object上的方法,同样操作一个对象,没有统一性,换成用 Reflect 实现:const obj = { name: 'hi', age: 18 } // 判断某个属性是否在对象里 console.log(Reflect.has(obj, 'name')) // 删除对象某个属性 console.log(Reflect.deleteProperty(obj, 'name')) // 将对象的属性名提取成数组 console.log(Reflect.ownKeys(obj))Map数据结构有下面的一个例子,将 对象 作为另一个对象的键存入,但是实际打印结果不如所愿:const obj = {} obj[{ name: 'tom' }] = 'Tom' console.log(obj) // { [object Object]: "Tom" }无论传入什么数据当作属性名,都会强制转换成字符串,转换不了的会转换成 [object Object] ,而通过Map数据映射结构就没有这种问题const map = new Map() const tom = { name: 'tom' } map.set(tom, 'Tom') console.log(map.get(tom))SymbolSymbol 是一个基本数据类型,主要用于创建一个独一无二的值console.log(Symbol() === Symbol()) // false,永远不可能有两个相等的Symbol在没有Symbol之前,一个对象里面,很有可能会存入属性名相同的两个属性,例如// 例如这是一个全局公用的缓存对象 const cache = {} ... cache['file_cache'] = 'xxx' ... // 代码多了后,可能就不知道cache里面有个file_cache的缓存名,然后继续存入file_cache缓存名,此时就会将之前传入的缓存给覆盖掉,因此就可能会出现问题 cache['file_cache'] = 'ooo'将上方缓存改成 Symbol 去实现:// 例如这是一个全局公用的缓存对象 const cache = {} ... // 括号里的是对这个Symbol的描述 cache[Symbol('file_cache')] = 'xxx' ... cache[Symbol('file_cache')] = 'ooo' /* * * { * Symbol(file_cache): "xxx" * Symbol(file_cache): "ooo" * } * */Symbol 还可以作为对象的私有成员,让外界去无法访问var People = (function() { var name = Symbol("name"); class People { constructor(n) { //构造函数 this[name] = n; } sayName() { console.log(this[name]); } } return People; })();Symbol内置属性1、控制instanceof的返回值class Person { static [Symbol.hasInstance](params) { console.log(params) console.log("我被用来检测") return false } } let o = {} console.log(o instanceof Person)Symbol的一些补充1、创建的Symbol属性,永远 不相等console.log(Symbol() === Symbol()) // false2、注册全局的Symbol,注册全局的Symbol,接收一个字符串,如果不是字符串会转换成字符串,相同的字符串生成的全局Symbol会 相等const s1 = Symbol.for('s1') const s2 = Symbol.for('s1') console.log(s1 === s2) // true console.log(Symbol.for(true) === Symbol.for('true')) // true3、对象内的Symbol,通过传统的获取,是 获取不到 的const obj = { [Symbol('hi')]: 'hi', name: 'hello' } for(let key in obj) { console.log(key) // name } // 如果想获取Symbol,需要通过Object.getOwnPropertySymbols()方法 console.log(Object.getOwnPropertySymbols(obj))生成器 Generatorfunction *fn() { console.log(111) yield 100 console.log(222) yield 200 console.log(333) yield 300 } var g = fn() // 调用一次,执行一次 g.next() // 100 {value: 100, done: false}使用生成器实现一个自增的idfunction *createId() { let id = 1 while(true) { yield id++ } } var c = createId() c.next() // 1 c.next() // 2 c.next() // 3generator传参function *fn(arg) { console.log(arg) let one = yield 100 console.log(one) let two = yield 100 console.log(two) let three = yield 300 console.log(three) } var g = fn('arg') // 会传入到arg处 g.next() g.next('aaa') // 会传入到one的位置 g.next('bbb') // 会传入到two的位置 g.next('ccc') // 会传入到three的位置使用生成器实现迭代器let todos = { life: ['吃饭', '睡觉'], study: ['语文', '数学', '英语'], [Symbol.iterator]: function* () { const all = [...this.life, ...this.study] for (let item of all) { yield item } } } for (let item of todos) { console.log(item); }使用生成器实现按顺序调用 用户信息 -> 订单信息 -> 商品信息 接口function getUser() { setTimeout(() => { let data = '用户信息' iterator.next(data) }, 1000) } function getOrder() { setTimeout(() => { let data = '订单信息' iterator.next(data) }, 1000) } function getGoods() { setTimeout(() => { let data = '商品信息' iterator.next(data) }, 1000) } function* fn() { let user = yield getUser() console.log(user); let order = yield getOrder() console.log(order); let goods = yield getGoods() console.log(goods); } let iterator = fn() iterator.next()数组includes方法与indexOf的区别indexOf无法查找NaN的情况const arr = ['foo', NaN, null] arr.indexOf(NaN) // -1使用includes可以查找出const arr = ['foo', NaN, null] arr.includes(NaN) // trueclass中将属性挂载到实例身上class Person { name = '杜恒' say = function() { console.log(1) } }class中static关键字的作用static 关键字定义的属性,是属于类上面的,而不是实例化出来的对象上面class Person { static name = 'hello' } const p = new Person() console.log(p.name) // undefined console.log(Person.name) // helloObject.entries & Object.fromEntries将对象转换成二维数组const obj = { name: '杜恒', age: 23 } const res = Object.entries(obj) console.log(res) // [ ["name", "杜恒"], ["age", 23] ]将二维数组、Map转换成普通对象const map = new Map() map.set("name", "杜恒") map.set("age", 23) console.log(map) const res = Object.fromEntries(map) console.log(res)Promise.allSettledPromise.allSettled用于接收一个数组, 与Promise.all很像,但是Promis.all是所有结果为成功状态,才返回成功。而Promise.allSellted是无论有没有失败情况下,都返回成功globalThis无论在什么环境下,globalThis都指向全局对象
2021年04月11日
658 阅读
18 评论
5 点赞
1
2