最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS工作中的小貼士之”閉包“與事件委托的”阻止冒泡“

 更新時(shí)間:2016年06月16日 17:28:56   投稿:mrr  
這篇文章主要介紹了JS工作中的小貼士之”閉包“與事件委托的”阻止冒泡“的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

說下閉包的由來

function a() {
var i = 0;
function b() {
console.log(i);
}
return b;
}
var c = a();
c(); 

一般來說,當(dāng)一個(gè)函數(shù)內(nèi)部匿名函數(shù)用到了自己的變量,并且這個(gè)匿名函數(shù)被返回了,這就建立了一個(gè)閉包,比如上面的代碼

這個(gè)時(shí)候,就算a調(diào)用結(jié)束被銷毀,i也會(huì)存在不會(huì)消失當(dāng)a定義時(shí),js解釋器會(huì)將函數(shù)a的作用域鏈設(shè)置為定義a時(shí)所在環(huán)境當(dāng)執(zhí)行a時(shí),a會(huì)進(jìn)入相應(yīng)的執(zhí)行環(huán)境,執(zhí)行環(huán)境創(chuàng)建后才會(huì)有作用域scope屬性,然后創(chuàng)建一個(gè)活動(dòng)對象,然后將其置為作用域鏈的頂端

現(xiàn)在a的作用域鏈就有a的活動(dòng)對象以及window

然后為活動(dòng)對象加入arguments屬性

這個(gè)時(shí)候a的返回函數(shù)b的引用給了c,b的作用域鏈包含a的活動(dòng)對象引用,所以c可以訪問到a的活動(dòng)對象,這個(gè)時(shí)候a返回后不會(huì)被GC

以上便是對閉包的簡單介紹,說多了就容易繞進(jìn)去了,我們這里簡單結(jié)束,然后進(jìn)入實(shí)際的場景加以說明

實(shí)際場景

同事的疑惑

之前一個(gè)同事讓我去看一個(gè)代碼:

var User = function (opts) {
var scope = this;
for (var k in opts) {
scope['get' + k] = function () {
return opts[k];
};
scope['set' + k] = function (v) {
return opts[k] = v;
};
}
};
var u = new User({
name: '測試',
age: 11
}); 

代碼本意很簡單,希望對傳入的對象生成get/set方法,但是他這里就遇到一個(gè)閉包問題:

導(dǎo)致這個(gè)問題的原因就是返回值內(nèi)部使用的k永遠(yuǎn)是“age”,這個(gè)k便是由于getXXX函數(shù)共享的活動(dòng)對象,這里修改也比較簡單

var User = function (opts) {
var scope = this;
for (var k in opts) {
(function (k) {
scope['get' + k] = function () {
return opts[k];
};
scope['set' + k] = function (v) {
return opts[k] = v;
};
})(k);
}
};
var u = new User({
name: '測試',
age: 11
}); 

在for循環(huán)內(nèi)部創(chuàng)建一個(gè)立即執(zhí)行函數(shù),將k傳入,這個(gè)時(shí)候getXXX函數(shù)共享的就是各個(gè)匿名函數(shù)的“k”了

生成唯一ID

生成唯一ID也是閉包一個(gè)經(jīng)典的使用方式

function getUUID() {
var id = 0;
return function () {
return ++id;
}
}
var uuid = getUUID(); 

這段代碼其實(shí)非常有意義,我們在瀏覽器中不停的執(zhí)行uuid()確實(shí)會(huì)得到不同的值,但是如果我們只使用getUUID()()的話每次值仍然一樣

導(dǎo)致這個(gè)問題的原因是,我們將getUUID執(zhí)行后的結(jié)果賦予uuid,這個(gè)時(shí)候uuid就保存對其中匿名函數(shù)的引用,而匿名函數(shù)保存著getUUID的活動(dòng)對象,所以id一直未銷毀

而直接調(diào)用的話,每次都會(huì)重新生成活動(dòng)對象,所以id是不能保存的

一段有意思的代碼

Util.tryUrl = function (url) {
var iframe = document.createElement('iframe');
iframe.height = 1;
iframe.width = 1;
iframe.frameBorder = 0;
iframe.style.position = 'absolute';
iframe.style.left = '-9999px';
iframe.style.top = '-9999px';
document.body.appendChild(iframe);
Util.tryUrl = function (url) {
iframe.src = url;
};
U.tryUrl(url);
}; 

這段代碼十分有意思,當(dāng)我們第一次調(diào)用時(shí)候會(huì)創(chuàng)建一個(gè)iframe對象,而第二次調(diào)用時(shí)候iframe對象就存在了,我們這里將代碼做一定簡化后

var getUUID = function () {
var i = 0;
getUUID = function () {
return i++;
};
return getUUID();
}; 

這樣調(diào)整后,其實(shí)并不存在返回函數(shù),但是我們其實(shí)依然形成了閉包

