Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写 call、apply、bind 实现 #79

Open
laoergege opened this issue Oct 16, 2021 · 1 comment
Open

手写 call、apply、bind 实现 #79

laoergege opened this issue Oct 16, 2021 · 1 comment

Comments

@laoergege
Copy link
Owner

laoergege commented Oct 16, 2021

  • call、apply 调用目标函数并改变其 this 指向
    • fun.call(thisArg, arg1, arg2, ...) 第二参数起为参数列表
    • func.apply(thisArg, [argsArray]) 第二参数为参数数组或者类数组
  • bind 返回一个新函数,其 this 绑定目标对象
    • 调用新函数时,将给定参数列表作为新函数的参数序列的前若干项
    • new 调用时,忽略绑定的对象
var obj = {
    value: 1
}

function bar(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}

Function.prototype.call2 = function(context) {
    var _context = typeof context === 'undefined' ?  window : context
    _context.fn = this

    var args = []
    for (let i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
    }

    var result = eval('_context.fn(' + args + ')') // array.toString [1, 2, 3] => '1,2,3'

    delete _context.fn
    return result
}

console.log('call2', bar.call2(obj, 'lys', 1))

Function.prototype.apply2 = function(context, arr) {
   var _context = typeof context === 'undefined' ?  window : context
    _context.fn = this

    var args = []
    for (let i = 0; i < arr.length; i++) {
        args.push('arr[' + i + ']')
    }

    var result = eval('_context.fn(' + args + ')') // array.toString [1, 2, 3] => '1,2,3'

    delete _context.fn
    return result
}

console.log('apply2', bar.apply2(obj, ['lys', 1]))

Function.prototype.bind2 = function(context) {
    var fn = this
    var args = Array.prototype.slice.call(arguments, 1)
    var wrapper = function () {
        var _args = args.concat(Array.prototype.slice.call(arguments)) // 调用新函数时,将给定 bind 后面的参数列表作为新函数的参数序列的前若干项
        
        if(this instanceof fn) { // new 调用时,忽略 bind 指定的 context
            fn.apply(this, _args)
            return this
        } else {
            return fn.apply(context, _args)
        }
    }
    wrapper.prototype = fn.prototype // new 调用时,将原型为绑定原函数的原型

    return wrapper
}

console.log(bar.bind2(obj)('lys', 1))

console.log(bar.bind2(obj, 'lys')(1))

console.log(new (bar.bind2(obj, 'lys'))(1)) // . 优先级比 new 高

apply 的一些妙用:使用 apply 巧妙得将参数列表的函数支持参数数组

1、Math.max。用它来获取数组中最大的一项 Math.max.apply(null, nums)
2、类数组方法借用,实现两个数组合并 Array.prototype.push.apply(arr1, arr2)

@laoergege laoergege changed the title 实现 call、bind、apply 手写 call、apply、bind 实现 Oct 26, 2021
@laoergege
Copy link
Owner Author

laoergege commented Dec 6, 2021

ES6 简约版实现

Function.prototype.call2 = function(ctx, ...args) {
  ctx = ctx || window

  ctx.fn = this
  const result = ctx.fn(...args)
  delete ctx.fn
  return result
}

Function.prototype.apply2 = function(ctx, args) {
  ctx = ctx || window

  ctx.fn = this
  const result = ctx.fn(...Array.from(args))
  delete ctx.fn
  return result
}

Function.prototype.bind2 = function(ctx, ...args) {
  const fn = this
  const wrapper = function (...args2) {
    if(new.target) {
      ctx = Object.create(fn.prototype)
      ctx.fn = fn
      const result = ctx.fn(...args, ...args2)
      delete ctx.fn
      return typeof result === 'object' ? result : ctx
    } else {
      ctx.fn = fn
      const result = ctx.fn(...args, ...args2)
      delete ctx.fn
      return result
    }
  }

  wrapper.prototype = fn.prototype
  return wrapper
}

console.log(bar.bind2(obj)('lys', 1))

console.log(bar.bind2(obj, 'lys')(1))

let t = new (bar.bind2(obj, 'lys'))(1)
console.log(t, t instanceof bar) 

function A() {

}

let a = A.bind2({a: 1})

new a() instanceof a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant