Fork me on GitHub

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

JS 手写call 、 apply 、 bind 、 new 、防抖debounce 、节流throttle(内含闭包原理解释)

**这里很多手写代码都是基于闭包,闭包如何能保持访问外层函数的变量?

**执行栈会将调用到的函数的执行上下文( 变量环境、词法环境、this、outer), 压入执行的顺序:全局windows -> closezure函数 -> increase函数 只要increase继续调用,执行栈就无法跨过它销毁closezure的执行上下文,即可以继续访问外层函数变量

手写call
// 手写 call
Function.prototype.mycall = function(){ 
  var args = Array.from(arguments)
  var obj = args.slice(0,1)[0]
  var fn = this
  if(!obj) obj = window
  obj.fn = fn
  args = args.slice(1)
  console.log(args)
  let ret = obj.fn(...args) 
  delete obj.fn
  return ret
}

function fn(){
  console.log(this.name)
}

var o = {name : 'lhx'}
fn.mycall(null)

ES6 ...扩展运算符 , 放在函数定义中可以将剩余参数表示为 Aarry对象;如果是给函数传参时使用,则可以将 Array数组解析为 ',' 分隔的参数传递给函数

手写apply
Function.prototype.myapply = function(context,arg){
  var args = Array.from(arg)
  var obj = context ? context : window
  var fn = this
  obj.fn = fn
  let ret = obj.fn(...args)
  delete obj.fn
  return ret
}
手写bind
Function.prototype.mybind = function(context){
  var args = Array.from(arguments).slice(1)
  var obj = context ? JSON.parse(JSON.stringify(context)) : window
  var fn = this

/* 这里 bind 的 2 个复杂点:
*****返回的是一个函数,并不是直接执行的结果
*****返回的函数仍然可以传参
*****这里使用闭包来 封装
*/
  function newfn(){
    args = args.concat(Array.from(arguments))
    obj.fn = fn
    return obj.fn(...args)
  }
  return newfn
}

var o = {
  name : 'lxs',
  getname : function(age,sex){  console.log(this.name+age+sex) }
}

var b = {
	name : 'bbb'
}

var gn = o.getname
var gnb = gn.mybind(b , '18' )
gnb('nam')
手写new
function myNew(constructFn){
// 首先创建一个新对象
  var newO = {}
  constructFn.apply(newO , Array.from(arguments).slice(1))
// 满足 instanceof 判断要求
  newO.__proto__ = constructFn.prototype
  return newO
}
function Person(name, age){
    this.name = name
    this.age = age
}

var p = myNew(Person,'lhx',12)
var p2 = new Person()
// 测试
console.log(p instanceof Person)
手写防抖debounce :在 wait 等待时间内,如果再次触发则会重新计时
使用场景:查询输入框连续敲击,敲完之后再去查询,避免资源浪费
function debounce(fn,wait){
  var st = null 
  return function(){
    if(st){
      clearInterval(st)
      st = setTimeout(()=>{
        fn()
        clearInterval(st)
      },wait)
    }
  }
}
手写节流throttle : 在 wait 时间内,只会触发一次
使用场景:鼠标 mousemove 事件,避免大量占用 JS 引擎线程
function throttle(fn,wait){
  var st = null, start = (new Date).getDate()       
  return function(){
    var end = (new Date).getDate()
    if(end-start > wait){
      start = (new Date).getDate()
      st = setTimeout(()=>{
        fn()
        clearInterval(st)
      },wait)
    }
  }
}
手写interator:
你让它动(.next)一下,它就动一下
    function myInterator(arr){
      var list = Array.from(arr)
      let length = list.length
      let curIndex = 0
      this.next = function(){
        if(curIndex === length){
          return "done"
        }
 // 创造了一个没有 原型链的空对象
        let ret = Object.create(null)
        ret.value = list[curIndex++]
        ret.done = false
        return ret
      }
    }
    var testIt = new myInterator([1,2,3])
    console.log(testIt.next());
    console.log(testIt.next());
    console.log(testIt.next());
    console.log(testIt.next());
    console.log(testIt.next());
    console.log(testIt.next());
posted @   365/24/60  阅读(188)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示