手写JavaScript中的call()、apply()和bind()

本文详细介绍了JavaScript中的call、apply和bind方法,包括它们的使用方式、作用及其实现原理。通过示例展示了如何在不同上下文中调用函数并改变this的指向。此外,还提供了这三个方法的手动实现代码,帮助理解其内部工作过程。
摘要由CSDN通过智能技术生成

call()

定义:

call()为函数中的一个方法, 其参数为一个指定的this值,以及一个参数列表

语法:

fn.call(thisArg, a,b,c)
  • thisArg:在fu函数运行时指定this的值,如果指定为null或者undefined会指向全局对象,浏览器中就是window对象。
  • a,b,c:参数列表,作为参数传给fu函数

也就是说call能使fn函数中的this指向其第一个参数thisArg对象

const obj1 = {
    name: 'ahua',
    getName(h1,h2) {
        console.log(`我叫${this.name},喜欢${h1}${h2}`); 
    }
};

const obj2 = {
    name: '阿花'
};
obj1.getName.call(obj2,'读书','看报')
// 我叫阿花,喜欢读书和看报

手写call()

  1. call支持多个参数,可能一个,可能一个也没有
  2. 多参数时要把thisArg外的参数传给扩展方法
  3. 结束后要把扩展的自定义函数删除
Function.prototype.mycall = function(context){
	// context 就是上面call的参数obj2,因为只有一个形参,所以后面两个参数会被忽略
	// 如果没有指定this,this指向window
    var context = context || window;
    // 调用mycall的是obj1对象的getName函数,所以this指向getName函数
    // 这个表达式的意思就是在obj2中定义一个fn去复制getName函数
    context.fn = this;

    var args = [];
    // Javascrip中的函数都有一个Arguments对象实例arguments(类数组),它引用着函数的实参arguments,arguments[num]和其参数一一对应。
    // 将mycall中第一个参数thisArg对象(obj2)移除,剩余的('读书','看报')作为fn的参数
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push(arguments[i]);
  }
  const result = context.fn(...args);
  // 删除fn方法
  delete context.fn;
  return result;
  }
  obj1.getName.mycall(obj2,'读书','看报')
 // 我叫阿花,喜欢读书和看报

apply()

定义:

apply() 为函数的一个方法, 其参数为一个指定的this值,以及一个数组(或类似数组的对象)

语法:

fu.apply(thisArg, [argsArray])
  • thisArg:在fu函数运行时指定this的值,如果指定为null或者undefined会指向全局对象,浏览器中就是window对象。
  • arrgsArray:一个数组或者类数组对象,作为参数传给fu函数

apply也能使函数中的this指向其第一个参数thisArg对象

const obj1 = {
    name: 'ahua',
    getName(x,y) {
        console.log(`我叫${this.name},喜欢${x}${y}`); 
    }
};

const obj2 = {
    name: '阿花'
};
obj1.getName.apply(obj2,['吃饭','睡觉']) 

// 我叫阿花,喜欢吃饭和睡觉

手写apply

apply不同的就是第二个参数变成了数组

Function.prototype.myapply = function(content) {
     // 如果没有传或传的值为空对象 context指向window
     if (typeof context === "undefined" || context === null) {
         context = window
     }
     let args = [...arguments].slice(1) //效果同mycall中的for循环
     // 给obj2添加一个方法fn(复制this) 因为是getName调用的myapply所以this指向getName
     content.fn = this 
     // arguments引用着myapply的两个参数,第一个是obj2,第二个是['吃饭','睡觉']
     const result = content.fn(arguments[1][0],arguments[1][1])//执行fn
     delete content.fn //删除方法
	 return result
 }  obj1.getName.myapply(obj2,['吃饭','睡觉'])
 // 我叫阿花,喜欢吃饭和睡觉

bind()

用法和call类似,不过bind()会创建一个新的函数,需要再次进行调用

function a(x, y, z){
	return this.name + '喜欢' + x + '、' + y + '和' + z;
}
 
var b = {name : '阿花'};
a.getName.bind(b,'读书','看报')('滑板')
// 阿花喜欢读书、看报和滑板	

手写bind()

  1. 函数调用
  2. 改变this
  3. 返回一个绑定this的函数
  4. 接收多个参数
  5. 支持柯里化形式传参 fn(1)(2)(3)

代码实现

Function.prototype.mybind = function(context){
     if (typeof context === "undefined" || context === null) {
         context = window
     }
     // a.mybind(b,参数1,参数2)(参数3)  context是b args是通过arguments获取参数1和参数2  return函数中的参数是参数3
	var args = [...arguments].slice(1);
    // args['读书','看报']
   
    // 保存this,因为如果后面进入return函数后,this的指向会改变
	var self = this;

    // 调用bind后返回一个this指向第一个参数(b)的函数
	return function(){
        // mybind(b, '读书', '看报')是调用自定义的mybind方法,
        // a.mybind(b, '读书', '看报')('滑板')是继续调用的mybind中return的函数,所以return中的函数参数为('滑板')
        // 将类数组转换为数组  innerArgs ['滑板']
		var innerArgs = [...arguments];
        console.log(arguments)

        // 实现函数curry:把接受多个参数的函数变换成接受一个单一参数的函数
        // 即:将bind(thisArg,参数1,参数2)(参数3) 中的参数1,参数2,参数3合并为一个参数作为apply的第二个参数
		var finalArgs = args.concat(innerArgs);
		//finalArgs ['读书','看报' ,'滑板']

        // 用apply是因为传入的第二个参数为数组
		return self.apply(context, finalArgs);
	}
}
// 测试结果
function a(x, y, z){
	return this.name + '喜欢' + x + '、' + y + '和' + z;
}
 
var b = {name : '阿花'};
 
console.log(a.mybind(b, '读书', '看报')('滑板'));
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值