网上看到一句话,匿名函数的执行是具有全局性的,那怎么具有的全局性呢?闭包内部this的指向是window,为什么指向了window呢?下面通过js函数调用模式和部分案例分析了为什么确实如此
案例1闭包-了解this指向最后一个调用者
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
这里的sayTwo相当于是一个闭包,内部的匿名函数的执行是this的函数调用模式,具体在案例2通过调用模式分析
var name = 'window'
var person = {
name :'Alan',
sayOne:function () {
console.log(this.name)
},
sayTwo:function () {
return function () {
console.log(this.name)
}
}
}
person.sayOne()
person.sayTwo()()
结果
第一个say打出来的是Alan,而第二个则是window
原因
- 函数内部的this指向调用者
- sayOne调用者是person对象,所以this指向person;
- sayTwo的调用者虽然也是person对象,但是区别在于这次调用并没有打出this而是在全局返回了一个匿名函数
- 而这个匿名函数不是作为某个对象的方法来调用执行,是在全局执行,最后的执行者就是window
案例2匿名函数-函数调用模式分析
var name = 'window'
var person = {
name :'one',
wrap: function(){
(function (){
console.log(this.name)// window
})()
function sum(){
console.log(this.name)// window
}
sum()
}
}
person.wrap()
wrap内部是一个自执行的匿名函数,this.name 打出来是 window那根据那句老话:this指向最后一个调用者;感觉无法分析,因为是自己执行自己。
用函数调用模式来分析
JS(ES5)里面有三种函数调用形式:
- func(p1, p2) 函数调用模式
- obj.child.method(p1, p2) 方法调用模式
- func.call(context, p1, p2) 上下文调用模式
可以看到不是通过对象来调用的方法,而是直接执行的,属于函数调用模式。
匿名函数自动执行属于函数调用模式,我们定义的非匿名函数sum虽然在对象里,但不是通过对象(obj.sum)直接调用而是在对象的函数里调用,所以sum也属于函数调用模式
调用模式的转化
其实我们常用调用模式如下:
- func(p1, p2)
- obj.child.method(p1, p2)
其实两种都是语法糖,可以等价地变为 call 形式
- func.call(undefined, p1, p2)
- obj.child.method.call(obj.child, p1, p2);
再加上浏览器里有一条规则:
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
所以上面person.wrap()里匿名函数的this指向window
总结
之前看到的匿名函数的执行是具有全局性是有道理的,我感觉只要不是对象直接调用的函数,好像执行都具有全局性呃这样看来。
所以嵌套函数调用this很容易就指向了window,func.call(this)可以帮我修正。
还有些特殊情况需要从上层传入this,我们通过let that = this 然后在func.call(that)来绑定this
综合应用:函数节流案例分析
1.throttle函数的执行环境是在全局,内部this通常是指向window的,然后返回一个匿名函数。
2.返回的匿名函数绑定了事件,this指向监听的元素(document)
3.fn其实与上面返回匿名函数形成了闭包,且fn也其实是一个匿名函数,匿名函数的执行具有全局性,fn内部this应该指向window
4这里用apply修正this指向,使fn内部的this重新指向document
function throttle(fn, delay) {
console.log(this)//window
// 记录上一次函数触发的时间
var lastTime = 0;
return function() {
// 记录当前函数触发的时间
var nowTime = Date.now();
if(nowTime - lastTime > delay) {
/*
fn();
console.log(this)//document
*/
fn.apply(this)// 修正this指向问题
console.log(this)//document
// 同步时间
lastTime = nowTime;
}
}
}
document.onscroll = throttle(function() {
/*console.log(this)//window*/
console.log(this)//document
console.log('scroll事件被触发了' + Date.now())
}, 1000)
参考JS高级编程的案例用call修改this指向
var name = "global";
var foo = {
name: "foo",
getName : function(){
console.log(this.name);
}
}
var bar = {
name: "bar",
getName : function(){
return (function(){
console.log(this.name);
})();
}
}
foo.getName(); //foo
foo.getName.call(bar); //bar
foo.getName.call(this); //global
foo.getName.call(window); //global
(function(){
console.log(this.name)
}.bind(bar))(); //bar
(function(){
console.log(this.name)
}.bind())(); //global
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。