共计 2642 个字符,预计需要花费 7 分钟才能阅读完成。
网上看到一句话,匿名函数的执行是具有全局性的,那怎么具有的全局性呢?闭包内部 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(); //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 |
正文完
发表至: javascript
2019-05-11