apply、call、bind的区别,以及手写apply、call、bind

callapplybind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向

有关this指向以及函数上下文、作用域的问题后面我会写文章总结(我也是在准备前端面试,一点一点学习这些东西)。

apply

apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入。

改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

call

call方法的第一个参数也是this的指向,后面传入的是一个参数列表

apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

由以上例子可以看出,apply与call的不同点只是接收的参数不一样

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}
 
fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
console.log("/")
fn.apply(obj,[2,4]);
console.log("/")
fn(1,2) // this指向window

 通过借鉴

dreamer_sen 的文章 我学习到 有一个比较特殊的是apply函数还可以接收arguments对象

sum = 1;

function getSum(num1, num2) {


  this.sum = this.sum + num1 + num2;
  console.log(this.sum);
}

function myGetSum() {
  console.log(arguments); // [Arguments] { '0': 99, '1': 88 }
  getSum.apply({ sum: 100 }, arguments);
}
getSum(1, 2);
myGetSum(99, 88);

————————————————
版权声明:本文为CSDN博主「dreamer_sen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dreamer_sen/article/details/112172575

bind

bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)

改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}
 
const bindFn = fn.bind(obj); // this 也会变成传入的obj,bind函数最后是返回绑定this之后的函数需要用一个参数接收
//可以分多次传入
bindFn(1,2) // this指向obj
fn(1,2) // this指向window

总结:

三者都可以改变函数的this对象指向

三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window

三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入

bind是返回绑定this之后的函数,apply、call 则是立即执行

手写call:

Function.prototype.myCall = function(thisArg,...args) {
    //判断调用myCall的对象类型。谁调用myCall,本函数内的this值就是谁。
    if(typeof this !== "function") throw new TypeError('Not a Function')
 
    //判断参数指定的this的类型,如果是null或undefined的话会自动转为全局变量。
    //浏览器环境下全局变量是window node环境下全局变量是global
    if(thisArg === undefined || thisArg === null){
         //node环境下window是undefined
        thisArg = typeof window === 'undefined'? global:window;
    }
 
    //包装一下
    thisArg = Object(thisArg);
 
    let key = Symbol('fn');
    //在指定的thisArg上添加方法,使用symbol可以避免覆盖了thisArg的同名方法/属性。
    thisArg[key] = this;
    //先将结果保存起来
    let result = thisArg[key](...args);
    //删除在thisArg上添加的方法,回到最初的样子
    delete thisArg[key];
    return result;
} 
 
//测试代码
function foo() { }
function bar(x,y) {
    console.log(this instanceof Number)
    console.log(x+y);
}
 
bar.myCall(foo,3,1);//false 4 
bar.myCall(123,3,1);//true 4 

手写apply:

Function.prototype.myApply = function (thisArg,args) {
    if(typeof this !== 'function') throw new Error('Not a Function')
 
    if(thisArg ===null || thisArg === undefined) {
        thisArg = typeof window === 'undefined'? global:window;
    }
    thisArg = Object(thisArg);
    let  key = Symbol('fn');
    thisArg[key] = this;
    let result = thisArg[key](...args);
    delete thisArg[key];
    return result;
 
}
 
//测试代码
function foo() { }
function bar(x,y) {
 
    console.log(x+y);
}
 
bar.myApply(foo,[3,1]);

手写bind:

Function.prototype.bind2 = function(asThis,...args) {
    //这里的this就是调用bind2的函数(普通函数this指向调用者)
    if (typeof this !== "function") {
        throw new TypeError("Not a Function");
    }
    //  这里保存调用bind的函数以及传给bind后面的参数。
    var origin = this;
    let  args0 = args;
 
    function bound(...args) {
        if(this instanceof bound) 
            //如果是new的形式来使用绑定函数的
            return new origin( ...args0,...args)
        else 
            //如果是普通函数的形式来使用绑定函数的(基本上与call、apply无区别,实现柯里化即可)
            return origin.call(asThis, ...args0,...args);
      }
      bound.prototype = origin.prototype;
      return bound;
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

画大饼之王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值