shfshanyue / Daily-Question

互联网大厂内推及大厂面经整理,并且每天一道面试题推送。每天五分钟,半年大厂中
https://q.shanyue.tech
4.92k stars 508 forks source link

【Q656】JS 中如何实现 call/apply #674

Open shfshanyue opened 3 years ago

shfshanyue commented 3 years ago

在 JS 中如何实现 call/apply?

相关问题:

shfshanyue commented 3 years ago
const call = (fn, thisObj, ...args) => {
  thisObj.fn = fn;
  const r = thisObj.fn(...args);
  delete thisObj.fn; 
  return r;
}
wussss commented 3 years ago

bind/softBind/apply/call 都是this显式绑定的方法

Function.prototype.mySoftBind = function (ctx = globalThis) {
  const fn = this;
  const args = Array.from(arguments).slice(1);
  function bound() {
    if (!this || this === globalThis) {
      fn.apply(ctx, args);
    } else {
      fn.apply(this, args);
    }
  }
  bound.prototype = fn.prototype;
  return bound;
};
Function.prototype.myCall = function (ctx = globalThis) {
  const args = Array.from(arguments).slice(1);
  const key = Symbol("key");
  ctx[key] = this;
  const res=ctx[key](...args);
  delete ctx[key]
  return res
};
Function.prototype.myApply = function (ctx = globalThis) {
  const args = arguments[1];
  const key = Symbol("key");
  ctx[key] = this;
  const res=ctx[key](...args);
  delete ctx[key]
  return res
};
heretic-G commented 3 years ago

Function.prototype.call = function call(arm, ...args) {
    let fun = this
    if (typeof fun !== 'function') throw TypeError('must is function')
    let armObj = arm
    if (typeof arm !== 'object') {
        armObj = Object(arm)
    }

    let symbolKey = Symbol('tempKey')
    armObj[symbolKey] = fun
    let result = armObj[symbolKey](...args)
    delete armObj[symbolKey]
    return result
}

Function.prototype.apply = function call(arm, ...args) {
    let fun = this
    if (typeof fun !== 'function') throw TypeError('must is function')
    let armObj = arm
    if (typeof arm !== 'object') {
        armObj = Object(arm)
    }

    let symbolKey = Symbol('tempKey')
    armObj[symbolKey] = fun
    let result = armObj[symbolKey](args)
    delete armObj[symbolKey]
    return result
}

// TODO 完善bind 这里其实还有很多问题
Function.prototype.bind = function aBind (that, ...args) {
    let armFun = this
    if (typeof armFun !== 'function') throw TypeError('must a function')
    function BoundFun (...other) {
        if (new.target) {
            return new armFun(...args, ...other)
        } else {
            return armFun.call(that,...args, ...other)
        }
    }
    BoundFun.__proto__ = armFun.__proto__
    BoundFun.prototype = undefined

    return BoundFun
}
iceycc commented 3 years ago
  let person1 = {
    name: "Tom",
    sayHi(to,...args) {
      console.log(`Hi,${to}, my name is ${this.name}。${args && args.toString()}`);
    },
  };
  person1.sayHi();

  let person2 = {
    name: "Jerry",
  };
  // call
  person1.sayHi.call(person2, "Heydi");
  // apply
  person1.sayHi.apply(person2, ["Heydi"]);
  // bind
  let sayHiToJark = person1.sayHi.bind(person2, "Heydi"); // 柯里化
  sayHiToJark("Wellcom to you");
  // my call
  Function.prototype.myCall = function(ctx,...args){
    let fn = this
    if(typeof fn !== 'function') throw TypeError('must is fucntion')
    let thisObj = ctx;
    if(typeof ctx !== 'object'){
      thisObj = Object(ctx)
    }
    const key = Symbol("key");
    thisObj[key] = fn;
    const res = thisObj[key](...args);
    delete thisObj[key];
    return res;
  }
  person1.sayHi.myCall(person2,"Tim")
  // my apply
  Function.prototype.myApply = function(ctx,args){
    let fn = this
    if(typeof fn !== 'function') throw TypeError('must is fucntion')
    let thisObj = ctx;
    if(typeof ctx !== 'object'){
      thisObj = Object(ctx)
    }
    if(!Array.isArray(args)) throw TypeError('must is array')
    const key = Symbol("key");
    thisObj[key] = fn;
    const res = thisObj[key](...args);
    delete thisObj[key];
    return res;
  }
  person1.sayHi.myApply(person2,["Tim"])
  // my bind
  Function.prototype.myBind = function(ctx,...args){
    const fn = this
    return function(...args2){
      const key = Symbol("key");
      ctx[key] = fn;
      const res = ctx[key](...args,...args2)
      delete ctx[key]
      return res;
    }
  }
  let sayHiToMary = person1.sayHi.bind(person2,'Mary')
  sayHiToMary('Wellcom to you')
