# call apply bind
# call
call 也就是 Function.prototype.call() ,是函数类型特有的原型方法,可以传入一个指定的this和其他参数:
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);
// expected output: "cheese"
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
函数中this所指始终是调用函数的对象,如果不使用call:
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product(name, price);
this.category = "food";
}
console.log(new Food("cheese", 5).name);
// expected output: "undefined"
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Food 函数中调用Product函数,Product函数中的this就会指向window而不是调用Food 的对象,所以通过它生成的实例中也就是没有相应的属性。
可以看到通过调用call,改变了函数中this的指向。
# apply
apply 和 call 非常相似,不同之处在于提供参数的方式,apply使用参数数组而不是一组参数列表。
# bind
bind 函数会创建一个新函数(绑定函数),新函数和被调函数(绑定函数的目标函数)具有相同的函数体。当新函数被调用时this 绑定到bind 的第一个参数:
this.x = 9;
var module = {
x: 81,
getX: function() {
return this.x;
}
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bind 一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为 bind()的第二个参数跟在 this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面:
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例:
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log("I am a beautiful flower with " + this.petalCount + " petals!");
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 代码实现
// bind
if (!Function.prototype.bind) {
Function.prototype.bind = function(context) {
if (typeof this !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
// 第一个参数是 context 获取第二个及以后的参数
var args = Array.prototype.slice.call(arguments, 1),
self = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明被当做构造函数调用
return self.apply(
this instanceof fBound ? this : context,
// 绑定完成后还可以继续补充参数
args.concat(Array.prototype.slice.call(arguments))
);
};
// 通过添加中间函数 fNOP 利用原型链继承
// 使得修改 fBound.prototype 不会影响绑定函数原型
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
// call
Function.prototype.call = function(ctx) {
// 非严格模式下
// 如果第一个参数为null或者undefined,则指向全局对象
// ---
// 严格模式下
// 如果是null则this为null,如果是undefined则this为undefined
let context = ctx || window;
context.fn = this; // 待执行函数
// 参数是类数组对象没有slice方法
// 需要先转换成数组
let args = [];
for (let i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
// es6
// let args = [...arguments].slice(1);
context.fn(...args);
delete context.fn;
};
// apply
Function.prototype.apply = function(ctx) {
// 非严格模式下
// 如果第一个参数为null或者undefined,则指向全局对象
// ---
// 严格模式下
// 如果是null则this为null,如果是undefined则this为undefined
let context = ctx || window;
context.fn = this; // 待执行函数
let args = [...arguments][1];
if (args) return context.fn(...args);
else return context.fn();
delete context.fn;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71