面向对象,一个老生常谈的话题,但你有没有想过面向对象要解决什么问题?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()的原型方法和属性吗?
....