美文网首页
深入之call、apply的模拟实现

深入之call、apply的模拟实现

作者: 明里人 | 来源:发表于2019-07-12 20:33 被阅读0次
call

call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法

例子:

let foo = {
  value: 1
};
function bar() {
  console.log(this.value);
}
bar.call(foo);  // 1

注意两点:
1、call 改变了this的指向,指到了foo
2、bar 函数执行了

模拟实现第一步

试想当调用 call 的时候,把foo 对象改造成如下:

let foo = {
  value: 1,
  bar: function() {
    console.log(this.value);
  }
}
foo.bar();  // 1

这个时候this就指向了 foo,
但却给foo对象增加了一个属性,
我们使用delete再删除掉它不就好了
所以模拟的步骤可以分为:

1、将函数设为对象的属性
2、执行该函数
3、删除该函数

// 第一版
Function.prototype.call2 = function(context){
  // 首先获取调用call的函数,用this可以获取
  context.fn = this;
  context.fn();
  delete context.fn;
}

// 测试一下
let foo = {
  value: 1
};
function bar(){
  console.log(this.value);
}
bar.call2(foo);  // 1
模拟实现第二步

call 函数给定参数执行函数,但传入参数并不确定, 我们可以从 Arguments 对象中取值,取第二个到最后一个值,放到一个数组里

// 第二版
Function.prototype.call2 = function(context){
    context.fn = this;
    let args = [];
    for(var i = 1; i < arguments.length; i ++){
        args.push(arguments[i]);
    }
    context.fn(... args); // ES6解构数组
    delete context.fn;
}

// 测试一下
let foo = {
    value: 1
};
function bar(name,age){
    console.log(name);  // cyl
    console.log(age);  // 18
    console.log(this.value);  // 1
}
bar.call2(foo,'cyl',18);
模拟实现第三步

有两小点需要注意:
1、this参数可以传 null,当为 null 时,视为指向 window
2、函数可以有返回值的

// 第三版
Function.prototype.call2 = function(context){
  var context = context || window;
  context.fn = this;

  let args = [];
  for(var i = 1,len = arguments.length; i < len; i ++){
    args.push(arguments[i]);
  }
  
  let result = context.fn(... args);
  
  delete context.fn;
  return result;

}

// 测试一下
// 注意全局这里使用let声明时,通过window获取value为undefined,let声明不属于window
var value = 2;
let obj = {
    value: 1
}
function bar(name,age){
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call2(null);  // 2
console.log(bar.call2(obj,'cyl',18));
// 1
// Object {
//      value: 1,
//      name: cyl,
//      age: 18
// }
apply

apply 的实现跟 call 类似,区别在于call是序列化传参,apply传参时传递的是一个数组

Function.prototype.apply2 = function(context,arr){
  var context = Object(context) || window;
  context.fn = this;
  
  var result;
  if(!arr){
    result = context.fn();
  }else{
    result = context.fn(... arr);  // apply第二参数为数组,无需使用arguments
  }

  delete context.fn;
  return result;
}

相关文章

网友评论

      本文标题:深入之call、apply的模拟实现

      本文链接:https://www.haomeiwen.com/subject/fioykctx.html