导航
[[深刻01] 执行上下文](https://juejin.im/post/684490...
[[深刻02] 原型链](https://juejin.im/post/684490...
[[深刻03] 继承](https://juejin.im/post/684490...
[[深刻04] 事件循环](https://juejin.im/post/684490...
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深刻08] 前端平安](https://juejin.im/post/684490...
[[深刻09] 深浅拷贝](https://juejin.im/post/684490...
[[深刻10] Debounce Throttle](https://juejin.im/post/684490...
[[深刻11] 前端路由](https://juejin.im/post/684490...
[[深刻12] 前端模块化](https://juejin.im/post/684490...
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深刻14] canvas](https://juejin.im/post/684490...
[[深刻15] webSocket](https://juejin.im/post/684490...
[[深刻16] webpack](https://juejin.im/post/684490...
[[深刻17] http 和 https](https://juejin.im/post/684490...
[[深刻18] CSS-interview](https://juejin.im/post/684490...
[[深刻19] 手写Promise](https://juejin.im/post/684490...
[[深刻20] 手写函数](https://juejin.im/post/684490...
[[react] Hooks](https://juejin.im/post/684490...
[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...
[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
[[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...
前置常识
包装对象
- <font color=red>包装对象:Number,String,Boolean三个对象能够把原始类型的值变成对象 -> new 调用</font>
- number,string,boolean三个原始类型的值,在肯定的条件下也会主动转为对象,也就是原始类型的包装对象
包装对象的目标
- 使得js中的对象涵盖所有的值
使得 ( 原始类型 ) 的值,能够不便的 ( 调用某些办法 )
- 比方
toString
valueOf
- 比方
<font color=red>总结</font> Nubmer,String,Boolean
- 作为构造函数:通过new命令调用,能够将 ( 原始类型 ) 的值转为 ( 对象 )
- 作为一般函数:能够将 ( 任何类型 ) 的值,转换成 ( 原始类型 ) 的值
构造函数
特点:
- 构造函数首字母通常大写
- 应用 new 命令调用,new命令调用时,能够在函数名前面加小括号,也能够不加
- 构造函数外部的this指向实例对象
- 应用构造函数,属性和办法都生成在实例上的,彼此之间不共享
<font color=red>new 命令总会返回一个对象</font>,要么是实例对象,要么是return后指定的对象
- 如果构造函数内 ( return ) 返回的是一个 ( 对象 ),new命令就会返回回这个 ( 对象 )
- 如果构造函数内 ( return ) 返回的是 ( 原始类型的值 ),new命令就会返回 ( this对象 )
- ( 一般函数 ) 应用 ( new )命令,会返回一个 ( 空对象 )
new命令的原理
- 创立一个空对象,即是须要返回的那个实例对象
- 将空对象的原型,指向构造函数的prototype属性
- 将构造函数外部的this绑定到这个空对象上
- 执行构造函数
一些单词
immediate: 立刻的shuffle: 洗牌flatten:扁平的infinity:无穷recursive:递归
call 模仿实现
call办法的作用
- 绑定函数的 this 指向
- 执行该函数 ( 在指定的作用域中执行该函数 )
call的参数
- 应该是一个 ( <font color=red>对象</font> )
- 如果传入 ( <font color=red>null,undefined,空</font> ),则默认传入 ( <font color=red>全局对象</font> ) window/global
- 如果参数是 ( <font color=red>原始值</font> ) 那么这个原始值会主动转成对应的 ( <font color=red>包装对象</font> ),而后传入call办法
能够接管多个参数
- 第一个参数是 this 要绑定的对象
- 前面的参数是函数调用时须要的参数
call办法的作用利用 ( 重要 )
call办法的一个利用就是 - 调用对象的原生办法
- 当一个对象调用继承的属性时,如果该对象本身也有一个同名的属性时,将会产生笼罩
- call办法能够把继承的办法的原始定义放在该对象上执行,这样无论该对象是否有同名的属性,都不会受影响
[1]// call办法的利用// - call办法的一个利用就是 - 调用对象的原生办法-------var obj = {};obj.hasOwnProperty('toString') // false,留神toString是继承的办法,空对象本身并没有该办法// hasOwnProperty 示意是否含有本身属性// 笼罩掉继承的 hasOwnProperty 办法// 通过在obj本人定义一个办法,这样就会笼罩原型链上的同名办法,并且这样就是本身属性obj.hasOwnProperty = function () {return true;};obj.hasOwnProperty('toString') // true,调用时先找本身是否有该办法,而本意是调用原型链上的hasOwnProperty办法// 这样就造成了后果偏差,能够用call办法解决// call办法的一个次要利用 - 调用对象的原生办法Object.prototype.hasOwnProperty.call(obj, 'toString') // false下面代码中,hasOwnProperty是obj对象继承的办法,如果这个办法一旦被笼罩,就不会失去正确后果。 - call办法能够解决这个问题 - 调用对象的原生办法 - 它将hasOwnProperty办法的原始定义放到obj对象上执行,这样无论obj上有没有同名办法,都不会影响后果。
- call办法的模仿实现 - es5
### call办法的模仿实现 - es5留神点:1. fn.call() 的参数 - 当为空,null,undefined时,示意传入全局对象window或者global - 当为原始类型的值时,会被转为包装对象再传入fn - 第一个参数是fn中this指向的对象,其余参数将作为调用fn时的参数2. fn能够有返回值3. 实现思路 - 在对象上 (增加) 函数 (办法) - 在该对象上(执行)该函数,此时this的指向就是该对象(运行时确定this指向) - 因为fn.call()是能够接管不定个数的参数的,怎么办? - 能够用arguments对象获取所有参数,这里须要去除第一个参数即this绑定的对象,前面的才是传入fn函数的参数 - 记得留神下 fn 也能够有返回值 - (删除)该函数4. eval(string) - 将字符串当作语句来执行 - 留神:参数必须是字符串 代码:const obj = { name: 'woow_wu7', age: 18}function fn(address, sex) { return this.name + this.age + sex + address }Function.prototype._call = function(obj) { const context = obj || window // 相当于 context = context ? context : window; // 相当于 const context = obj ? obj : window; // fn.call() 当参数为空,null,undefined时,默认是传入window对象 // 空,null,unfined的布尔值都是false const params = [] // 用于收集arguments中进来第一个参数外,剩下的每一个参数 for(let i = 1; i < arguments.length; i++) { params.push('arguments[' + i + ']') } // 留神:循环是从 1 开始的,因为arguments[0] 是fn中this绑定的对象,这里是收集传入fn的参数 // arguments是相似数组的对象,具备length属性 // params相当于 ['arguments[1]', 'argument[2]'] context.fn = this // --- 第一步:在对象上增加一个函数属性fn(对象中应该叫做办法) // 函数的值是this,即 fn 函数 // this这里指的是 fn 函数,因为this就是函数调用时所在的对象,fn._call()是fn在调用,所以this指向fn const res = eval('context.fn(' + params + ')') // fn函数可能有返回值 // + 号是存在(重载)的,即能够是(相加)或者(相连) // 当 + 运算子存在对象时,会先将对象转成原始类型的值,即先执行 valueOf -> 对象自身 -> toStirng -> 出object外,都是返回该类型对象的字符串模式 // params数组会被转成 'arguments[1],arguments[2]'这样的模式 // 例如:[1,'2',3,'4'].toString() -> "1,2,3,4"-> 数组返回数组的字符串模式 // 最终是这样的: const res = eval('context.fn(arguments[1],arguments[2])') Reflect.deleteProperty(context, 'fn') // delete context.fn // 删掉对象上的函数 // 留神:delete context.fn 这样的旧的写法会逐步被摈弃 return res // fn可能有返回值,须要返回后果}const res = fn._call(obj, 'chongqing', 'man')console.log(res, 'res')
call办法的模仿实现 - es6
### call办法的模仿实现 - es6const obj = {name: 'woow_wu7',age: 18}function fn(address, sex) {return this.name + this.age + sex + address }Function.prototype._call = function(context) {context = context ? context : window;context.fn = this;const res = context.fn(...[...arguments].slice(1));Reflect.deleteProperty(context, 'fn')//delete context.fn;return res;}const res = fn._call(obj, 'chongqing', 'man')console.log(res, 'res')
apply模仿实现
const obj = { name: 'wang',};function fn(name) { return { name: name || this.name }}Function.prototype._apply = function (context, arr) { context = context ? context : window; context.fn = this; let res = null; if (!arr) { //_apply第二个参数不存在,就不给fn传参,间接执行fn res = context.fn() } else { arr instanceof Array ? res = context.fn(...arr) : console.log('第二个参数只能是数组') } delete context.fn; return res;}const result = fn._apply(obj, ['woow-wu']);console.log(result)
bind模仿实现
bind函数的作用
- 返回一个新的函数
- 绑定this的指向
bind函数的参数
- 第一个参数:须要绑定的对象
- 前面的参数:作为函数的参数
留神点:
- 当第一个参数是null,undefined时,相当于讲this绑定到全局对象上( window|global )
除去第一个参数外,残余的参数作为 ( 须要绑定this的函数的全副或者局部参数 )
- bind时能够只传局部参数
- 残余的参数在返回的新函数被调用时传入
<font color=red>返回的新函数,能够应用new命令调用,即返回的新函数能够作为 ( 构造函数 )</font>,这种状况下
- bind时绑定的this生效,<font color=red>因为在构造函数中,this指向的是实例对象</font>
- 然而传入的参数无效
代码模仿实现
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script> const obj = { name: 'woow_wu7', age: 20 } function fn(sex, address) { this.sex = sex this.address = address return this.name + this.age + sex + address } fn.prototype.go = function() { console.log('hangzhou') return 'hangzhou' } // 当_bind()返回的新函数通过new命令调用时,生成的实例能够继承fn.prototype上的办法 Function.prototype._bind = function(context) { context = context ? context : widnow // 传入的第一个参数 // 如果是null或者undefined时,相当于传入全局对象 ( window|global ) const params = Array.prototype.slice.call(arguments, 1) // params:是外层函数承受的要传入被绑定函数的参数,除去第一个参数即除去绑定的对象 // arguments:实参列表 const self = this // 固定外层函数的this const Ftemp = function(){} // 寄生式继承 // 构造函数首字母通常大写 const fbind = function() { const bindParams = Array.prototype.slice.call(arguments) // fbind函数的实参 const totalParams = params.concat(bindParams) // 所有要传入被绑定函数的参数数组 return self.apply(this instanceof self ? this : context, totalParams) // self:是外层的this,调用时确定指向,即fn函数 // this:fbind函数中的this,如果是构造函数this就指向实例,如果不是就执行须要绑定的对象 // this instanceof self ? this : context // 因为:上面将 fbind.prototype => new Ftemp => this.prototype // 所以:如果是new命令在调用fbind的话,判断是true,绑定到实例this上,否则绑定到传入的对象上 // 可能有返回值,须要return } Ftemp.prototype = this.prototype fbind.prototype = new Ftemp() // 将fbind.prototype绑定到Ftemp的实例上 // 这样fbind作为构造函数时,fbind的实例能继承Ftemp实例上的属性和Ftemp原型链上的属性 return fbind // 返回一个新的函数 } const resFn = fn._bind(obj, 'man') const res = resFn('hangzhou') const res2 = new resFn('chongqing') const xx = res2.go() console.log(res, 'res') console.log(res2, 'res2') console.log(xx, 'xx') </script></body></html>
new命令的模仿实现
new命令的作用
- 执行构造函数
- 返回实例对象
构造函数返回值
- return 后跟一个对象,new命令返回这个对象
- return 后跟一个简略数据类型,new命令会不论这个值,返回this对象
继承相干
- 构造函数中的属性和办法,都是间接生成在实例上的,实例之间不共享,造成资源节约
- 实例能继constructor.prototype上的属性和办法,多个实例能够共享,批改则相互影响
arguments
- 应用argumetns的益处是在参数个数不确定的状况下,获取任意多的参数
手写原理
1.新建一个空对象
2.将空对象的隐式原型指向构造函数的显示原型
3.将构造函数中的this绑定到空对象上
4.执行构造函数
5.判断返回值,如果构造函数return一个对象,就返回构造函数返回的对象,否则返回空对象即this对象
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><script> function Constructor(name, age) { this.name = name this.age = age return this } function _new() { const obj = {} // 第一步 // 新建一个空对象 // 相当于 const obj = new Object() const paramsConstructor = Array.prototype.shift.call(arguments) // 将arguments转化成数组,并且取除数组中的第一个元素,即传入的构造函数 // 相当于 ([]).prototype.shift.call(arguments) // 相当于 ([]).shift.call(arguments) => 因为数组实例是继承了Array.prototype上的属性和办法 // 相当于 Array.prototype.shift.apply(arguments) => call 和 apply 都能够 // 留神: // push unshift pop shift都会扭转原数组 // push unshift 返回值是操作后,数组的长度 // pop shift 返回值是增加或者删除的元素 obj.__proto__ = paramsConstructor.prototype // 第二步 // 将 ( 空对象的隐式原型 ) 指向 ( 构造函数的显示原型 ) // 这样空对象就能够继承构造函数prototype上的属性和办法 // 留神: // const obj = {} 和 obj.__proto__ = paramsConstructor.prototype // 能够简写为:const obj = Object.create(paramsConstructor.prototype) // b = Object.create(a)作用是以参数对象a为原型,生成实例对象b - 即能够用一个对象创立实例对象 const res = paramsConstructor.apply(obj, arguments) // 第三步 // 将构造函数中的this绑定到空对象上,并执行构造函数 // 留神: // 这里是argumets是去除了结构函数参数后的,残余参数的汇合 // _new(constructor, p1, p2, ...) return /Object|Function/.test(Object.prototype.toString.call(res)) ? res : obj // 如果构造函数的返回值 // 是对象,就返回这个对象 // 是原始类型的值,就返回this对象,即空对象 } const instance = _new(Constructor, 'woow_wu7', 20) console.log(instance, 'instance')</script></body></html>
Debounce 防抖函数
性能:
- 延时执行,如果在延时的工夫内屡次触发,则从新计时
- 能够获取event事件对象作为参数和传入其余参数
- 第一点击立刻触发,而不是延时执行
- 能够勾销debounce的执行
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <div id="debounce">Debounce</div> <div id="cancel" style="margin-top: 20px">Debounce-cancel</div> <script> const fn = () => { console.log('fn running') } // 传入debounce须要延时执行的函数fn /** * @param {function} fn 须要debounce防抖函数解决的函数 * @param {number} delay 定时器延时的工夫 * @param {boolean} immediate 是否立刻执行 */ function debounce(fn, delay, immediate) { let timer = null debounce.cancel = () => { console.log('cancel running') clearTimeout(timer) } // 能够手动勾销debounce的执行 return (...args) => { if (immediate && !timer) { // 立刻执行的状况 // immediate = true // timer = false // 满足这两个条件就立刻执行 fn.apply(this, args) } if (timer) { console.log('timer exist') clearTimeout(timer) // timer存在,就革除定时器 } timer = setTimeout(() => { if (immediate) { // 立刻执行条件下 // 第一次执行 // 第一次立刻执行,则在延时的工夫内不触发第二次 // 第二次执行 // immediate = false,则不再进入该判断条件了 immediate = false return } console.log(args[0].target, 'e.target') fn() }, delay) } } const button = document.getElementById('debounce') const buttonCancel = document.getElementById('cancel') button.addEventListener('click', debounce(fn, 2000, true), false) buttonCancel.addEventListener('click', debounce.cancel, false) </script></body></html>
Throttle 节流函数
性能:
- 每隔一段时间,只执行一次
- 即在外层函数增加标记位,依据标记位的状况决定是否进入闭包
根底版<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <div id="throttle">Throttle</div> <script> const fn = () => { console.log('fn running') } function throttle(fn, delay) { let isRun = true // 标记位 let timer = null return (...args) => { // 标记位是false,就return if (!isRun) { return } isRun = false timer = setTimeout(() => { fn.apply(this, args) isRun = true // 执行完标记位改为true,则下次点击又能够执行了 clearTimeout(timer) // 革除定时器 }, delay) } } const button = document.getElementById('throttle') button.addEventListener('click', throttle(fn, 1000), false) </script></body></html>
工夫戳版<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <div id="throttle">Throttle</div> <script> const fn = () => { console.log('fn running') } function throttle(fn, delay) { let previous = null return (...args) => { const now = + new Date() // + new Date() 获取当初的工夫戳,即间隔1970.1.1 00:00:00的毫秒数字 // 留神:单位是毫秒数,和定时器的第二个参数吻合,也是毫秒数,尽管这里没有定时器 // ( + ) 能够把任意类型的数据转成数值,只有两种可能,即 ( 数值 ) 和 ( NaN ) // + new Date() === new Date().valueOf() === new Date().getTime() if (now - previous > delay) { fn.apply(this, arguments) previous = now // 执行完fn,同步工夫,用于下一次计算 // 第一次:now - previous > delay是true,所以立刻执行一次 // 而后 previous = now // 第二次:第二次能进来的条件就是差值毫秒数超过delay毫秒 // 这样频繁的点击时,就能依照固定的频率执行,当然是升高了频率 } } } const button = document.getElementById('throttle') button.addEventListener('click', throttle(fn, 1000), false) </script></body></html>
数组乱序
sort
- 默认会依照字典进行排序,数字会先被转成字符串,在排序
- <font color=red>扭转原数组,没有返回值</font>
- 2021/03/18更正,sort扭转原数组,有返回值,返回值就是排序过后的数组即被扭转过后的数组
参数
- 能够没有
也能够是一个函数(自定义形式排序就承受函数为参数),参数函数又有两个参数
- 返回值大于0,示意比拟的第一个成员排在第二个成员的前面 (
a-b>0示意升序
) - 返回值小于0,示意比拟的第一个成员排在第二个成员的后面(
a-b<0示意降序
) - 返回值等于0,地位不变
- 返回值大于0,示意比拟的第一个成员排在第二个成员的前面 (
乱序的利用
- 换一批
- 猜你喜爱
- 中奖计划
办法1arr.sort(() => Math.random() - .5)const arr = [1, 3, 7, 2, 5, 4, 6]arr.sort(() => Math.random() - .5)// 因为:Math.random() 的值得范畴区间是 [0, 1)// 所以:Math.random() - 0.5 ===> 大于0或小于0的概率都是50% // Math.random()参数参数的返回值: // 函数返回值大于0,示意两个比拟的成员,第一个排在第二个的前面 // 函数返回值小于0,示意两个比拟的成员,第一个排在第二个的后面 // 函数返回值等于0,示意两个比拟的成员,地位不变 // 当小数是0点几时,能够省略后面的0console.log(arr, 'arr')
----办法1存在的问题:1. 概率不均等2. 造成概率不均等的起因: - 因为sort()办法底层因为不同浏览器实现形式不一样,v8中 - 当数组长度小于10时,用的是插入排序 - 否则,应用的是插入排序和疾速排序的混合排序 - 插入排序就会造成概率不均等 - 因为把无序局部的元素插入到有序局部中时,如果一旦找到了地位,剩下的元素就没有在和缓存的值就行比拟了 - 即没有机会比拟所有元素,造成概率不均等,就得不到齐全随机的后果3. 如何晓得sort()排序造成概率不均等呢?能够用上面的办法统计const countArr = [0, 0, 0, 0, 0] // 用来寄存数组arr最初一个地位,各数字呈现的次数for (let i = 0; i < 100000; i++) { const arr = [0, 1, 2, 3, 4] arr.sort(() => Math.random() - .5) // 随机打乱 countArr[arr[4]]++ // arr[4] 的可能值是 0 1 2 3 4 // 如果呈现过一个值,就在countArr绝对应的地位加1,用来统计各数字呈现的次数}console.log(countArr)// [24977, 6951, 20999, 18784, 28289]// 从后果中能够看出:arr数组的最初一个地位,呈现1的概率显著要小很多,即 arr[4] = 1 的概率显著小很多// 其余地位上也会得出相应的后果 arr[x] = 1 的概率都比其余数字呈现的概率小
----间接插入排序温习插入排序- 思维:将一个数组分成有序局部和无序局部,将无序局部的一个值插入到有序局部,有序局部依然有序 (联想打牌时插牌)- 原理: - 1. 将数组分成两个局部,右边是有序局部(初始成员个数为1),左边是无序局部(下标从1开始,有序数组中取了一个) - 2. 从左边的无序局部顺次取出一个值,插入到有序局部,直到取完无序局部 - 3. 如何找有序局部的插入地位? 1. 先缓存须要插入的无序局部的值,用一个变量来缓存 let temp = arr[i] 2. 从有序局部的最初地位找(arr[i-1]),如果arr[i-1] > arr[i] 则该元素往后挪动一位 3. 如果有序该地位依然比须要插入的值大,有序中该地位的值,也后挪动一位,直到 j>=0const arr = [1, 4, 3, 2]const insert_sort = (arr) => { for(let i = 1, len = arr.length; i < len; i++) { // 循环数组的无序局部,从1开始,因为假如初始化时有序局部有一个元素 let temp = arr[i] // 缓存须要插入有序局部的这个无序局部的值,因为有序局部可能会往后挪动地位,将其笼罩 let j = i - 1 // j = i - 1 示意有序局部的最初一个元素的地位,有序局部从最初的地位顺次往前查找须要插入的地位 // 这里采纳有序局部从后往前寻找,也能够从前往后找 while(j >= 0 && arr[j] > temp) { // 有序局部循环条件,如果有序的地位的值比无序中的大并且要保障j>=0,就把有序的值往后挪动一位 arr[j+1] = arr[j] // 有序该地位值大于temp,则往后挪动一位 j-- // 顺次往前查找 } arr[j+1] = temp // 循环完后,j+1就是须要插入的地位,因为条件是大于temp,不满足时,j+1就是要插入的地位 } return arr // 最初返回数组}console.log(insert_sort(arr))
----乱序改进版Fisher–Yates1. 应用sort(() => Math.random() - .5)存在的问题就是不能齐全实现全副元素的比拟,造成概率不均等2. 为什么叫 Fisher–Yates 呢? - 因为这个算法是由 Ronald Fisher 和 Frank Yates 首次提出的。3.shuffle:是洗牌的意思4.Fisher–Yates的原理:- 首先选取(数组最初一个地位)的元素和(数组长度随机地位)上的元素替换- 接着选取(数组倒数第二个地位)的元素和(除去最初一个地位剩下的数组地位,即倒数第二到第一个元素)上的元素替换- 反复以上步骤,直到length >= 15.代码// 数组乱序const arr = [1, 2, 3, 4, 5, 6]function shuffle(arr) { let length = arr.length while(length > 1) { const pivot = Math.floor(Math.random() * length--) // 1. 留神这里是先赋值,再减 1,即Math.floor(Math.random() * 6) // (Math.random() * 6的区间[0, 6) // Math.floor(Math.random() * 6) 区间是 [0-5] // 2. 语句以分号结尾,一个分号就示意一个语句完结。所以这里赋值语句前面记得加分号 // 3. ;[arr[pivot], arr[length]] = [arr[length], arr[pivot]]这里条语句前加上分号,因为后面也是语句 // 在小括号结尾,或者中括号结尾的语句,后面的语句开端须要加分号,或者加到本条语句后面 //4. const pivot = Math.floor(Math.random() * length--); 这样也行 ;[arr[pivot], arr[length]] = [arr[length], arr[pivot]] // 这里的length是减1之后的length // const temp = arr[length] // arr[length] = arr[pivot] // arr[pivot] = temp } return arr}console.log(shuffle(arr))
数组扁平化
Array.prototype.flat(拉平的层数)
参数
- 整数,示意拉平的层数
- Infinity:示意任意层数的数组都拉平,都开展成一元数组
返回值:
- 一个新数组,不扭转原数组
前置常识
concat
- 作用:用于多个数组的合并,将新数组的成员增加到原数组的尾部
- 返回值:新的数组
不扭转原数组
办法1Array.prototype.flat(Infinity)-------2021/03/18 更新如下Array.prototype.flat.call(arr, Infinity)
办法2递归 - recursiveconst arr = [1, [2, [3, 4, 5, [6]]], 7, 8]function flat(arr) {let result = []for (let i = 0; i < arr.length; i++) {if (Array.isArray(arr[i])) {// 如果每一项还是数组,就递归,最终把数据都收集到result中result = result.concat(flat(arr[i]))}else { // 递归完结条件result.push(arr[i])}}return result}const res = flat(arr)console.log(res, 'res')
- toString
- 如果数组元素都是数值(留神所有的数组中的所有元素都要是数字),能够应用toString()
然而这种办法应用的场景却十分无限,如果数组是 [1, '1', 2, '2'] 的话,这种办法就会产生谬误的后果。
const arr = [1, [2, 3, [4, [5]]]]
function flat(arr) {
return arr.toString().split(',').map(item => +item)
// arr.toString()数组的字符串模式,会展平。 => 1,2,3,4,5
// arr.toString().split(',')以,为分隔符,将字符串转成数组 ["1", "2", "3", "4", "5"]
// +item相当于Number(item)
}
const res = flat(arr)
console.log(res)- reduce((累积变量, 当面变量,以后地位index, 原数组), 累计变量的初始值)
参数
函数
- 参数
- 累积变量prev:如果reduce()没有第二个参数,就是数组的第一个元素
- 以后变量next:如果reduce()没有第二个参数,就是数组的第二个元素
累计变量初始值
- 累计变量初始值,即赋值给prev,此时next就是数组的第一个成员
const arr = [1, [2, [3, 4, 5, [6]]], 7, 8, 9]
function flat(arr) {
return arr.reduce((prev, next) => {
return prev.concat(Array.isArray(next) ? flat(next) : next)
// 返回拼接后的数组,如果以后变量是数组,recursive执行
}, [])
// 这里指定了初始值是 []
// prev = []
// next = 1,因为prev初始值是空数组,所以next的初始值是 1// reduce((accumulate, currentValue, index, arr) => {....}, [])
// 第一个参数:是一个函数
// 第一个参数:累积变量,默认是数组的第一个元素
// 第二个参数:以后变量,默认是数组的第二个元素
// 第三个参数:以后地位(以后变量在数组中的地位)
// 第四个参数:原数组
// 第二个参数:累积变量的初始值,留神如果指定了初始值,那么以后变量就从数组的第一个元素开始
}
const res = flat(arr)
console.log(res, 'res')
// [1, 2, 3, 4, 5, 6, 7, 8, 9] "res"应用 ...开展运算符
let arr = [1, [2, [3, 4]]];
function flat(arr) {
while (arr.some(item => Array.isArray(item))) { // 循环判断是不是数组,是数组就开展
arr = [].concat(...arr); // 每次都扁平一层
}
return arr;
}
console.log(flat(arr))
XMLHttpRequest
如何获取response???
- xhr.response
- xhr.responseText
- xhr.responseXML
xhr.responseText
- 在 xhr.responseType = 'text' , '',不设置时,xhr实例对象上才有此属性,此时能力调用
xhr.response
- 在 xhr.responseType = 'text' ,'' 时,值是 ( '' )
- 在 xhr.resposneType 是其余值时,值是 ( null )
xhr.responseType
- text
- document
- json
- blob
- arrayBuffer
const xhr = new XMLHttpRequest()// new 命令总是返回一个对象,要么是this对象,要么是return前面跟的对象// new 调用的是构造函数,阐明XMLHttpRequest是一个构造函数
- 初始化 HTTP 申请参数,比方url,http申请办法等,但并 ( 不发送申请 )
xhr.open() 办法次要供 xhr.send() 办法应用
xhr.open(method, url, async, username, password)
参数
- method:http申请的办法,包含 GET POST HEAD
- url:申请的地址
async:是否异步
- true,默认值,异步申请,通常须要调用 onreadystatechange() 办法
- false,对 send() 办法的调用将阻塞,直到响应齐全接管
(2) xhr.send()
发送一个http申请
xhr.send(body)
- get申请:get申请的参数能够间接写在 open() 办法中
post申请:post申请的参数写在 send() 办法中
留神:
- body参数的数据类型会影响 requestHeader 中的 Content-Type 的默认值,如何手动指定则会笼罩默认值
- 如果data是 Document 类型,同时也是HTML Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8;
- 如果data是 DOMString 类型,content-type默认值为text/plain;charset=UTF-8;
- 如果data是 FormData 类型,content-type默认值为multipart/form-data; boundary=[xxx]
- 如果data是其余类型,则不会设置content-type的默认值
(3) xhr.setRequestHeader()
- 指定一个http申请的头部,只有在 readState = 1 时能力调用
setRequestHeader能够调用的机会
- 在 readyStaet = 1 时
- 在 open() 办法之后,send() 办法之前
- 其实 1 2 是一个意思
xhr.setRequestHeader('name', 'value')
参数
- name:头部的名称
- value:头部的值
留神
- setRequestHeader() 办法能够 ( 屡次调用 ) ,值不是 ( 笼罩override ) 而是 ( 追加append )
- setRequestHeader() 只有在 readyState = 1 时能力调用,即 open() 办法之后,send() 办法之前
(4) xhr.getResponseHeader()
指定http响应头部的值
(5) xhr.abort()
- 勾销以后响应,敞开连贯并且完结任何未决的网络流动
- xhr.abort()会将 readyState 重置为0
- 利用:勾销申请,在申请耗时太长,响应不再有必要时,调用该办法
abort:是终止的意思
(6) xhr.onreadystatecange()
- 在 readyState 状态扭转时触发
- xhr.onreadystatechange() 在 readyState = 3 时,可能屡次调用
- onreadystatechange 都是小写
readyState 驼峰
readyState状态
- UNSENT ------------- xhr对象胜利结构,open() 办法未被调用
- OPEND ------------- open() 办法被调用,send() 办法未被调用,setRequestHeader() 能够被调用
- HEADERS_RECEIVED --- send() 办法曾经被调用,响应头和响应状态曾经返回
- LOADING ------------ 响应体 ( response entity body ) 正在下载中,此状态下通过 xhr.response 可能曾经有了响应数据
NODE ------------- 整个数据传输过程完结,不论本次申请是胜利还是失败
(7) xhr.onload
- 申请胜利时触发,此时 readyState = 4
留神:重点!!!
- 1.除了在 xhr.onreadystatechange 指定的回调函数的 readyState = 4 时取值
2.还能够在 xhr.onload事件中取值
xhr.onload = function() {// 申请胜利if (xhr.status === 200 ) { // do successCallback}
}
3.判断 xhr.status === 200 是有坑的,因为胜利时返回的状态码不只有200,上面的写法更靠谱
xhr.onload = function () {//如果申请胜利if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { // 304 not modified 资源未被批改 协商缓存 // 2结尾的状态码,示意申请胜利 //do successCallback}
}
(8) xhr.timeout
- 设置过期工夫
- 问题1:申请的开始工夫怎么确定?是 ( xhr.onloadstart ) 事件触发的时候,也就是xhr.send()调用的时候
- 解析:因为xhr.open()只是创立了链接,当并没有真正传输数据,只有调用xhr.send()时才真正开始传输
- 问题2:什么时候是申请完结?
解析:( xhr.loadend ) 事件触发时完结
(9) xhr.onprogress 下载进度信息
(10) xhr.upload.onprogress 上传进度信息
xhr.upload.onprogress = function(e) {
if ( e.lengthComputable ) {const present = e.loaded / e.total * 100;
}
}XMLHttpRequest申请案例
XMLHttpRequest申请案例----<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><button id="buttonId">点击,申请数据</button><script> const button = document.getElementById('buttonId') button.addEventListener('click', handleClick, false) function handleClick() { const xhr = new XMLHttpRequest() xhr.open('GET', 'http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1=明星&tag2=全副&ie=utf8', true) // open()办法 xhr.setRequestHeader('Content-Type', 'application/json') // setRequestHeader必须在open()办法后,send()办法前调用,即 readyState === 1时 xhr.responseType = 'text' xhr.timeout = 10000 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { // 这里通过 this 代替 xhr 其实是一样的 // 因为 this 在运行时确定指向,xhr实例在调用onreadystatechange办法,所以this指向xhr实例 console.log(JSON.parse(this.responseText)) // 等价于 console.log(JSON.parse(xhr.responseText)) } } xhr.onload = function () { if ((xhr.status >= 200 && xhr.status < 300) || (xhr.status === 304)) { console.log(JSON.parse(xhr.responseText), 'xhr.onload是在申请实现时触发的回调') } } xhr.send() // 发送申请 }</script></body></html>
材料
call,apply 模仿实现 https://github.com/mqyqingfen...
bind模仿实现 https://juejin.im/post/684490...
new模仿实现 https://github.com/mqyqingfen...
new 模仿实现
1.https://github.com/mqyqingfen...
2.https://segmentfault.com/a/11...
3.https://segmentfault.com/a/11...
Throttle Debounce 我的掘金 https://juejin.im/post/684490...
数组乱序 https://juejin.im/post/684490...
数组扁平化 https://github.com/mqyqingfen...