事件委托與閉包

我們都知道jquery的on是采用的事件委托,但是真正了解什么事事件委托仍然要花一定功夫,于是我們這里來試試

閉包是事件委托實(shí)現(xiàn)的基石,我們最后就以事件委托深入學(xué)習(xí)下閉包結(jié)束今天閉包的學(xué)習(xí)吧

加入我們頁面下有如下dom結(jié)構(gòu)

<input id="input" value="input" type="button" />
<div id="div">
我是div</div>
<span id="span">我是span</span>
<div id="wrapper">
<input id="inner" value="我是inner" type="button"/>
</div> 

我們使用zepto的話是使用如下方式綁定事件

$.on('click', 'selector', fn) 

我們這里沒有zepto就自己簡單實(shí)現(xiàn)吧

事件委托原理

首先事件委托實(shí)現(xiàn)的基石是事件冒泡,我們在頁面的每次點(diǎn)擊最終都會(huì)冒泡到其父元素,所以我們在document處可以捕捉到所有的事件

知道了這個(gè)問題后,我們可以自己實(shí)現(xiàn)一個(gè)簡單的delegate事件綁定方式:

function delegate(selector, type, fn) {
document.addEventListener(type, fn, false);
}
delegate('#input', 'click', function () {
console.log('ttt');
}); 

這段代碼是最簡單的實(shí)現(xiàn),首先我們無論點(diǎn)擊頁面什么地方都會(huì)執(zhí)行click事件,當(dāng)然這顯然不是我們想要看到的情況,于是我們做處理,讓每次點(diǎn)擊時(shí)候觸發(fā)他應(yīng)有的事件

這里有幾個(gè)問題比較尖銳:

① 既然我們事件是綁定到document上面,那么我怎么知道我現(xiàn)在是點(diǎn)擊的什么元素呢

② 就算我能根據(jù)e.target獲取當(dāng)前點(diǎn)擊元素,但是我怎么知道是哪個(gè)元素具有事件呢

③ 就算我能根據(jù)selector確定當(dāng)前點(diǎn)擊的哪個(gè)元素需要執(zhí)行事件,但是我怎么找得到是哪個(gè)事件呢

如果能解決以上問題的話,我們后面的流程就比較簡單了

確定點(diǎn)擊元素是否觸發(fā)事件

首先,我們點(diǎn)擊時(shí)候可以使用e.target獲取當(dāng)前點(diǎn)擊元素,然后再根據(jù)selector依次尋找其父DOM,如果找得到就應(yīng)該觸發(fā)事件

因?yàn)檫@些都是要在觸發(fā)時(shí)候才能決定,所以我們需要重寫其fn回調(diào)函數(shù),于是簡單操作后:

var arr = [];
var slice = arr.slice;
var extend = function (src, obj) {
var o = {};
for (var k in src) {
o[k] = src[k];
}
for (var k in obj) {
o[k] = obj[k];
}
return o;
};
function delegate(selector, type, fn) {
var callback = fn;
var handler = function (e) {
//選擇器找到的元素
var selectorEl = document.querySelector(selector);
//當(dāng)前點(diǎn)擊元素
var el = e.target;
//確定選擇器找到的元素是否包含當(dāng)前點(diǎn)擊元素,如果包含就應(yīng)該觸發(fā)事件
/*************
注意,此處只是簡單實(shí)現(xiàn),實(shí)際應(yīng)用會(huì)有許多判斷
*************/
if (selectorEl.contains(el)) {
var evt = extend(e, { currentTarget: selectorEl });
evt = [evt].concat(slice.call(arguments, 1));
callback.apply(selectorEl, evt);
var s = '';
}
var s = '';
};
document.addEventListener(type, handler, false);
} 

于是我們可以展開調(diào)用了:

delegate('#input', 'click', function () {
console.log('input');
});
delegate('#div', 'click', function () {
console.log('div');
});
delegate('#wrapper', 'click', function () {
console.log('wrapper');
});
delegate('#span', 'click', function () {
console.log('span');
});
delegate('#inner', 'click', function () {
console.log('inner');
}); 

我們這里來簡單解析下整個(gè)程序

① 我們調(diào)用delegate為body增加事件

② 在具體綁定時(shí)候,我們將其中的回調(diào)給重寫了

③ 在具體點(diǎn)擊時(shí)候(綁定幾次事件實(shí)際就會(huì)觸發(fā)幾次click),會(huì)獲取當(dāng)前元素,查看其選擇器搜索的元素是否包含他,如果包含的話便觸發(fā)事件

④ 由于這里每次注冊時(shí)候都會(huì)形成一個(gè)閉包,傳入的callback被維護(hù)起來了,所以每次調(diào)用便能找到自己的回調(diào)函數(shù)(這里對閉包理解很有幫助)

⑤ 最后重寫event句柄的currentTarget,于是一次事件委托就結(jié)束了

