面向對象,一個老生常談的話題,但你有沒有想過面向對象要解決什麼問題?css
有一位大神說的很直接,」面向對象要解決的問題,並非封裝、繼承和多態,而是寫代碼的套路「。bash
我以爲有理,因此簡單粗暴點,今天略看下jQuery的封裝。函數
使用jQuery時,咱們一般會這樣寫:ui
//聲明一個JQ實例
$('.target')
//獲取元素的css屬性
$('.target').css('width')
//獲取元素的位置信息
$('.target')複製代碼
是否是與普通的對象實例不太同樣,new關鍵字去哪了,$符合又是什麼?固然咯,您確定是知道的,如今就讓咱們來簡化一下JQ吧。this
一個庫就是一個單獨的模塊,所以咱們使用自執行函數的方式模擬一個模塊。spa
(function() {
// to do something
})()複製代碼
既然可以在全局直接調用jQuery,則說明JQ被掛載在了全局對象上。所以當咱們在模塊中對外提供接口時,能夠採起window.jQuery的方式。prototype
var jQuery = function() {};
//....
window.jQuery = jQuery複製代碼
咱們在使用過程當中,並無使用jQuery,而是使用了$,其實只是多加了一個賦值操做。code
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方法。
其實,到這裏我是有不少疑問的。
一、你知道爲何要爲自執行函數設置參數window嗎?
二、你知道爲何要在構造函數jQuery內部用new建立並返回另外一個構造函數的實例嗎?
三、你知道爲何要jQuery.fn = jQuery.prototype,設置jQuery.fn 指向構造函數jQuery()的原型對象jQuery.prototype嗎?
四、你知道爲何能在構造函數jQuery.fn.init()的實例上調用構造函數jQuery()的原型方法和屬性嗎?
....