<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div id="content" class="draggable"></div>
<script>
var getId=document.getElementById;
getId("content");
</script>
</body>
</html>
獲取不到div對象:許多引擎的document.getElementById方法的內部實現中需要用到this,當getElementById方法作爲document對象的屬性調用時,方法內部的this確實指向document,但是引用document.getElementById後,就變成了普通函數調用,函數內部的this指向的window。
解決方法
document.getElementById=(function (func) {
return function () {
return func.apply(document,arguments);
}
})(document.getElementById);
var getId=document.getElementById;
getId("content");
當使用call或者apply的時候,如果我們傳入的第一個參數爲null,函數體內的this會指向默認的宿主對象,在瀏覽器中爲this
變量生存週期
對於在函數內用var關鍵字聲明的局部變量來說,當退出函數時,這些局部變量就失去了他們的價值,他們會隨着函數調用的結束而被銷燬。
var func=function () {
var a=1;
return function () {
a++;
console.log(a);
}
};
var f=func();
f();//2
f();//3
f();//4
f();//5
執行var f=func()時,f返回一個匿名函數的引用,它可以訪問到func()被調用時產生的環境,而局部變量一直存在這個環境裏。所以a的生命得以延續。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var nodes=document.getElementsByTagName("div");
for(var i=0,len=nodes.length;i<len;i++){
nodes[i].οnclick=function () {
console.log(i);
}
}
</script>
</body>
</html>
無論點擊哪個div,最後彈出的結果都是4,這是因爲div節點的onclick事件是被異步觸發的,當事件被觸發的時候,for循環已經結束了。
解決辦法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var nodes=document.getElementsByTagName("div");
for(var i=0,len=nodes.length;i<len;i++){
(function (i) {
nodes[i].οnclick=function () {
console.log(i);
}
})(i);
}
</script>
</body>
</html>
閉包的應用
封裝變量
延長局部變量的壽命
var extent=function () {
var value=0;
return {
call:function () {
value++;
console.log(value);
}
}
}
var extent=extent();
extent.call();
var extent={
value:0,
call:function () {
this.value++;
console.log(this.value);
}
}
extent.call();
var Extent=function () {
this.value=0;
}
Extent.prototype.call=function () {
this.value++;
console.log(this.value);
}
var extent=new Extent();
extent.call();
以上三種方式實現閉包。
高階函數
高階函數是指滿足下列條件之一的函數
1. 函數可以作爲參數被傳遞
2. 函數可以作爲返回值輸出
高階函數的應用
函數柯里化:
currying又稱部分求值,一個currying的函數首先會接受一些參數,接受這些參數之後,該函數並不會立即求值,而是繼續返回另外一個函數,剛纔傳入的參數在函數形成的閉包中被保存起來。真正需要求值的時候,之前傳入的參數一次性用於求值。
var monthlycost=0;
var cost=function (money) {
monthlycost+=money;
}
cost(100);
cost(100);
cost(100);
cost(100);
cost(100);
console.log(monthlycost);
var cost=(function () {
var args=[];
return function () {
if(arguments.length===0){
var money=0;
for(var i=0,l=args.length;i<l;i++){
money+=args[i];
}
return money;
} else {
[].push.apply(args,arguments);
}
}
})();
cost(100);
cost(200);
cost(300);
console.log(cost());
var currying=function (fn) {
var args=[];
return function () {
if(arguments.length===0){
return fn.apply(this,args);
} else {
[].push.apply(args,arguments);
return arguments.callee;
}
}
};
var cost=(function () {
var money=0;
return function () {
for(var i=0,l=arguments.length;i<l;i++){
money+=arguments[i];
}
return money;
}
})();
var cost=currying(cost);
cost(100);
cost(100);
cost(100);
console.log(cost());
閉包
當函數可以記住並訪問所在的詞法作用域時,就產生了閉包。即使函數是在當前詞法作用域之外執行。
for(var i=1;i<=5;i++){
setTimeout(function timer() {
console.log(i);
},i*1000);
}
延遲函數的回調是在循環結束時才執行。當定時器在運行時即使每個迭代中執行的是setTimeout(…,0),所有的回調函數依然是在循環後纔會被執行。因此每次都輸出6.儘管循環中的五個函數是在各個迭代中分別定義的,但是他們都被封閉在一個共享的全局作用域中,因此實際上只有一個i。
for(var i=1;i<=5;i++){
(function () {
setTimeout(function timer() {
console.log(i);
},i*1000);
})();
}
解決方法:
for(var i=1;i<=5;i++){
(function () {
var j=i;
setTimeout(function timer() {
console.log(j);
},j*1000);
})();
}
for(var i=1;i<=5;i++){
(function (j) {
setTimeout(function timer() {
console.log(j);
},j*1000);
})(i);
}