PS:我這里實(shí)現(xiàn)還有問題的,比如在event的處理上就有問題,但是作為demo的話我便不去關(guān)注了,有興趣的朋友自己去看zepto實(shí)現(xiàn)吧

事件委托的問題

事件委托可以提高效率但是有一個(gè)比較煩的事情就是阻止冒泡沒用

拿上面代碼來說,有一個(gè)inner元素和一個(gè)wrapper元素,他們是互相包裹關(guān)系

但是其執(zhí)行順序并不是先內(nèi)再外的事件冒泡順序,因?yàn)槭录拷壎ǖ搅薲ocument上面,所以這里執(zhí)行順序便是以其注冊順序所決定

這里有一個(gè)問題便是如何“阻止冒泡”

在inner處完了執(zhí)行

e.stopImmediatePropagation() 

是可以達(dá)到目的的,但是仍然要求inner元素必須注冊到之前

除此之外,就只給這種會(huì)嵌套的元素綁定一個(gè)事件,又e.target決定到底執(zhí)行哪個(gè)事件,具體各位自己斟酌

以上問題在使用backbone可能實(shí)際會(huì)遇到

相關(guān)文章

  • Bootstrap Table從零開始

    Bootstrap Table從零開始

    Bootstrap Table從零開始,這篇文章主要介紹了Bootstrap Table的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • JavaScript web網(wǎng)頁入門級開發(fā)詳解

    JavaScript web網(wǎng)頁入門級開發(fā)詳解

    Web框架正如前文所述, 在整個(gè)項(xiàng)目結(jié)構(gòu)中處于一個(gè)承上啟下的位置, 是整個(gè)項(xiàng)目的核心組件, 所以這次來聊聊Web框架的一些普適性特性和如何快速的入門
    2021-10-10
  • 微信小程序?qū)崿F(xiàn)側(cè)邊欄分類

    微信小程序?qū)崿F(xiàn)側(cè)邊欄分類

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)側(cè)邊欄分類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • 前臺(tái)js對象在后臺(tái)轉(zhuǎn)化java對象的問題探討

    前臺(tái)js對象在后臺(tái)轉(zhuǎn)化java對象的問題探討

    在開發(fā)項(xiàng)目中多次遇到前臺(tái)js對象在后臺(tái)轉(zhuǎn)化java對象的問題,下面就為大家介紹下前臺(tái)js對象轉(zhuǎn)后臺(tái)java對象,感興趣的朋友可以了解下
    2013-12-12
  • javascript中undefined的本質(zhì)解析

    javascript中undefined的本質(zhì)解析

    這篇文章主要介紹了javascript中undefined的本質(zhì)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 關(guān)于javascript獲取內(nèi)聯(lián)樣式與嵌入式樣式的實(shí)例

    關(guān)于javascript獲取內(nèi)聯(lián)樣式與嵌入式樣式的實(shí)例

    這篇文章主要介紹了關(guān)于javascript獲取內(nèi)聯(lián)樣式與嵌入式樣式的實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • JavaScript的防抖和節(jié)流一起來了解下

    JavaScript的防抖和節(jié)流一起來了解下

    這篇文章主要為大家詳細(xì)介紹了JavaScript的防抖和節(jié)流,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • javascript jquery對form元素的常見操作詳解

    javascript jquery對form元素的常見操作詳解

    下面小編就為大家?guī)硪黄猨avascript jquery對form元素的常見操作詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • 小程序如何實(shí)現(xiàn)中間帶加號的tabbar

    小程序如何實(shí)現(xiàn)中間帶加號的tabbar

    自定義tabBar可以讓開發(fā)者更加靈活地設(shè)置tabBar樣式,以滿足更多個(gè)性化的場景,下面這篇文章主要給大家介紹了關(guān)于小程序如何實(shí)現(xiàn)中間帶加號tabbar的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • JavaScript中雙符號的運(yùn)算詳解

    JavaScript中雙符號的運(yùn)算詳解

    這篇文章主要給大家介紹了JavaScript中雙符號的運(yùn)算,其中包括雙波浪號和雙感嘆號的運(yùn)算,文中給出了詳細(xì)的示例代碼,需要的朋友們可以參考借鑒,下面來一起看看吧。
    2017-03-03

最新評論

泾阳县| 镇江市| 连南| 华蓥市| 六枝特区| 沅江市| 图木舒克市| 泽州县| 临泽县| 忻城县| 永川市| 东山县| 新建县| 牡丹江市| 大连市| 景洪市| 额尔古纳市| 岚皋县| 渝北区| 天长市| 武强县| 静海县| 吉林省| 仙居县| 枣阳市| 大方县| 青川县| 江津市| 浦县| 色达县| 台湾省| 宁蒗| 金坛市| 新源县| 满洲里市| 汪清县| 循化| 盖州市| 怀集县| 桑植县| 巴林右旗|