网上看到一句话,匿名函数的执行是具有全局性的,那怎么具有的全局性呢?闭包内部this的指向是window,为什么指向了window呢?下面通过js函数调用模式和部分案例分析了为什么确实如此
1.js函数调用的模式
1.方法调用模式和函数调用模式
- 如果一个函数被设置为一个对象的属性,则称它为一个方法。当通过对象对其进行调用时,即this的方法调用模式。在方法调用模式下,函数中的this指向该函数所属的对象。
- 当一个函数并非对象的属性,而是直接作为函数进行调用时,为函数调用模式。此模式来调用函数的时候,this绑定的是全局对象。这是语言设计的一个错误。倘若语言设计正确,那么当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权
var obj = { val : 1, show : function(){alert(this.val);},//方法调用模式 outFunc : function(){ function innerFunc(){ console.log(this); } innerFunc(); //函数调用模式 }};obj.innerFunc();//在严格模式下,console.log(this)中的this为undefined,//否则,console.log(this)中的this为全局对象(浏览器中为window)//下文讲解为什么
2.构造器调用模式
当以new来调用一个函数时,即构造器模式。
当使用new调用时,发生这些事情:
创建一个连接到该函数prototype的新对象
将this绑定到创建的新对象上
函数结束时,如果没有返回其它对象,就返回this,即新创建的对象。
var quo=function(string){ this.status=string; } quo.prototype.get_status=function(){ return this.status; } var qq=new quo("aaa"); alert(qq.get_status());
3.上下文调用模式(call,apply)
var myobject={};var sum = function(a,b){ return a+b;};var sum2 = sum.call(myobject,10,30); //var sum2 = sum.apply(myobject,[10,30]); alert(sum2);
2.闭包和匿名函数案例分析(这两种都是函数调用模式)
var person = { name :'one', sayOne:function () {//方法调用模式 console.log(this.name) }, sayTwo:function () {//函数调用模式 return function () { console.log(this.name) } }, wrap: function(){//函数调用模式,匿名函数的执行环境具有全局性的由来 (function (){ console.log(this.name) })() } } person.sayOne()//one sayOne调用者是person对象,所以this指向person;(方法调用模式) person.sayTwo()()//window 返回的匿名函数在全局执行所以是window(函数调用模式) person.wrap()//window 语言就是这样设计的,也是匿名函数具有全局性的由来(函数调用模式)
3.事件监听内部调用方法案例分析
var div=document.getElementById("one"); function f2(){ console.log(this) } div.onclick =function () { console.log(this)//one那个节点 f2()//window(函数调用模式) }
4.综合应用:函数节流案例分析
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)
5.参考JS高级编程的案例解决问题
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(); //foofoo.getName.call(bar); //barfoo.getName.call(this); //globalfoo.getName.call(window); //global(function(){ console.log(this.name)}.bind(bar))(); //bar(function(){ console.log(this.name)}.bind())(); //global