方法调用模式:
当一个函数被保存为对象的一个方法时,如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用,此时的this被绑定到这个对象。
var a = 1
var obj1 = {
a:2,
fn:function(){
console.log(this.a)
}
}
obj1.fn()//2
此时的this是指obj1这个对象,obj1.fn()
实际上是obj1.fn.call(obj1)
,事实上谁调用这个函数,this就是谁。补充一下,DOM对象绑定事件也属于方法调用模式,因此它绑定的this就是事件源DOM对象。如:
document.addEventListener('click', function(e){ console.log(this); setTimeout(function(){ console.log(this); }, 200); }, false);
点击页面,依次输出:document和window对象
解析:点击页面监听click事件属于方法调用,this指向事件源DOM对象,即
解析:点击页面监听click事件属于方法调用,this指向事件源DOM对象,即
obj.fn.apply(obj)
,setTimeout内的函数属于回调函数,可以这么理解,f1.call(null,f2)
,所以this指向window。把函数赋值之后再调用
var a = 1
var obj1 = {
a:2,
fn:function(){
console.log(this.a)
}
}
var fn1 = obj1.fn
fn1()//1
obj1.fn是一个函数
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)function(){console.log(this.a)}
,此时fn1就是不带任何修饰的函数调用,function(){console.log(this.a)}.call(undefined)
,按理说打印出来的 this 应该就是 undefined 了吧,但是浏览器里有一条规则:因此上面的this绑定的就是window,它也被称为隐性绑定。
如果你希望打印出2,可以修改
如果你希望打印出2,可以修改
fn1()
为fn1.call(obj1)
,显示地绑定this为obj1回调函数
var a = 1
function f1(fn){
fn()
console.log(a)//1
}
f1(f2)
function f2(){
var a = 2
}
改写代码如下:
var a = 1
function f1(){
(function (){var a = 2})()
console.log(a)//1
}
f1()
仍旧是最普通的函数调用,f1.call(undefined)
,this指向window,打印出的是全局的a。
借此,我们终于可以解释为什么setTimeout总是丢失this了,因为它也就是一个回调函数而已。
setTimeout(function() {
console.log(this)//window
function fn(){
console.log(this)//window
}
fn()
}, 0);
call
call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
var arr = [1, 2, 3, 89, 46]
var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4])//89
可以这么理解:
obj1.fn()
obj1.fn.call(obj1);
fn1()
fn1.call(null)
f1(f2)
f1.call(null,f2)
应用场景
求数组中的最大和最小值
var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89
var min = Math.min.apply(null,arr)//1
将类数组转化为数组
var trueArr = Array.prototype.slice.call(arrayLike)
call、apply和bind函数存在的区别:
bind返回对应函数, 便于稍后调用; apply, call则是立即调用。