call、apply以及bind

call、applay、bind是改变函数上下文this的常用方式,下面将对其使用及实现方式进行分析。

call

函数解析

call为Function.prototype.call,call方法的参数由一个制定的this和一个或多个参数来调用,需要注意的是call方法接受的参数是参数列表。MDN官方示例如下:

1
2
3
4
5
6
7
8
9
10
11
function Product(name, price) {
this.name = name;
this.price = price;
}

function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}

console.log(new Food('cheese', 5).name);

函数实现

1
2
3
4
5
6
7
8
Function.prototype.myCall = function(context) {
context = context || window;
context.fn = this;
var args = [...arguments].slice(1)
var result = context.fn(...args);
delete context.fn;
return result;
}
  • context为可选参数,传null则为windows
  • 为context创建一个fn属性并指向this
  • 因为call可以传多个参数进来,所以需要把参数剥离

apply

函数解析

Function.prototype.apply:apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

1
2
3
4
5
6
7
8
9
var numbers = [5, 6, 2, 3, 7];

var max = Math.max.apply(null, numbers);

console.log(max);

var min = Math.min.apply(null, numbers);

console.log(min);

函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myApply = function() {
context = context || window;
context.fn = this;
var result = null;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}

delete context.fn;
return result;
}

apply的实现和call的实现类似,唯一的区别在于对参数的处理上。

bind

函数解析

用Function.prototype.bind方法创建一个新的函数,在bind()被调用是,这个函数的this被bind的第一个参数指定。MDN实例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var module = {
x: 42,
getX: function() {
return this.x;
}
}

var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

函数实现

对于bind函数有两个特点,返回一个函数,可以传入参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.myBind = function(context) {
var _ = this;

var args = Array.prototype.slice.call(arguments, 1);
var fNop = function() {};
var fBound = function() {
var bindArgs = Array.prototype.slice.call(arguments);
_.apply(this instanceof _ ? this: context, args.concat(bindArgs));
}

fNop.prototype = this.prototype;
fBound.prototype = new fNop();
return fBound;
}