Javascript-模拟call和apply方法

call

功能

模拟前首先搞清楚call方法的功能:

  1. 改变调用函数的this指向
  2. 可传入多个参数:func.call(context, arg1, arg2…)
  3. 立即执行调用函数

不传参

function Fn() {
   console.log(this.name);
   this.name = "fan01"
   console.log(this.name);
}
const o = {
   name:"fan02"
}
Fn.call(o)
// fan02
// fan01

传参

var number = 2;
var obj ={
  number:1
}
function Fn(name,age) {
  console.log(this.number,name,age);
}
Fn.call(obj,"fan","18")
// 1 'fan' '18'

模拟

call方法 与 将函数当作对象的方法来调用 十分相似(如下)

// 测试一下
var number = 2;

var obj = {
    number: 1,
    test: function () {
        console.log(this.number);
    }
}

obj.test(); // 1

这里的this就指向了obj。

通过上面这个例子,我们可以将调用函数当做上下文对象的方法调用,那么this便会指向上下文对象:

Function.prototype.call2 = function (context){
  // 获取调用的函数,这里的this指向调用 call方法的函数
  var self = this;
  //将调用的函数当作上下文对象的属性
  context.fn = self;
  //执行调用函数
  context.fn();
  //执行后删除context上的该函数属性(fn()) ,因为其是函数新增的一个属性 
  //并不是原先上下文中的原始属性,所以需要删除
  delete context.fn
}
// 测试
var number = 2;
var obj = {
  number:1
}
function a(){
  //注意这里输出 number 与 this.number结果不同 
  console.log(this.number);
}
a.call2(obj)
// 1

接下来实现传入参数:

 Function.prototype.call2 = function (context,...args){
      // 获取调用的函数,这里的this指向调用 call方法的函数
      var self = this;
      //将调用的函数当作上下文对象的属性
      context.fn = self;
      //执行调用函数(这里传入了参数)
      context.fn(...args);
      //执行后删除context上的该函数属性(fn()) ,因为其是函数新增的一个属性 并不是原先上下文中的原始属性,所以需要删除
      delete context.fn
    }
    // 测试
    var number = 2;
    var obj = {
      number:1
    }
    function a(name,age){
      //注意这里输出 number 与 this.number结果不同 
      console.log(this.number,name,age);
    }
    a.call2(obj,"fan","12")
    // 1 'fan' '12'

完善call2方法

  1. 调用函数的需要返回值
  2. 传入的上下文不是引用类型
  3. 传入的上下文为null或者undefined,this会指向window
Function.prototype.call2 = function (context,...args){
  //对传入的上下文进行包装
  context = new Object(context|| window);
  console.log(context);
  // 获取调用的函数,这里的this指向调用 call方法的函数
  var self = this;
  
  //将调用的函数当作上下文对象的属性
  context.fn = self;
  //打印对象新结构(用于传入的上下文不是引用类型时)
  console.log(context);
  //执行调用函数
  var res = context.fn(...args);
  //执行后删除context上的该函数属性(fn()) ,因为其是函数新增的一个属性 并不是原先上下文中的原始属性,所以需要删除
  delete context.fn

  return res;
}
// 测试
var number = 2;
var obj = {
  number:1
}
function a(name,age){
  return {
    number: this.number,
    name,
    age
  };
}
console.log(a.call2(obj, '张三', 12)); //  {"number":1,"name":"张三","age":12}
console.log(a.call2(null, '张三', 12)); //  {"number":2,"name":"张三","age":12}
console.log(a.call2(undefined, '张三', 12)); //  {"number":2,"name":"张三","age":12}
//传入上下文不是引用类型 this为underfined
console.log(a.call2(1, '张三', 12)); //  {"number":undefined,"name":"张三","age":12}
// Number {1, fn: ƒ}  Number 对象内没有number
console.log(a.call2('1', '张三', 12)); //  {"number":undefined,"name":"张三","age":12}

apply

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数 我们将方法一修改一下

Function.prototype.apply2  = function (context,args){
  //对传入的上下文进行包装
  context = new Object(context|| window);
  console.log(context);
  // 获取调用的函数,这里的this指向调用 call方法的函数
  var self = this;
  
  //将调用的函数当作上下文对象的属性
  context.fn = self;
  //打印对象新结构(用于传入的上下文不是引用类型时)
  console.log(context);
  //执行调用函数
  var res = context.fn(...args);
  //执行后删除context上的该函数属性(fn()) ,因为其是函数新增的一个属性 并不是原先上下文中的原始属性,所以需要删除
  delete context.fn

  return res;
}
// 测试
var number = 2;
var obj = {
  number:1
}
function a(name,age){
  return {
    number: this.number,
    name,
    age
  };
}
console.log(a.apply2(obj, ['张三', 12])); //  {"number":1,"name":"张三","age":12}
console.log(a.apply2(null, ['张三', 12])); //  {"number":2,"name":"张三","age":12}
console.log(a.apply2(undefined, ['张三', 12])); //  {"number":2,"name":"张三","age":12}
//传入上下文不是引用类型 this为underfined
console.log(a.apply2(1, ['张三', 12])); //  {"number":undefined,"name":"张三","age":12}
// Number {1, fn: ƒ}  Number 对象内没有number
console.log(a.apply2('1', ['张三', 12])); //  {"number":undefined,"name":"张三","age":12}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一缕阳光@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值