首先,这三个都是可以用来改变this的,但是我们首先要解决一个问题什么是this,什么是调用模式。调用模式在上一篇文章有讲到。
call
首先call和apply一样,是上下文调用的两个通用方法。
平时调用call的时候,要用老手写法,也就是
function add(x,y){
return x+y
}
add.call(undefined,1,2)
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
因此上面的this绑定的就是window,它也被称为隐性绑定。**
如果你希望打印出你想要的this,可以修改手动修改,显性地绑定this为你想要的东西。
第一个永远要把this写出来,后面再写其他变量。new一个函数时,背地里会将创建一个连接到prototype成员的新对象,同时this会被绑定到那个新对象上。
apply
apply和call第一个参数一样:表示使用那个对象来调用函数;apply第二个参数是:是一个数组或伪数组,数组的值做为函数的参数被传入;call第二个参数是:是基本数据类型(number string boolean);eg:
若要求一个数组中的最大值
使用上下文调用模式(apply)
let arr = [9, 1, 4, 101, 7, 22, 8];
let maxNum=Math.max.apply(window,arr);
//Math.max是window中的排序方法我们可以通过apply借用window中Math.max方法来对数组进行排序。
console.log(maxNum);
事实上apply 和 call 的用法几乎相同, 唯一的差别在于:当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。
eg:
let obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.apply(obj, ['Dot', 'Dolby'])// My name is: Dot Dolby
可以看到,obj 是作为函数上下文的对象,函数 getName 中 this 指向了 obj 这个对象。参数 firstName 和 lastName 是放在数组中传入 getName 函数。
call和apply可用来借用别的对象的方法,这里以call()为例:
let Person1 = function () {
this.name = 'Dot';
}
let Person2 = function () {
this.getname = function () {
console.log(this.name);
}
Person1.call(this);
}
let person = new Person2();
person.getname(); // Dot
从上面我们看到,Person2 实例化出来的对象 person 通过 getname 方法拿到了 Person1 中的 name。因为在 Person2 中,Person1.call(this) 的作用就是使用 Person1 对象代替 this 对象,那么 Person2 就有了 Person1 中的所有属性和方法了,相当于 Person2 继承了 Person1 的属性和方法。
对于什么时候该用什么方法,其实不用纠结。如果你的参数本来就存在一个数组中,那自然就用 apply,如果参数比较散乱相互之间没什么关联,就用 call。像上面的找一组数中最大值的例子,当然是用apply合理。
bind
和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。
bind返回值是函数
let obj = {
name: 'Dot'
}
function printName() {
console.log(this.name)
}
let dot = printName.bind(obj)
console.log(dot) // function () { … }
dot() // Dot
bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。而原函数 printName 中的 this 并没有被改变,依旧指向全局对象 window。
参数的使用
function fn(a, b, c) {
console.log(a, b, c);
}
var fn1 = fn.bind(undefined, 'aaa');
fn('A', 'B', 'C'); // A B C
fn1('A', 'B', 'C'); // aaa A B
fn1('B', 'C'); // aaa B C
fn.call(null, 'aaa'); // aaa undefined undefined
call 是把第二个及以后的参数作为 fn 方法的实参传进去,而 fn1 方法的实参实则是在 bind 中参数的基础上再往后排。
有时候我们也用bind方法实现函数柯里化,以下是一个简单的示例:
let add = function(x) {
return function(y) {
return x + y;
};
};
let increment = add(1);
let addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
我们也可以自己实现一个bind。
if (!Function.prototype.bind) {
Function.prototype.bind = function () {
let self = this, // 保存原函数
context = [].shift.call(arguments), // 保存需要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转为数组
return function () { // 返回一个新函数
self.apply(context, [].concat.call(args, [].slice.call(arguments)));
}
}
}
网友评论