jQuery原理(附帶部分new的原理)

文章 jQuery誕生記-原理與機(jī)制 讀后感

要點(diǎn)總結(jié):

  1. 只要new表達(dá)式之后的constructor返回(return)一個(gè)引用對(duì)象(數(shù)組澳叉,對(duì)象科侈,函數(shù)等)涛舍,都將覆蓋new創(chuàng)建的匿名對(duì)象(我的理解:this還是那個(gè)this,但new以后的返回卻不是那個(gè)this);
  2. 如果返回(return)一個(gè)原始類型(無(wú)return時(shí)其實(shí)為return原始類型undefined),那么就返回new創(chuàng)建的匿名對(duì)象(我的理解:返回的是this)。

關(guān)于第1種情況的代碼示例:

var o = new function() {return "圓心"};
alert(o); //將返回顯示“[object object] ”

關(guān)于第2種情況的代碼示例:

var o = new function() {return new String("圓心")};
alert(o); //返回的是“圓心”

  • 關(guān)于jQuery原理

總體思路:

  1. 原生的獲取對(duì)象或?qū)?duì)象的處理都太繁瑣,用函數(shù)將其包裝抛杨,使其簡(jiǎn)單化。
  2. 將對(duì)元素的處理方法通過原型繼承的方式進(jìn)行定義荐类。

原文中有一段文字表述可能有問題:

上面代碼顯然是有問題的蝶桶,new的是$.fn.init, $.fn.init的返回值是this. 也就是$()的返回值是$.fn.init的原型對(duì)象,尼瑪$.fn.init的prototype原型現(xiàn)在就是個(gè)光桿司令啊掉冶,喲真竖,正好脐雪,$.fn對(duì)應(yīng)的原型方法,除了init沒用外恢共,其他hide(), each()就是我們需要的战秋。因此,我們需要加上這么一行:

我覺得應(yīng)該改為:

上面代碼顯然是有問題的讨韭,new的是$.fn.init, $.fn.init的返回值是this. 也就是$()的返回值是$.fn.init的this對(duì)象脂信,$()的這個(gè)返回值沒法訪問hide和each方法,why透硝?因?yàn)檫@個(gè)返回值的原型對(duì)象是$.fn.init.prototype狰闪,也就是F.prototype.init.prototype,而hide和each方法是定義在$.fn濒生,也就是F.prototype上埋泵,所以根據(jù)原型鏈的查找機(jī)理,$()的這個(gè)返回值是訪問不到hide和each方法的罪治。 因此丽声,我們需要加上這么一行:

好了,現(xiàn)在我們用代碼的方式進(jìn)行演進(jìn)講解:

  1. 原生
var button = document.getElementById("button")
var image1 = document.getElementById("image1")
var image2 = document.getElementById("image2")

button.onclick = function() {
    image1.style.display = "none";
    image2.style.display = "none";
};
  1. 化繁為簡(jiǎn)觉义,進(jìn)行包裝
var $ = function(id) {
    return document.getElementById(id);
};

$("button").onclick = function() {
    $("image1").style.display = "none";
    $("image2").style.display = "none";
};
  1. 樣式處理還是繁瑣雁社,繼續(xù)簡(jiǎn)化,封裝一個(gè)方法到元素對(duì)象上去
var $ = function(id) {
    return document.getElementById(id);
};

HTMLElement.prototype.hide = function() {
    this.style.display = "none";
};

$("button").onclick = function() {
    $("image1").hide();
    $("image2").hide();
};
  1. IE6~IE8瀏覽器不認(rèn)識(shí)HTMLElement晒骇,上述方法不通用霉撵。改進(jìn)思路:Function的原型擴(kuò)展大家都認(rèn)識(shí),創(chuàng)建一個(gè)函數(shù)洪囤,然后通過new這個(gè)函數(shù)返回HTML元素徒坡,再將hide方法綁定到該函數(shù)的原型上。
var F = function(id) {
    return document.getElementById(id);
};
F.prototype.hide = function() {
    this.style.display = "none";
};

new F("button").onclick = function() {
    new F("image1").hide();
    new F("image2").hide(); 
};
  1. 上述方案有問題箍鼓,原因請(qǐng)參見文章開頭關(guān)于new的解釋:
    new F()返回的不是this對(duì)象了,而是DOM對(duì)象呵曹。其原型對(duì)象是構(gòu)造函數(shù).prototype款咖,而該構(gòu)造函數(shù)已經(jīng)不是F了。所以new F()的返回值根本訪問不到hide方法奄喂。

  2. 那就用this來(lái)做橋接铐殃,new F()還是返回this,DOM對(duì)象以屬性的方式綁定到this上跨新。原型對(duì)象上的hide方法可以通過點(diǎn)的方式訪問到DOM對(duì)象

var F = function(id) {
    this.element = document.getElementById(id);
};
F.prototype.hide = function() {
    this.element.style.display = "none";
};
new F("button").element.onclick = function() {
    new F("image1").hide();
    new F("image2").hide(); 
};
  1. 上面的方法富腊,元素的獲取直接在F方法中,但是域帐,實(shí)際情況赘被,考慮到兼容性實(shí)現(xiàn)是整,元素獲取可能會(huì)相當(dāng)復(fù)雜,同時(shí)方法私有民假,不能重利用浮入。因此,可以把元素獲取方法放在原型上羊异,便于管理和重用事秀。代碼如下:
var F = function(id) {
    return this.getElementById(id);
};
F.prototype.getElementById = function(id) {
    this.element = document.getElementById(id);
    return this;
};
F.prototype.hide = function() {
    this.element.style.display = "none";
};
new F("button").element.onclick = function() {
    new F("image1").hide();
    new F("image2").hide(); 
};
  1. 能不能不用new
var F = function(id) {
   return this.getElementById(id);
};
F.prototype.getElementById = function(id) {
    this.element = document.getElementById(id);
    return this;
};
F.prototype.hide = function() {
    this.element.style.display = "none";
};

var $ = function(id) {
    return new F(id);
};

$("button").element.onclick = function() {
    $("image1").hide();
    $("image2").hide();    
};
  1. 獲取元素不僅僅只用id,還有class等其他方式
var F = function(selector, context) {
    return this.getNodeList(selector, context);
};
/**
替換掉特殊的getElementById野舶,使用通用的獲取list的方式
*/
F.prototype.getNodeList = function(selector, context) {
    context = context || document;
    this.element = context.querySelectorAll(selector);
    return this;
};
var $ = function(selector, context) {
    return new F(selector, context);
};
/**
以下代碼有些問題易迹,因?yàn)楝F(xiàn)在是操作list了。不過可以用來(lái)理解流程平道。
*/
$("button").element.onclick = function() {
    $("image1").hide();
    $("image2").hide();    
};
  1. 解決上面提出的問題睹欲,遍歷list
var F = function(selector, context) {
    return this.getNodeList(selector, context);
};
F.prototype.getNodeList = function(selector, context) {
    context = context || document;
    this.element = context.querySelectorAll(selector);
    return this;
};
F.prototype.each = function(fn) {
    var i=0, length = this.element.length;
    for (; i<length; i+=1) {
        fn.call(this.element[i], i, this.element[i]);
    }
    return this;
};
F.prototype.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
var $ = function(selector, context) {
    return new F(selector, context);
};
$("button").element[0].onclick = function() {
    $("img").hide();  
};
  1. $("button").element[0] 看上去不爽
var F = function(selector, context) {
    return this.init(selector, context);
};
//這個(gè)方法已經(jīng)不是獲取list了,應(yīng)該將名字getNodeList換成更準(zhǔn)確的init
F.prototype.init = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    this.length = nodeList.length;
    for (var i=0; i<this.length; i+=1) {
        /**
          每一個(gè)DOM對(duì)象都以 屬性:對(duì)象 的方式保存在了this中;
          這個(gè)屬性名=nodeList中每個(gè)DOM對(duì)象的索引值
        */
        this[i] = nodeList[i];
    }
    return this;
};
F.prototype.each = function(fn) {
    var i=0, length = this.length;
    for (; i<length; i+=1) {
        fn.call(this[i], i, this[i]);
    }
    return this;
};
F.prototype.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
var $ = function(selector, context) {
    return new F(selector, context);
};
//這時(shí)可以不用$("button").element[0]了
$("button")[0].onclick = function() {
    $("img").hide();  
};
  1. F名字看著不爽巢掺,能不能換一個(gè):F → $.fn
var $.fn = function(selector, context) {
    return this.init(selector, context);
};
$.fn.prototype.init = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    this.length = nodeList.length;
    for (var i=0; i<this.length; i+=1) {
        this[i] = nodeList[i];
    }
    return this;
};
$.fn.prototype.each = function(fn) {
    var i=0, length = this.length;
    for (; i<length; i+=1) {
        fn.call(this[i], i, this[i]);
    }
    return this;
};
$.fn.prototype.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
var $ = function(selector, context) {
    return new $.fn(selector, context);
};
$("button")[0].onclick = function() {
    $("img").hide();  
};
  1. 每次擴(kuò)展新方法句伶,都要 $.fn.prototype.functionName=function(){} 嗎?作為插件陆淀,每次擴(kuò)展方法都要訪問高級(jí)屬性prototype好嗎考余?插件應(yīng)該把這一類難的高級(jí)的幫我們隱藏掉,那我們給他們重新起個(gè)名字吧轧苫,就讓 $.fn = F.prototype 吧
