国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Javascript中this與閉包學習筆記

pinecone / 1632人閱讀

摘要:但是在調用函數值執行之后并沒有達到我們想要的效果。解析在這里我們為每一個的事件綁定了一個匿名函數,這個匿名函數就形成了一個閉包。這樣我們就為每個的事件的匿名函數,都保存下了自己閉包變量。

博客原址

理解 Javascript中的this

基于不同的調用方式this的指向也會有所不同,調用方式大致有如下幾種:

調用方式 表達式
構造函數調用 new Foo();
對象方法調用 o.method();
函數直接調用 foo();
call/apply/bind func.call(o);

現在就來看看這些不同的調用模式,this的指向會有怎么樣的區別:

構造函數調用模式
function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName  = function(){
        console.info(this.name);
    };
}
var allen = new Person("allen",12);
console.info(allen);//{name: "allen", age: 12};...

通過這樣的代碼可以很清楚的的看出,構造函數 Person 內部的this指向被創建的調用對象 allen

對象方法調用

通過上面的代碼很明顯我們創建了一個 allen 對象,其中有一個 sayName 方法, 直接打印 this.name ,現在我們就來看一下它會輸出什么。

allen.sayName();//allen

很明顯,這里函數中的this指向allen對象本身。

函數直接調用

先來看一段代碼

function add(a, b) {
    return a + b;
}
var myNumber = {
    value: 1,
    double: function() {
        function handle() {
            this.value = add(this.value, this.value);
        }
        handle();
    }
};
console.info(myNumber.value);//1
myNumber.double();
console.info(myNumber.value);//1

解析: 首先我們定義了一個全局函數add用于加法運算,接著我們定義了一個對象,有一屬性value為1,還有一個方法的目的是讓value值乘以二。我們在函數內嵌套定義了一個函數handle,調用add方法并且執行。但是在調用函數值執行之后并沒有達到我們想要的效果。這是為什么呢?
如何你打開chrome調試工具并打下斷點會發現在handle函數內部的this會指向window!
由此可以發現,在函數內部創建的函數,在這個函數調用時,函數內部的this會指向window而不是外部的函數

下面就就可以看一下常見的兩個方案:

// 取消 handle函數的定義,直接在對象的方法中使用this
double2: function() {
    this.value = add(this.value, this.value);
},
// 使用變量保存外部函數的this。
double3: function() {
    var that = this;
    function handle() {
        that.value = add(that.value, that.value);
    }
    handle();
}
使用 call/applybind 手動改變 this

先來看下面這樣一段代碼:

function Point(x, y){
    this.x = x;
    this.y = y;
}
Point.prototype.move = function(stepX, stepY){
    this.x += stepX;
    this.y += stepY;
};
var p = new Point(0, 0);
console.log(p);//{x: 0, y: 0}
p.move(2,2);
console.log(p);//{x: 2, y: 2}
var circle = {x:1,y:1,r:1};
p.move.apply(circle, [2,1]);
console.info(circle);//{x: 3, y: 2, r: 1}

我們使用Point構造函數可以創建出一個點,在其原型上有一個move方法可以使這個點坐標移動。
之后我們又創建circle對象,有x/y/r屬性(把它想象成一個圓),之后我們的需求是:將這個圓的圓心移動,我們就使用了apply來借用move方法,最終將圓移動了位置,最終效果如下圖:

function.prototype.apply/call

在上面我們可以看到能實現圓心移動的關鍵方法就是apply,大致解析如下,p.move是一個函數它的作用就是將一個點移動,然后我們通過apply方法把它借用給circle這個對象。將circle對象上的x/y屬性進行變更,分別加2和1,實現了圓心的移動。很明顯在這里 apply方法描述的就是一個借用的功能.

為什么會把apply/call放在一起說呢,因為他們的功能并沒有實質性的區別。只是在傳入參數的時候,apply需要將參數以數組的形式進行傳遞,而call是將需要傳入的參數一個一個跟在借用的對象后。下面一個小例子足以說明:

