JS中的函数也是一种对象,但是并不是所有的对象都是函数。函数中拥有 call、apply、bind三个原型方法。call()
、apply()
可以看作为某个对象的方法,通过调用方法的形式来间接调用函数。
如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文, 也就是该函数的this的值。
每一个函数都包含一个prototype属性,这个属性是指向一个对象的引用,这个对象称作“原型对象”。 每一个函数都包含不同的原型对象。当将函数用作构造函数的时候,新创建的对象会从原型对象上继承属性。
bind
将函数绑定到某个对象,将返回一个新的对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
// 此时 this 的指向是 module
module.getX(); // 返回 81
// 在这种情况下,"this"指向全局作用域
var retrieveX = module.getX;
retrieveX(); // 返回 9,
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
再来看一个例子:
1 | var Foo = { |
那么进行多次 bind() 呢?
1 | var bar = function() { |
以上输出什么?答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。
其实真的没什么大不了的,看看 bind 的实现就明白了:
1 | Function.prototype.bind = Function.prototype.bind || function(context){ |
call 与 apply
在 JavaScript 中 call
、apply
都是为了改变函数运行时的上下文而存在的,也就是说为了改变函数体内部 this
的指向。其语法:fun.call(thisArg[, arg1[, arg2[, ...]]])
。可以让 call()
中的对象调用当前对象所拥有的function,可以用这种方法来实现继承:
1 | function Foo() {} |
call
与 apply
的功能是一样的,唯一的不同是接收参数方式不一样:1
2
3var func = function() {};
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
在func
函数运行时指定 this
。该 this
并不是真正的 this 值,在严格模式下为全局对象(在浏览器中为window)。非严格模式下为 undefined
。
三者区别
apply、call、bind比较:
1 | var obj = { x: 10 }; |
三个输出的都是10,但是注意看使用 bind() 方法的,他后面多了对括号。
也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
再总结一下:
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
参考
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://segmentfault.com/a/1190000000375138?page=1
http://web.jobbole.com/83642/
http://yanhaijing.com/basejs/index.html#sect_var_scope_closures