var F = function(selector, context) {
    return this.init(selector, context);
};
var $ = function(selector, context) {
    return new F(selector, context);
};
$.fn = F.prototype;
$.fn.init = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    this.length = nodeList.length;
    for (var i=0; i<this.length; i+=1) {
        this[i] = nodeList[i];
    }
    return this;
};
$.fn.each = function(fn) {
    var i=0, length = this.length;
    for (; i<length; i+=1) {
        fn.call(this[i], i, this[i]);
    }
    return this;
};
$.fn.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
$("button")[0].onclick = function() {
    $("img").hide();  
};
  1. 我們看這段代碼
var F = function(selector, context) {
    return this.init(selector, context);
};
var $ = function(selector, context) {
    return new F(selector, context);
};

明顯可以合并簡(jiǎn)化

var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};

然后我們?cè)僦匦聦徱曊未a發(fā)現(xiàn)楚堤,除了在其他地方用到了 F.prototype,F(xiàn)在其他地方?jīng)]有任何使用含懊,也就是說F可以隨便定義身冬,那我們可以用把F.prototype改成$.prototype,然后把原來(lái)的F定義刪除岔乔,于是代碼變成了

var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};
$.fn = $.prototype;
$.fn.init = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    this.length = nodeList.length;
    for (var i=0; i<this.length; i+=1) {
        this[i] = nodeList[i];
    }
    return this;
};
$.fn.each = function(fn) {
    var i=0, length = this.length;
    for (; i<length; i+=1) {
        fn.call(this[i], i, this[i]);
    }
    return this;
};
$.fn.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
$("button")[0].onclick = function() {
    $("img").hide();  
};
  1. 但是這個(gè)時(shí)候我們發(fā)現(xiàn)
var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};

這段代碼是有問題的酥筝,問題解釋請(qǐng)看我這篇文章開始處這段文字

上面代碼顯然是有問題的,new的是$.fn.init, $.fn.init的返回值是this. 也就是$()的返回值是$.fn.init的this對(duì)象雏门,$()的這個(gè)返回值沒法訪問hide和each方法嘿歌,why?因?yàn)檫@個(gè)返回值的原型對(duì)象是$.fn.init.prototype茁影,也就是F.prototype.init.prototype宙帝,而hide和each方法是定義在$.fn,也就是F.prototype上募闲,所以根據(jù)原型鏈的查找機(jī)理步脓,$()的這個(gè)返回值是訪問不到hide和each方法的。 因此,我們需要加上這么一行:

那怎么辦呢靴患?在指回去不就得了 $.fn.init.prototype = $.fn

于是整段代碼變成了現(xiàn)在這樣

var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};
$.fn = $.prototype;
$.fn.init = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    this.length = nodeList.length;
    for (var i=0; i<this.length; i+=1) {
        this[i] = nodeList[i];    
    }
    return this;
};
$.fn.init.prototype = $.fn;
$.fn.each = function(fn) {
    var i=0, length = this.length;
    for (; i<length; i+=1) {
        fn.call(this[i], i, this[i]);
    }
    return this;
};
$.fn.hide = function() {
    this.each(function() {
       this.style.display = "none";
    });
};
$("button")[0].onclick = function() {
    $("img").hide();  
};
  1. 在init方法中仍侥,判斷第一個(gè)參數(shù),如果是節(jié)點(diǎn)蚁廓,直接 this[0] = this_node
  2. 每個(gè)擴(kuò)展方法都要 $.fn.functionName, 太繁瑣
$.fn.extend({
    css: function() {},
    attr: function() {},
    data: function() {},
    // ...
});

over!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末访圃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子相嵌,更是在濱河造成了極大的恐慌腿时,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饭宾,死亡現(xiàn)場(chǎng)離奇詭異批糟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)看铆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門徽鼎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人弹惦,你說我怎么就攤上這事否淤。” “怎么了棠隐?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵石抡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我助泽,道長(zhǎng)啰扛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任嗡贺,我火速辦了婚禮隐解,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诫睬。我一直安慰自己煞茫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布摄凡。 她就那樣靜靜地躺著续徽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪架谎。 梳的紋絲不亂的頭發(fā)上炸宵,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天辟躏,我揣著相機(jī)與錄音谷扣,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛会涎,可吹牛的內(nèi)容都是我干的裹匙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼末秃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼概页!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起练慕,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤惰匙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后铃将,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體项鬼,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年劲阎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绘盟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悯仙,死狀恐怖龄毡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锡垄,我是刑警寧澤沦零,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站偎捎,受9級(jí)特大地震影響蠢终,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茴她,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一寻拂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丈牢,春花似錦祭钉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至申尼,卻和暖如春垮卓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背师幕。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工粟按, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诬滩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓灭将,卻偏偏與公主長(zhǎng)得像疼鸟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庙曙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容