Vi-jay commented 2 years ago
Function.prototype.myCall = function (target) {
  const args = [].slice.apply(arguments, [1]);
  const fnName = Symbol("fn");
  target[fnName] = this;
  Object.defineProperty(target,fnName,{enumerable:false})
  let res;
  eval(`res  = target[fnName](${args.join(",")})`);
  delete target[fnName];
  return res;
};
yxw007 commented 2 years ago

Function.prototype.myCall = function (context) {
    //! 说明:node环境根作用域this 就是globalthis, browser 环境就是window
    if (context) {
        //! 参数:可能不为对象,所以需要利用Object包裹一层
        if (typeof context !== 'object') {
            context = Object(context);
        }
    } else {
        context = globalThis;
    }

    //! 说明:由于第一个参数为context,后面的才为调用函数参数,所以需要slice(1)
    const args = Array.from(arguments).slice(1);

    //! f1 调用的myCall方法, 此时this就是调用的函数本身
    context.fn = this;
    let ret = context.fn(...args);

    //! 说明:不应该改变了this指向,就给调用方法的对象添加一个方法属性,所以调用完后需要删除
    delete context.fn;

    return ret;
}

function f1() {
    console.log("f1, this:", this, ",arguments:", arguments);
}

f1.myCall("hello", "123");
zhangtuo1999 commented 1 year ago
Function.prototype.myCall = function (ctx) {
    ctx ??= globalThis
    ctx = Object(ctx)

    const args = [...arguments].slice(1)

    const key = Symbol('key')
    ctx[key] = this
    const res = ctx[key](...args)
    delete ctx[key]

    return res
}

Function.prototype.myApply = function (ctx) {
    ctx ??= globalThis
    ctx = Object(ctx)

    const args = arguments[1]

    const key = Symbol('key')
    ctx[key] = this
    const res = ctx[key](...args)
    delete ctx[key]

    return res
}
Yeti-xxx commented 1 year ago

实现一个call:

function person(a, b) {
    return {
        name: this.name,
        sum: a + b
    }
}

const yeti = {
    name: 'yeti'
}

// 实现一个call
Function.prototype.newCall = function (obj, ...args) {
    const window = { windwo: 'window' }
    if (!obj) {
        obj = window
    }
    obj.p = this    //此处的this是person函数 相当于在obj中添加了一个person方法
    const resCall = obj.p(...args)  //由于函数可能会返回值,所以将执行的结果保存并返回
    delete obj.p
    return resCall

}

const res = person.newCall(yeti, 1, 2)
console.log(res);
Yeti-xxx commented 1 year ago

实现一个apply:

function person(a, b, c) {
    console.log(Math.max.apply(null, [...arguments]));
    console.log(Math.max.newApplay(null, [...arguments]));
    return {
        name: this.name,
        sum: a + b + c
    }
}

const yeti = {
    name: 'yeti'
}
// 实现一个apply
Function.prototype.newApplay = function (obj, arr) {
    const window = { windwo: 'window' }
    if (!obj) {
        obj = window
    }
    obj.p = this
    if (!arr) { //如果为传入参数数组,直接执行
        const resnewApply = obj.p()
        delete obj.p
        return resnewApply
    } else {
        const resnewApply = obj.p(...arr)
        delete obj.p
        return resnewApply
    }

}

const resApplay = person.newApplay(yeti, [1, 2, 6])
console.log(resApplay);
algok-876 commented 1 year ago

如果contxt中原本就有同名的fn属性呢,调用完你这个call之后,原本的fn属性就消失了 @yxw007