function sum(a, b) {
    return a + b;
}
function call1(num1, num2) {
    return sum.call(this, num1, num2);
}
function apply1(num1, num2) {
    // return sum.apply(this,[num1,num2])
    return sum.apply(this, arguments);//利用函數的arguments對象

}
console.info(call1(10, 20));//30
console.info(call1(5, 10));//15

可以看到我們在后兩個函數中,可以直接使用sum方法。

function.prototype.bind

這里來看看ES5引入的bind,又有什么不同,還是和上面類似的代碼

function Point(x, y){
    this.x = x;
    this.y = y;
}
Point.prototype.move = function(stepX, stepY){
    this.x += stepX;
    this.y += stepY;
};
var p = new Point(0, 0);
var circle = {x:1,y:1,r:1};
var circleMove = p.move.bind(circle,2,2);
circleMove();
console.info(circle);//{x: 3, y: 3, r: 1}
circleMove(3,4);
console.info(circle);//{x: 5, y: 5, r: 1}

這里我使用了和 call 類似的調用方法,但是顯然 bind 和 call 不一樣,使用 bind 時,它會將我們綁定 this 后的函數引用返回,然后手動執行。可以看到的是,因為在這里我們綁定的對象的后面傳入了x/y兩個值,所以執行后坐標立即變化,并且在后來手動設置偏移量時也不再起到效果。
這樣的相比于apply立即執行的好處時,我們可以使用定時器,例如:setTimeout(circleMove,1000),延遲一秒后移動。

當然,每次只能移動固定的值也不是一件很好的事情,所以我們在使用 bind 的時候常常不會設置其默認參數, var circleMove2 = p.move.bind(circle,);,之后在執行函數時,再將參數傳入circleMove(3,4);,這樣就可以實現每次自定義偏移量了

這又引出了call/applybind的作用的另外一種說法: 擴充作用域

var color = "red";
var obj = {color:"blue"};
var obj1 = {color:"black"};
var obj2 = {color:"yellow"};

function showColor(){
    console.info(this.color);
}
showColor();//red
showColor.call(obj);//blue
showColor.apply(obj1);//black
showColor.bind(obj2)();//yellow

可以看到這里都實現了一樣的效果。值得說的是使用callaplly()來擴充作用域的最大好處就是對象不需要與方法有任何耦合關系。

 閉包 簡單定義

先來看這樣的一段代碼,在chrome中找到Scope列表,可以看到,在作用域鏈上我們已經創建了一個閉包作用域!

(function() {
    var a = 0;
    function b() {
        a = 1;
        debugger;
    }
    b();
})();

閉包一個最簡單的定義就是:閉包就是說在函數內部定義了一個函數,然后這個函數調用到了父函數內的相關臨時變量,這些相關的臨時變量就會存入閉包作用域里面.這就是閉包最基礎的定義

保存變量

下面就來看一下閉包的一個基本特性保存變量

function add(){
    var i = 0;
    return function(){
        console.info(i++);
    };
}
var f = add();
f();//1
f();//2

我們定義了一個 add 方法,執行完畢后會返回一個函數,接著我們就把這個函數賦值給了變量f,由于 add 函數也是返回一個函數,在我們每一次執行f()的時候,它引用了add內的變量i,并且保存在自己的閉包作用域內,所以一直輸出執行的話,也會累加輸出。

小tips

需要我們記住的是 每次函數調用的時候創建一個新的閉包

var fun = add();
fun();//1
fun();//2

我們再來通過簡單的例子看看另一個注意的地方:

function test(){
    var a  = 0;
    var ff =  function(){
        console.info(a);
    };
    a = 1214;
    return ff;
}
var b = test();
b();//1214

執行的結果是1214,從這里我們可以看到 閉包中局部變量是引用而非拷貝,其實這樣的改變發散開來我們就可以知道,即使在這里變量 a 未在函數 ff 之前定義,而是var a = 1214;我們同樣會得到同樣的結果

點擊li顯示對應編號案例解析

