2

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);

bottle_
259 声望22 粉丝

好好学习,好好生活,好好工作


引用和评论

1 篇内容引用
0 条评论