call和apply的模拟实现

本文深入解析JavaScript中call与apply方法的工作原理,通过逐步分析和代码示例,展示如何从零开始模拟实现这两个核心函数,帮助读者理解它们在改变函数调用上下文中的关键作用。

call和apply都可以改变this的指向

请看下面函数的执行
var name = "hello"
var foo = {
	name: "wucr"
};
function getName(){
	console.log(this.name)
}
getName() //返回值hello
getName.call(foo) // 返回值 wucr
那么如何模拟实现一个call函数呢,请看下面分析
var foo = {
	name: "wucr",
	getName:function(){
	   console.log(this.name)
    }
}
foo.getName() // wucr

//分析以上的代码,我们可以给对象自己添加一个方法,然后去调用,但是这样就会有一个问题就是都要在对象里面去新增方法,但是我们用 delete 再删除它不就好了~,所以我们的模拟步骤可以是下面三步
1,将函数设为对象的属性
2,执行该函数
3,删除该函数

call函数第一版抹模拟实现
Function.prototype.call1 = function(context){
  context.fn = this; // 这里的this指代调用的函数
   context.fn();
   delete context.fn
}
var name = "hello"
var foo = {
	name: "wucr"
};
function getName(){
	console.log(this.name)
}
getName.call1(foo) // 返回值 wucr

我们都知道,call里面可以传递很多参数,call(context,arguments1,arguments2…),所以以上的实现不能满足,参数不固定,但是我们可以使用arguments类数组来获取参数

call函数第二版模拟实现
Function.prototype.call2 = function(context){
  var args = [];
  for(var i =1; i < arguments.length; i++){
      args.push(arguments[i])
   }
  context.fn = this; // 这里的this指代调用的函数
   context.fn(...args);
   delete context.fn
}

上面第二版没有返回值和当我们不传递context时的判断

call函数终极版模拟实现
Function.prototype.call2 = function(context){
  var context = context || window
  var args = [];
  for(var i =1; i < arguments.length; i++){
      args.push(arguments[i])
   }
   context.fn = this; // 这里的this指代调用的函数
   var result = context.fn(...args);
   delete context.fn;
   return result;
}

结合call的模拟实现apply的实现就很简单了

apply函数终极版模拟实现
Function.prototype.apply = function(context,arr){
	var context = context || window;
	context.fn = this;
	if(!arr){
		var result = context.fn()
	}else{
		var  args = [];
		 for(var i =0; i < arr.length; i++){
		      args.push(arr[i])
		   }
       var  result = eval('context.fn(' + args + ')') // 这里也可以   var result = context.fn(...args);但是这语法是es6

	}
	return result;
}
JavaScript 中的 `Function.prototype.call` `Function.prototype.apply` 方法都用于调用函数,并允许显式指定函数执行时的 `this` 上下文。它们的主要区别在于参数传递的方式不同。 ### 参数传递方式 - `call` 方法接受一个 `this` 上下文对象后,接着是多个参数列表,这些参数将直接作为函数的参数传递。例如: ```javascript function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; } const person = { name: 'Alice' }; greet.call(person, 'Hello', '!'); // "Hello, Alice!" ``` 在这个例子中,`'Hello'` `'!'` 是作为独立的参数传递给 `greet` 函数的[^4]。 - `apply` 方法同样接受一个 `this` 上下文对象,但它接受的第二个参数是一个数组(或类数组对象),该数组中的元素会被展开作为函数的参数。例如: ```javascript function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; } const person = { name: 'Alice' }; greet.apply(person, ['Hello', '!']); // "Hello, Alice!" ``` 这里,`['Hello', '!']` 数组中的元素被依次作为 `greet` 函数的参数传递[^2]。 ### 使用场景 - 当你已经有一个参数列表(例如,函数参数已经以变量形式存在)并且需要将它们直接传递给另一个函数时,`call` 更加方便。 - 当你需要传递的参数是以数组形式存在的时候,`apply` 就显得更加合适。此外,`apply` 还可以用于将数组作为参数传递给那些期望多个独立参数的函数,比如 `Math.max` 或 `Array.prototype.push`。 ### 实现原理 两者都可以通过修改函数内部的 `this` 指向来实现对函数执行上下文的切换。`call` `apply` 的核心思想都是将函数作为对象的方法来调用,从而改变函数内部 `this` 的指向。具体实现上,`call` 是将参数逐一传递,而 `apply` 则是通过数组来传递参数[^3]。 ```javascript // 模拟 call 方法 Function.prototype.myCall = function(context) { context = context || window; const args = Array.from(arguments).slice(1); const fn = Symbol(); context[fn] = this; const result = context[fn](...args); delete context[fn]; return result; }; // 模拟 apply 方法 Function.prototype.myApply = function(context, args) { context = context || window; const fn = Symbol(); context[fn] = this; const result = context[fn](...(args || [])); delete context[fn]; return result; }; ``` 以上代码展示了如何模拟 `call` `apply` 方法的基本功能,其中 `myCall` `myApply` 都会临时将函数赋值给传入的对象的一个唯一属性上,然后调用该属性,最后删除这个属性以避免污染传入的对象。 ### 总结 尽管 `call` `apply` 都可以用来改变函数执行时的 `this` 值,但是它们在参数传递上的差异决定了它们各自适用的场景。理解这些差异有助于更好地利用 JavaScript 的特性来解决实际问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值