前言:上一篇简单说了this的飘忽不定,有时,需要把this
固定下来,避免出现意想不到的情况。JavaScript 提供了call
、apply
、bind
这三个方法,来切换/固定this
的指向。(出来混总是要还的)。
1、call()
函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
call
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
var n = 123;
var obj = { n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
call
方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call
方法。
var f = function () {
return this;
};
f.call(5)
// Number {[[PrimitiveValue]]: 5}
call
方法还可以接受多个参数。
function add(a, b) {
return a + b;
}
add.call(this, 1, 2) // 3
call
方法的一个应用是调用对象的原生方法。(这也是call()方法实现继承的原因)
var obj = {};
obj.hasOwnProperty('toString') // false
// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
return true;
};
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, 'toString') // false
上面代码中,hasOwnProperty
是obj
对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。call
方法可以解决这个问题,它将hasOwnProperty
方法的原始定义放到obj
对象上执行,这样无论obj
上有没有同名方法,都不会影响结果。
2、apply()
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数。
func.apply(thisValue, [arg1, arg2, ...])//apply()格式
//例子对比
function f(x, y){
console.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) //
(1)找出数组最大元素
JavaScript 不提供找出数组最大元素的函数。结合使用apply
方法和Math.max
方法,就可以返回数组的最大元素。
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 1
(2)将数组的空元素变为undefined
通过apply
方法,利用Array
构造函数将数组的空元素变成undefined
。
Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]
var a = ['a', , 'b'];
function print(i) {
console.log(i);
}
a.forEach(print)
// a
// b
Array.apply(null, a).forEach(print)
// a
// undefined
// b
(3)转换类似数组的对象
另外,利用数组对象的slice
方法,可以将一个类似数组的对象(比如arguments
对象)转为真正的数组。
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
3、bind()
bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var obj = {
count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
bind()
还可以接受更多的参数,将这些参数绑定原函数的参数。
var add = function (x, y) {
return x * this.m + y * this.n;
}
var obj = {
m: 2,
n: 2
};
var newAdd = add.bind(obj, 5);
newAdd(5) // 20
bind()
方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。
element.addEventListener('click', o.m.bind(o));
正确的方法是写成下面这样:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
// ...
element.removeEventListener('click', listener);