Javascript設計模式與開發實踐(關於this/call/apply)

<!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);
}
發佈了95 篇原創文章 · 獲贊 22 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章