如何在ES5环境下实现letlet's函数

  js select 搜索框_js 淘宝搜索框代码生成器_js搜索框代码

  如何在ES5环境下实现let

  对于这个问题,我们可以直接查看babel转换前后的结果,看一下在循环中通过let定义的变量是如何解决变量提升的问题。

  js搜索框代码_js select 搜索框_js 淘宝搜索框代码生成器

  babel在let定义的变量前加了道下划线,避免在块级作用域外访问到该变量,除了对变量名的转换,我们也可以通过自执行函数来模拟块级作用域。

  <pre class="code-snippet__js" data-lang="javascript">(function(){` for(var i = 0; i < 5; i ++){ console.log(i) // 0 1 2 3 4 }})(); `console.log(i)      // Uncaught ReferenceError: i is not defined</pre>

  如何在ES5环境下实现const

  实现const的关键在于Object.defineProperty()这个API,这个API用于在一个对象上增加或修改属性。

  通过配置属性描述符,可以精确地控制属性行为。Object.defineProperty()接收三个参数:

  <pre class="code-snippet__js" data-lang="javascript">Object.defineProperty(obj, prop, desc)</pre>

  js搜索框代码_js select 搜索框_js 淘宝搜索框代码生成器

  js搜索框代码_js select 搜索框_js 淘宝搜索框代码生成器

  对于const不可修改的特性,我们通过设置writable属性来实现。

  <pre class="code-snippet__js" data-lang="javascript">function _const(key, value) { ` const desc = { value, writable: false } Object.defineProperty(window, key, desc)} _const('obj', {a: 1}) //定义objobj.b = 2 //可以正常给obj的属性赋值`obj = {}                //抛出错误,提示对象read-only</pre>

  手写call()

  call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

  语法:function.call(thisArg, arg1, arg2, ...)

  call()的原理比较简单,由于函数的this指向它的直接调用者js搜索框代码,我们变更调用者即完成this指向的变更:

  <pre class="code-snippet__js" data-lang="javascript">//变更函数调用者示例`function foo() { console.log(this.name)} // 测试const obj = { name: '写代码像蔡徐抻'}obj.foo = foo // 变更foo的调用者`obj.foo()       // '写代码像蔡徐抻'</pre>

  基于以上原理, 我们两句代码就能实现call()

  <pre class="code-snippet__js" data-lang="javascript">Function.prototype.myCall = function(thisArg, ...args) {` thisArg.fn = this // this指向调用call的对象,即我们要改变this指向的函数 return thisArg.fn(...args) // 执行函数并return其执行结果}`</pre>

  但是我们有一些细节需要处理:

  <pre class="code-snippet__js" data-lang="javascript">Function.prototype.myCall = function(thisArg, ...args) {` const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性 thisArg = thisArg || window // 若没有传入this, 默认绑定window对象 thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数 const result = thisArg[fn](...args) // 执行当前函数 delete thisArg[fn] // 删除我们声明的fn属性 return result // 返回函数执行结果} //测试`foo.myCall(obj)     // 输出'写代码像蔡徐抻'</pre>

  手写apply()

  apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

  语法:func.apply(thisArg, [argsArray])

  apply()和call()类似,区别在于call()接收参数列表,而apply()接收一个参数数组,所以我们在call()的实现上简单改一下入参形式即可。

  <pre class="code-snippet__js" data-lang="javascript">Function.prototype.myApply = function(thisArg, args) {` const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性 thisArg = thisArg || window // 若没有传入this, 默认绑定window对象 thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数 const result = thisArg[fn](...args) // 执行当前函数 delete thisArg[fn] // 删除我们声明的fn属性 return result // 返回函数执行结果} //测试`foo.myApply(obj, [])     // 输出'写代码像蔡徐抻'</pre>

  手写bind()

  bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

  语法: function.bind(thisArg, arg1, arg2, ...)

  从用法上看,似乎给call/apply包一层function就实现了bind():

  <pre class="code-snippet__js" data-lang="javascript">Function.prototype.myBind = function(thisArg, ...args) {` return () => { this.apply(thisArg, args) }`}</pre>

  但我们忽略了三点:

  bind()除了this还接收其他参数,bind()返回的函数也接收参数,这两部分的参数都要传给返回的函数。

  new的优先级:如果bind绑定后的函数被new了,那么此时this指向就发生改变。此时的this就是当前函数的实例。

  没有保留原函数在原型链上的属性和方法。

  <pre class="code-snippet__js" data-lang="javascript">Function.prototype.myBind = function (thisArg, ...args) {` var self = this // new优先级 var fbound = function () { self.apply(this instanceof self ? this : thisArg, args.concat(Array.prototype.slice.call(arguments))) } // 继承原型上的属性和方法 fbound.prototype = Object.create(self.prototype); return fbound;} //测试const obj = { name: '写代码像蔡徐抻' }function foo() { console.log(this.name) console.log(arguments)} foo.myBind(obj, 'a', 'b', 'c')()    //输出写代码像蔡徐抻 ['a', 'b', 'c']`</pre>

  手写一个防抖函数

  防抖和节流的概念都比较简单,所以我们就不在“防抖节流是什么”这个问题上浪费过多篇幅了,简单点一下:

  防抖,即短时间内大量触发同一事件,只会执行一次函数,实现原理为设置一个定时器,约定在xx毫秒后再触发事件处理,每次触发事件都会重新设置计时器,直到xx毫秒内无第二次操作。

  防抖常用于搜索框/滚动条的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费。

  <pre class="code-snippet__js" data-lang="javascript">function debounce(func, wait) {` let timeout = null return function() { let context = this let args = arguments if (timeout) clearTimeout(timeout) timeout = setTimeout(() => { func.apply(context, args) }, wait) }`}</pre>

  手写一个节流函数

  防抖是延迟执行,而节流是间隔执行,函数节流即每隔一段时间就执行一次,实现原理为设置一个定时器,约定xx毫秒后执行事件。

  如果时间到了,那么执行函数并重置定时器,和防抖的区别在于js搜索框代码,防抖每次触发事件都重置定时器,而节流在定时器到时间后再清空定时器。

  <pre class="code-snippet__js" data-lang="javascript">function throttle(func, wait) {` let timeout = null return function() { let context = this let args = arguments if (!timeout) { timeout = setTimeout(() => { timeout = null func.apply(context, args) }, wait) } }}`</pre>

  实现方式2:使用两个时间戳prev旧时间戳和now新时间戳,每次触发事件都判断二者的时间差,如果到达规定时间,执行函数并重置旧时间戳。

  <pre class="code-snippet__js" data-lang="javascript">function throttle(func, wait) {` var prev = 0; return function() { let now = Date.now(); let context = this; let args = arguments; if (now - prev > wait) { func.apply(context, args); prev = now; } }}`</pre>

  数组扁平化

  对于[1, [1,2], [1,2,3]]这样多层嵌套的数组,我们如何将其扁平化为[1, 1, 2, 1, 2, 3]这样的一维数组呢:

  (1)ES6的flat()

  <pre class="code-snippet__js" data-lang="javascript">const arr = [1, [1,2], [1,2,3]]`arr.flat(Infinity)  // [1, 1, 2, 1, 2, 3]`</pre>

  (2)序列化后正则

  <pre class="code-snippet__js" data-lang="javascript">const arr = [1, [1,2], [1,2,3]]`const str = [${JSON.stringify(arr).replace(/(\[|\])/g, '')}]`JSON.parse(str)   // [1, 1, 2, 1, 2, 3]</pre>

  (3)递归

  对于树状结构的数据,最直接的处理方式就是递归

  <pre class="code-snippet__js" data-lang="javascript">const arr = [1, [1,2], [1,2,3]]`function flat(arr) { let result = [] for (const item of arr) { item instanceof Array ? result = result.concat(flat(item)) : result.push(item) } return result} flat(arr) // [1, 1, 2, 1, 2, 3]`</pre>

  (4)reduce()递归

  <pre class="code-snippet__js" data-lang="javascript">const arr = [1, [1,2], [1,2,3]]`function flat(arr) { return arr.reduce((prev, cur) => { return prev.concat(cur instanceof Array ? flat(cur) : cur) }, [])} flat(arr)  // [1, 1, 2, 1, 2, 3]`</pre>

  (5)迭代+展开运算符

  <pre class="code-snippet__js" data-lang="javascript">// 每次while都会合并一层的元素,这里第一次合并结果为[1, 1, 2, 1, 2, 3, [4,4,4]]`// 然后arr.some判定数组中是否存在数组,因为存在[4,4,4],继续进入第二次循环进行合并let arr = [1, [1,2], [1,2,3,[4,4,4]]]while (arr.some(Array.isArray)) { arr = [].concat(...arr);} console.log(arr)  // [1, 1, 2, 1, 2, 3, 4, 4, 4]`</pre>

  - 本章完 -

  #heading-48

  js 淘宝搜索框代码生成器_js select 搜索框_js搜索框代码

  你“在看”我吗?

文章由官网发布,如若转载,请注明出处:https://www.veimoz.com/1828
0 评论
650

发表评论

!