this的动态切换虽然为js创造了巨大的灵活性,也使编程变得困难和模糊
。利用call、apply、bind这三个方法,可以改变this的指向,使它指向我们期望的对象。
call
var f = function() {};
f.call(o);
在全局环境运行函数时,this指向全局环境,利用call方法,将this指向了对象o,在o的作用域中运行函数f。
var n = 123;
var o ={n: 234};
function a() {console.log(this.n)};
a.call() //123
a.call(null) //123
a.call(undefined) //123
a.call(window) //123
a.call(o) //234
可以看到,如果call方法没有参数,或者参数为null或undefined,则等同于指向全局对象
call方法的完整使用格式:
func.call(thisValue, arg1, arg2, ...)
第一个参数是this要指向的那个对象,后面的参数是调用时所需要的参数
function add(a,b) {
return a+b;
}
add.call(this, 1, 2) //3
call方法的一个应用是调用对象的原生方法
var obj = {};
obj.hasOwnProperty('toString') //false
obj.hasOwnProperty = function() {return true;}
obj.hasOwnProperty('toString') //true
Object.prototype.hasOwnProperty.call(obj, 'toString')
上述代码中,hasOwnProperty是obj对象继承的方法,一旦被覆盖重新定义,就得不到正确结果,利用call方法,将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论Obj上有没有同名方法,都不会影响结果。
apply
apply方法与call方法类似,使用格式如下:
func.apply(thisValue, [arg1, arg2, ...])
apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。
一些有趣的应用
1)找出数组最大元素
var a = [12, 2, 45, 4, 9];
Math.max.apply(null, a); //45
2)将数组的空元素变为undefined
Array.apply(null, ["a",,"b"]) // ['a', undefined, 'b']
空元素和undefined的差别在于,数组的foreach方法会跳过空元素,但是不会跳过undefined,因此遍历内部元素的时候,会得到不同的结果
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)转换类似数组的对象
Array.prototype.slice.apply({0:1,length:1})
// [1]
Array.prototype.slice.apply({0:1})
// []
Array.prototype.slice.apply({0:1,lengt:2})
// [1, undefined]
Array.prototype.slice.apply({length:1})
// []
利用数组的slice方法,可以将一个类似数组的对象,比如上面代码的apply方法的参数都是对象,但是反悔结果都是数组,这就起到了将对象转成数组的目的。
这个方法起作用的前提是,被处理的对象必须有length属性,以及相对应的数字键
4)绑定回调函数的对象
这个采用bind方法更好
bind
var o1 = new Object();
o1.p = 123;
o1.m = function() {console.log(this.p);};
o1.m(); //123
var o2 = new Object();
o2.p = 345;
o2.m = o1.m;
o2.m(); //345
o2.m = o1.m.bind(o1);
o2.m() //123
bind方法除了绑定this以外,还可以绑定原函数的参数
var add = function (x,y) {
return x*this.m + y*this.n;
}
var obj = {
m: 2,
n: 2
};
var newAdd = add.bind(obj, 5);//add()函数的第一次参数x绑定为5,如果给了两个参数,则同时给定了x、y的值
newAdd(5); //20
如果bind方法的第一个参数是null或undefined,等于将this绑定到全局
bind方法的使用注意点:
1)每一次返回一个新函数
element.addEventListener('click', o.m.bind(o));
click事件绑定到了o.m使用bind方法生成的一个匿名函数上,这样会导致无法取消。因此下面代码是无效的。
element.removeEventListener('click', o.m.bind(o));
正确的写法是下面这样:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
//...
element.removeEventListener('click', listener);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。