Skip to content

模拟实现call和apply方法 #14

@lfb

Description

@lfb

call和apply的用法

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

function foo(name, age) {
    console.log(this.value);
    console.log(name, age);
}

var obj = {
    value: 1
}

foo.call(obj, 'bobo', 18)
foo.apply(obj, ['bobo', 18])

call方法模拟实现思想

调用对象的方法this会指向当前的对象,比如

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

obj.getValue() // 1

模拟实现call()方法目的就是把this绑定一个对象中,再执行对象的方法。
步骤分为:

  • 将函数设为对象的属性
  • 执行该函数
  • 删除该函数
Function.prototype.myCall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  // this 参数可以传 null,当为 null 的时候,视为指向 window
  var context = context || window;
  context.fn = this;

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

  delete context.fn;
  return result;
}

测试:

function foo(name) {
    console.log(this.value);
    console.log(name);
}

var value = 1;
var obj = {
    value: 2
}

foo.myCall(obj, 'bobo'); // 2

apply模拟实现

apply和call实现原理差不多,只是处理参数不一样:

Function.prototype.myApply = function (context, arr) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var context = context || window;
  context.fn = this;

  var result;
  if (!arr) {
    result = context.fn();

  } else {
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {
      args.push('arr[' + i + ']');
    }
    result = eval('context.fn(' + args + ')');
  }

  delete context.fn;
  return result;
}

测试:

function foo(name) {
    console.log(this.value);
    console.log(name); // bobo
}

var value = 1;
var obj = {
    value: 2
}

foo.myApply(obj, ['bobo']); // 2

ES6语法实现

@作者:yck

// call模拟实现
Function.prototype.myCall = function(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  const args = [...arguments].slice(1)
  const result = context.fn(...args)
  delete context.fn
  return result
}

// apply模拟实现
Function.prototype.myApply = function(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  let result
  // 处理参数和 call 有区别
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions