文章 jQuery誕生記-原理與機(jī)制 讀后感
要點(diǎn)總結(jié):
-
關(guān)于new: (原文出處:詳解new function(){}和function(){}())
- 只要new表達(dá)式之后的constructor返回(return)一個(gè)引用對(duì)象(數(shù)組澳叉,對(duì)象科侈,函數(shù)等)涛舍,都將覆蓋new創(chuàng)建的匿名對(duì)象(我的理解:this還是那個(gè)this,但new以后的返回卻不是那個(gè)this);
- 如果返回(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原理
總體思路:
- 原生的獲取對(duì)象或?qū)?duì)象的處理都太繁瑣,用函數(shù)將其包裝抛杨,使其簡(jiǎn)單化。
- 將對(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)講解:
- 原生
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";
};
- 化繁為簡(jiǎn)觉义,進(jìn)行包裝
var $ = function(id) {
return document.getElementById(id);
};
$("button").onclick = function() {
$("image1").style.display = "none";
$("image2").style.display = "none";
};
- 樣式處理還是繁瑣雁社,繼續(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();
};
- 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();
};
上述方案有問題箍鼓,原因請(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方法奄喂。那就用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();
};
- 上面的方法富腊,元素的獲取直接在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();
};
- 能不能不用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();
};
- 獲取元素不僅僅只用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();
};
- 解決上面提出的問題睹欲,遍歷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();
};
- $("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();
};
- 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();
};
- 每次擴(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();
};
- 我們看這段代碼
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();
};
- 但是這個(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();
};
- 在init方法中仍侥,判斷第一個(gè)參數(shù),如果是節(jié)點(diǎn)蚁廓,直接 this[0] = this_node
- 每個(gè)擴(kuò)展方法都要 $.fn.functionName, 太繁瑣
$.fn.extend({
css: function() {},
attr: function() {},
data: function() {},
// ...
});