this和call/apply/bind

this和call/apply/bind

概括的说,call、apply、bind的用途是指定函数调用时所引用上下文对象(context object)的值,即this的值

深入理解this

普通函数的this

在最普通的函数调用中,this引用的是把函数当成方法调用的上下文对象,此时this值就是window。或者说,在网页的全局上下文中调用函数时,this指向window。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var hero = 'Jackie Chan';
//window.hero = 'Jackie Chan';

let person = {
hero:'Eason Chan'
};

function myHero(){
return this.hero;
}

myHero(); //'Jackie Chan'

person.myHero = myHero;
person.myHero(); //'Eason Chan'

定义在全局上下文中的函数 myHero()引用了 this 对象。这个 this 到底引用哪个对象必须到
函数被调用时才能确定。

若想只调用myHero()就得到person.hero值,则可以使用call设定this指向:

1
2
3
4
5
6
7
8
myhero();						//'Jackie Chan'
myHero.call(person,); //'Eason Chan'

myHero.call(null,); //'Jackie Chan'
myHero.call(undefined,); //'Jackie Chan'
myHero.call('',); //undefined
...
myHero.call([其他falsy值],); //undefined

浏览器里有一条规则:如果传的 context 是 null 或 undefined(不包括其他falsy值),那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined),因此会出现第三行代码以后的结果。

箭头函数的this

1
2
3
4
5
6
7
8
9
10
11
12
13
var hero = 'Jackie Chan';
//window.hero = 'Jackie Chan';

let person = {
hero:'Eason Chan'
};

let myHero = () => this.hero;

myHero(); //'Jackie Chan'

person.myHero = myHero;
person.myHero(); //'Jackie Chan'

在箭头函数中,this 引用的是定义箭头函数的上下文。 由于箭头函数myHero是在全局作用域中定义的,所以this指向的都是window。

注意:箭头函数的this是固定的,不能被call/apply/bind修改。

闭包中的this

1
2
3
4
5
6
7
8
9
10
11
12
13
var color = 'red';
// window.color = 'red';

let obj = {
color: 'blue',
sayColor() {
return function color(){
return this.color;
}
}
};

obj.sayColor()(); // 'red'

以上代码中,全局变量color和函数color()构成闭包。

具体解释:见《闭包》

要想使obj.sayColor()();返回obj的blue,则应该让obj.color和函数color()构成闭包。可将this保存到闭包可以访问的另一个变量that中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var color = 'red';
// window.color = 'red';

let obj = {
color: 'blue',
sayColor() {
let that = this;
return function color(){
return that.color;
}
}
};

obj.sayColor()(); // 'blue'

回调函数中的this

回调函数中的this指向window。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var hero = 'Jackie Chan';
function peoplesHero(fn){
fn();
return hero;
}

function myHero(){
var hero = 'Eason Chan';
return hero;
}

peoplesHero(myHero); //'Jackie Chan'

//以上为同步回调,代码等价于:
var hero = 'Jackie Chan';
function peoplesHero(){
(function myHero(){
var hero = 'Eason Chan';
return hero;
})();
return hero;
}

peoplesHero(); //'Jackie Chan'

call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

1
function.call(thisArg, arg1, arg2, ...)

代码举例在普通函数的this小节已经介绍,不再赘述。

注意:正确的函数调用方式是使用call/apply/bind指定this.

apply

apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

1
function.apply(thisArg, [argsArray])

注意:call()方法的作用和 apply() 方法非常非常类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

1
function.bind(thisArg[, arg1[, arg2[, ...]]])

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var hero = 'Jackie Chan';
//window.hero = 'Jackie Chan';

let person = {
hero:'Eason Chan'
};

function myHero(){
return this.hero;
}

var sayHero = myHero.bind(person);
console.log(sayHero); //ƒ myHero(){ return this.hero; }

myHero(); //'Jackie Chan'
sayHero(); //'Eason Chan'

call/apply/bind的应用场景

参考:call、apply和bind方法的用法以及区别 - 简书

  • 求数组中的最大和最小值
1
2
3
var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89
var min = Math.min.apply(null,arr)//1
  • 将类数组转化为数组
1
var trueArr = Array.prototype.slice.call(arrayLike)
  • 数组追加
1
2
3
4
5
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var total = [].push.apply(arr1, arr2);//6
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
  • 判断变量类型
1
2
3
4
5
function isArray(obj){
return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('dot') // false
  • 利用call和apply做继承
1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
// 这里的this都指向实例
this.name = name
this.age = age
this.sayAge = function(){
console.log(this.age)
}
}
function Female(){
Person.apply(this,arguments)//将父元素所有方法在这里执行一遍就继承了
}
var dot = new Female('Dot',2)
  • 使用 log 代理 console.log
1
2
3
4
function log(){
console.log.apply(console, arguments);
}
// 当然也有更方便的 var log = console.log()

版权声明:本文作者为「Andy8421」.本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!