其實上面這些我是很暈的,來看一個我們實際在前端編程過程中經常遇到的問題。
我們有一個列表,分別為1/2/3,我們的需求是在點擊不同的數字時,也能把它對應的編號彈出來。然后我們洋洋灑灑寫下了這樣的代碼:

  • 1
  • 2
  • 3

一運行,發現懵了。怎么彈出來的都是3?不對啊,我不是用循環將值都傳進去了嗎?

如果你確實理解了上面的 閉包中局部變量是引用而非拷貝這一節中的兩個案例的話,那么就應該能了解一些。

解析:在這里我們為每一個li的onclick事件 綁定了一個匿名函數,這個匿名函數就形成了一個閉包。這些匿名函數并不立即執行,而是在點擊對應的li的時候才回去執行它。
而在這時就和上面的a = 1214;這個例子一樣,此時的循環早已結束,i 就等于oLi.length,在我們點擊不同的li時,閉包中引用的其實都是引用的同一個變量i自然彈出來的都是3,(這里不理解引用的都是用一個i的話,可以將alert(i);替換成alert(i++);,再到瀏覽器上去進行測試)

解決方案:

(function() {
    var oLi = document.getElementById("#list").getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        oLi[i].onclick = (function(j) {
            return function(){
                alert(j);
            };
        })(i);
    }
})();
/*
(function() {
    var oLi = document.getElementById("#list").getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        (function(j){
            oLi[i].onclick= function(){
                alert(j);
            };
        })(i);
    }
})();
*/

可以看到這里給出了兩個簡單的寫法,但實際上除了寫法不同之外、閉包包含范圍、內容也不太一樣(有興趣的可以打開chrome調試工具看看),但是達到的效果是一樣的。這樣我們就為每個lionclick事件的匿名函數,都保存下了自己閉包變量。就可以實現在點擊每個li的時候彈出對應的標號了。(還可以將alert(j);替換成alert(j++);欣賞一下點擊不同li時的累加效果)

當然如果你只是想要記住一些標號這么簡單的事情,其實還可以將變量保留于元素節點上,也能達到一樣的效果,如下:

(function() {
    var oLi = document.getElementById("#list").getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        oLi[i].flag = i;
        oLi[i].onclick = function() {
            alert(this.flag);
        };
    }
})();

如果有錯誤之處,請指正。謝謝!

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90848.html

相關文章

  • 重學前端學習筆記(十八)--JavaScript閉包和執行上下文

    摘要:申明與賦值立即執行的函數表達式,通過創建一個函數,并且立即執行,來構造一個新的域,從而控制的范圍。函數接受一個的形參,該參數是一個對象引用,并執行了。在最新的標準中,引入了一個新概念。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄...

    silencezwm 評論0 收藏0
  • 重學前端學習筆記(十八)--JavaScript閉包和執行上下文

    摘要:申明與賦值立即執行的函數表達式,通過創建一個函數,并且立即執行,來構造一個新的域,從而控制的范圍。函數接受一個的形參,該參數是一個對象引用,并執行了。在最新的標準中,引入了一個新概念。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄...

    liaorio 評論0 收藏0
  • 重學前端學習筆記(十八)--JavaScript閉包和執行上下文

    摘要:申明與賦值立即執行的函數表達式,通過創建一個函數,并且立即執行,來構造一個新的域,從而控制的范圍。函數接受一個的形參,該參數是一個對象引用,并執行了。在最新的標準中,引入了一個新概念。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些要點筆記以及感悟,完整的可以加入winter的專欄...

    caikeal 評論0 收藏0
  • JavaScript學習筆記(二) 對象函數

    摘要:在中函數是一等對象,它們不被聲明為任何東西的一部分,而所引用的對象稱為函數上下文并不是由聲明函數的方式決定的,而是由調用函數的方式決定的。更為準確的表述應該為當對象充當函數的調用函數上下文時,函數就充當了對象的方法。 引言:當理解了對象和函數的基本概念,你可能會發現,在JavaScript中有很多原以為理所當然(或盲目接受)的事情開始變得更有意義了。 1.JavaScript...

    jeffrey_up 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<