物件導向,一個老生常談的話題,但你有沒有想過物件導向要解決什麼問題?
有一位大神說的很直接,”物件導向要解決的問題,並不是封裝、繼承和多型,而是寫程式碼的套路“。
我覺得有理,所以簡單粗暴點,今天略看下jQuery的封裝。
使用jQuery時,我們通常會這樣寫:
//宣告一個JQ例項
$(`.target`)
//獲取元素的css屬性
$(`.target`).css(`width`)
//獲取元素的位置資訊
$(`.target`)複製程式碼
是不是與普通的物件例項不太一樣,new關鍵字去哪了,$符合又是什麼?當然咯,您肯定是知道的,現在就讓我們來簡化一下JQ吧。
一個庫就是一個單獨的模組,因此我們使用自執行函式的方式模擬一個模組。
(function() {
// to do something
})()複製程式碼
既然能夠在全域性直接呼叫jQuery,則說明JQ被掛載在了全域性物件上。因此當我們在模組中對外提供介面時,可以採取window.jQuery的方式。
var jQuery = function() {};
//....
window.jQuery = jQuery複製程式碼
我們在使用過程中,並沒有使用jQuery,而是使用了$,其實只是多加了一個賦值操作。
window.$ = window.jQuery = jQuery複製程式碼
在使用過程中直接使用$,其實相當於直接呼叫建構函式jQuery建立了一個例項,而沒有使用new。但是建立一個例項時,new關鍵字是必不可少的,由此說明new的操作被放在了jQuery方法中來實現了,而jQuery並不是真正的建構函式,
我們應該知道,函式可以扮演不少角色,物件啊,類啊…JQ內部的實現其實就是利用這個,在具體實現時,改變內部某些函式的prototype指向。下面我們就來看看實現程式碼把。
(function(ROOT) {
//建構函式
var jQuery = function(selector) {
//在該方法中直接返回new建立的例項,
//因此這裡的init才是真正的建構函式
return new jQuery.fn.init(selector);
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
version:`xxx`,
init: function(selector) {
var elem, selector;
elem = document.querySelector(selector);
this[0] = elem;
return this;
},
//在原型上新增一堆方法 ...
}
//讓init方法的原型指向jQuery的原型
jQuery.fn.init.prototype = jQuery.fn;
ROOT.jQuery = ROOT.$ = jQuery;
})(window)複製程式碼
在上面的實現中,首先在jQuery建構函式中宣告瞭一個fn屬性,並將其指向了jQuery的原型。隨後在原型物件上新增了init方法。
jQuery.fn = jQuery.prototype = {
init: function() {}
}複製程式碼
之後又將init的原型指向了jQuery.prototype.
jQuery.fn.init.prototype = jQuery.fn;複製程式碼
而在建構函式jQuery中,則返回了init的例項物件。
var jQuery = function(selector) {
return new jQuery.fn.init(selector);
}複製程式碼
最後對外暴露介面時,將字串$與方法jQuery對等起來。
ROOT.jQuery = ROOT.$ = jQuery;
複製程式碼
因此當使用$(`xxxx`)建立一個jQuery例項時,實際上呼叫的是jQuery(`xxxx`)建立的一個init例項。這裡正是建構函式原型上的init方法。
其實,到這裡我是有很多疑問的。
1、你知道為什麼要為自執行函式設定引數window嗎?
2、你知道為什麼要在建構函式jQuery內部用new建立並返回另一個建構函式的例項嗎?
3、你知道為什麼要jQuery.fn = jQuery.prototype,設定jQuery.fn 指向建構函式jQuery()的原型物件jQuery.prototype嗎?
4、你知道為什麼能在建構函式jQuery.fn.init()的例項上呼叫建構函式jQuery()的原型方法和屬性嗎?
….