JavaScript高级手写apply()call()bind()

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级手写apply()call()bind()相关的知识,希望对你有一定的参考价值。

文章目录

手写之前

我们有一个函数foo。
已知:

  • foo的隐式原型是绑定在Function的显式原型上的(Function是一个构造函数)
function foo()
console.log(foo.__proto__===Function.prototype);//true
  • apply、call、bind方法是来自Function的原型对象
console.log(Function.prototype.apply);//ƒ apply()  [native code] 
  • 也就是说:在Function的原型对象上添加的属性或方法, 可以被所有的函数获取

apply

获取thisArg, 我们要确保是一个对象类型,要进行边界判断

Function.prototype.MyApply = function(thisArg)
   thisArg=(thisArg===undefined||thisArg===null)?window:Object(thisArg);
;

将this绑定到传入的参数thisArg上

我们想实现apply显式绑定,就只能用:默认绑定、隐式绑定、new绑定来实现。(总不能自己实现自己)。

默认绑定肯定不是我们要的,因为显式绑定并不希望帮到window上。
隐式绑定可以。
new绑定要用到构造函数,略显复杂。

所以我们用隐式绑定

Function.prototype.MyApply = function(thisArg)
    thisArg=(thisArg===undefined||thisArg===null)?window:Object(thisArg);

    //这步
    //当读取thisArg.fn时返回this,相当于thisArg.fn=this
    //这里创建一个属性fn,它的值为this
    Object.defineProperty(thisArg,"fn",
        configurable:true,//true,否则没法delete
        value:this,
    );

    //隐式绑定:让this指向thisArg
    thisArg.fn();

    //完成后删除thisArg中的fn属性
    delete thisArg.fn;
;

将剩余的其他参数传入thisArg

apply传入的其他参数是一个数组。

function foo(name, age) 
  console.log(this, name, age); // name: 'kaisa' 'kaisa' 18


Function.prototype.myapply = function (thisArg, arrArgs) 
  
  thisArg =thisArg === undefined || thisArg === null ? window : Object(thisArg);

  Object.defineProperty(thisArg, "fn", 
    configurable: true,
    value: this,
  );

  // 通过展开运算符, 将其他参数传入thisArg
  thisArg.fn(...arrArgs);

  delete thisArg.fn;
;

foo.myapply( name: "kaisa" , ["kaisa", 18]);

call

call传入的其他参数是参数列表

function foo(name, age) 
    console.log(this, name, age);


Function.prototype.MyCall = function (thisArg,...arrArgs) 
    thisArg = (thisArg === undefined || thisArg === null) ? window : Object(thisArg);

    Object.defineProperty(thisArg, "fn", 
        configurable: true,
        value: this,
    );
    
    thisArg.fn(...arrArgs);

    delete thisArg.fn;
;

foo.MyCall(name:"abc","abc", 18);

封装函数实现apply和call

显然,apply和call的函数有重复的代码。因此,我们可以把重复代码封装成一个函数。

function repFn(thisArg,otherArgs,fn)
    thisArg= (thisArg===null||thisArg===undefined)?window:Object(thisArg);

    Object.defineProperty(thisArg,"fn",
        configurable: true,
        value: fn,
    )

    thisArg.fn(...otherArgs);

    delete thisArg.fn;


//apply
Function.prototype.MyApply=function(thisArg,otherArgs)
    repFn(thisArg,otherArgs,this);


//call
Function.prototype.MyCall=function(thisArg,...otherArgs)
    repFn(thisArg,otherArgs,this);

测试:

//测试
function foo1(name,age)
    console.log(this,name,age);


function foo2(name,age)
    console.log(this,name,age);


foo1.MyApply(name:"apply",["apply",19]) //name: 'apply', fn: ƒ 'apply' 19
foo2.MyCall(name:"call","call",20)//name: 'call', fn: ƒ 'call' 20

bind

bind函数会返回一个新的函数,返回的新函数中可以传参数。

function foo(name,age,height)
    console.log(this,name,age,height);


Function.prototype.MyBind=function(thisArg,...otherArgs)
    thisArg= thisArg===null||thisArg===undefined?window:Object(thisArg);

    Object.defineProperty(thisArg,"fn",
        configurable:true,
        value:this
    )

    return (...newArgs) =>
        var allArgs=[...otherArgs,...newArgs];
        thisArg.fn(...allArgs);
    ;
;

var newFoo=foo.MyBind(name:"bind","bind");
newFoo(18,1.88);//name: 'bind', fn: ƒ 'bind' 18 1.88

参考

coderwhy的课
手写apply-call-bind
手写 实现call、apply和bind方法 超详细!!!
使用JS简单实现一下apply、call和bind方法
Function

以上是关于JavaScript高级手写apply()call()bind()的主要内容,如果未能解决你的问题,请参考以下文章

Javascript手写call, apply, bind

前端面试题 ---- 手撕JavaScript call apply bind 函数(超详细)

无敌秘籍之 — JavaScript手写代码

《JavaScript高级程序设计》里对 call() 和 apply() 的解释 (116页)

学习JavaScript之this,call,apply

源码来袭:callapply手写实现与应用