網上看到一句話,匿名函數的執行是具有全局性的,那怎麼具有的全局性呢?閉包內部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