之前写过一篇JavaScript命名空间的文章,写完后一对比对jQuery的简单使用很是惊羡,看了看人家源码,用的原理很类似啊,改进一下之前的版本,做个简易版的jQuery

之前的代码

(function () {
var _NS = function () { } _NS.prototype.select = function (selector,context) {
var context = context || document;
return context.querySelectorAll(selector);
} _NS.prototype.isArrayLike=function(obj){
if(obj instanceof Array){
return true;
} var length=obj.length;
if ( obj.nodeType === 1 && length ) {
return true;
}
return false;
} _NS.prototype.html = function (obj,value) {
var isArray=this.isArrayLike(obj), i=0; if (typeof value == 'string') {
if (!isArray) {
obj.innerHTML = value;
} else {
var length = obj.length;
while (i < length) {
obj[i].innerHTML = value;
i += 1;
}
}
} else {
if (!isArray) {
return obj.innerHTML;
} else {
return obj[0].innerHTML;
}
}
} window.NS = new _NS();
})();

这样的写法只是对各种自定义方法的隔离,只是能用而已,不支持日趋流行的链式调用,使用jQuery的时候可以很方便的写出$(selector).xxx().xxxx().xxxxxx() 这样格式的代码,既简洁易读效率又高,而上面的写法只能调用一个个孤零零函数,没有对象整体性可言。

上面的select方法返回值为查询的对象(IE低版本浏览器不支持),很多时候获取一个对象后我们希望使用库函数对其直接进行操作,比如我们希望把页面上所有div的innerHTML设为test,并隐藏这些div,如果用jQuery会这么写

$(div).html('test').css(‘display’,’none’);

上面的代码虽然没实现css方法,但是如果有的话得这么写

var divs=NS.select('div');
divs.html('test');
divs.css('display','none');

为什么jQuery很方便

jQuery好用有几个原因:

1. $本身是个function对象,包含一些“静态方法”(不用实例化就可以用的方法),比如$.ajax、$.animation,可以这样$.xxx()直接使用jQuery的一些库函数

2. 因为$本身是一个函数,可以被调用。但是$(selector) 返回结果并不是搜索的结果集,而是一个jQuery实例,结果集被封装在jQuery对象内,这样可以使用一些jQUery的实例方法(也就是定义在prototype内的方法等),例如$(‘div’).html(‘test’), 这样由于$(‘div’)返回的是jQuery实例,所以可以调用实例方法html()。

3. jQuery对象大部分实例方法尽量返回jQuery对象,即调用者本身,这样可以支持链式调用,比如$(‘div’).html(‘test’).css(‘display’,’none’) , $(‘div’)返回jQuery对象,里面包含结果集,调用实例方法html(‘test’) 同样返回jQuery对象,调用 css(‘display’,’none’) 同样也返回jQuery对象,可以这样一直调用下去。

构造函数的一些知识

想要做到上面几点除了prototype等基本知识,还需要了解一些关于JavaScript构造函数的知识。

1.什么样的函数是构造函数

在JavaScript的世界里构造函数并不神秘,也不特殊,任何函数通过new 操作符调用都可以变为构造函数,不使用new 操作符就不是构造函数,而是直接按普通函数调用。

2.构造函数返回什么样的结果

构造函数的返回值分为两种情况,当function没有return语句或者return回一个基本类型(bool,int,string,undefined,null)的时候,返回new 创建的一个匿名对象,该对象即为函数实例;如果function体内return一个引用类型对象(Array,Function,Object等)时,该对象会覆盖new创建的匿名对象作为返回值。

写个小例子验证一下

function A(){
return true;
} var a=new A();
console.log(a instanceof A); //true function B(){
return new Array();
} var b=new B();
console.log(b instanceof Array); //true

做个贫下中农版的jQuery

针对上面讲的jQuery的几点好处,尝试写一个版本,为了更像jQuery一些,把返回函数名字也改为$,先写一个框架

version 0.1

(function(){
var $=function(selector,context){ }; $.ajax=function(configs){ //静态方法
//TODO
} $.prototype.html=function(value){ //实例方法
//TODO
} window.$=$;
})();

关于静态方法和实例方法的部分很好实现,就写成类似version 0.1的样子就行,在这个版本中$确实是个函数了,但是怎么让$(selector)执行返回$的实例,同时实例内又包含搜索结果。尝试写下一个版本

version0.2

(function(){
var $=function(selector,context){
var context = context || document;
var nodeList = context.querySelectorAll(selector); var $=new $();
$.elements=nodeList;
return $;
}; $.ajax=function(configs){ //静态方法
//TODO
} $.prototype.html=function(value){ //实例方法
//TODO
} window.$=$;
})();

这个看起来可以,$是个函数,有一些静态方法,$(selector)返回$实例,包含搜索结果集,但是这种方式有语法上的错误,代码中试图在$function体内new自己,在执行到new的时候JavaScript还不认识$,失败!

可以尝试换一个思路 version 0.3

(function(){
function f(selector,context){
return ?;
} var $=(function(){
return f;
})();
window.$=$;
})();

这样$同样也是个函数,但是怎么才能让其执行结果返回本身的实例呢,也就是f到底应该怎样返回$的实例呢,说了这么多遍实例终于想起除了直接new一个对象可以得到其实例,还有一个地方可以得到其实例,在prototype中定义的函数可以访问this对象并返回。这就要求在f的prototype函数内返回this,不断的改啊调啊终于成了这样

version 0.4

(function(){
var $=(function(){
function f(selector,context){
return f.prototype.init(selector,context);
} f.prototype.init=function(selector,context){
var context = context || document;
var nodeList = context.querySelectorAll(selector);
this.length = nodeList.length;
this.elements=new Array();
for (var i = 0; i < this.length; i++) {
this.elements[i] = nodeList[i];
}
return this;
} return f;
})(); window.$=$;
})();

填上刚才自定义的函数

version 1.0

(function(){
var $=(function(){
function f(selector,context){
return f.prototype.init(selector,context);
} f.ajax=function(configs){
//TODO
} f.prototype.init=function(selector,context){
var context = context || document;
var nodeList = context.querySelectorAll(selector);
this.length = nodeList.length;
this.elements=new Array();
for (var i = 0; i < this.length; i++) {
this.elements[i] = nodeList[i];
}
, return this;
} f.prototype.html=function(value){
//TODO
} return f;
})(); window.$=$;
})();

这样终于所有要求都实现了,在内部匿名函数中定义function f,最后返回赋值给$,这样$是个函数,在执行的时候层层调用,最后调用到f.prototype.init,并返回其返回对象(好绕口),在init中把搜索结果放到this的属性中,最后返回this,然后f在把this返回,这样$(selector)的结果是$对象实例,而且包含搜索结果。

jQuery源码结构

上面的结果已经很让人满意了,仔细读了读jQuery源码,看看jQuery结构

(function( window, undefined ) {

    var jQuery = (function() {
// 构建jQuery对象
var jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context, rootjQuery );
} // jQuery对象原型
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
// selector有以下7种分支情况:
// DOM元素
// body(优化)
// 字符串:HTML标签、HTML字符串、#id、选择器表达式
// 函数(作为ready回调函数)
// 最后返回伪数组
}
}; //把jQuery的prototype赋值给init方法的prototype
jQuery.fn.init.prototype = jQuery.fn; // 合并内容到第一个参数中,后续大部分功能都通过该函数扩展
// 通过jQuery.fn.extend扩展的函数,大部分都会调用通过jQuery.extend扩展的同名函数
jQuery.extend = jQuery.fn.extend = function() {}; // 在jQuery上扩展静态方法
jQuery.extend({
// ready bindReady
// isPlainObject isEmptyObject
// parseJSON parseXML
// globalEval
// each makeArray inArray merge grep map
// proxy
// access
// uaMatch
// sub
// browser
}); return jQuery; })(); window.jQuery = window.$ = jQuery;
})(window);

总体上是一致的,但是jQuery的结构要科学很多

1.将window对象传入匿名函数,使匿名函数内部可以直接访问,防止匿名函数内部使用window对象的时候需要层层查找作用域链,最后才能找到window

2. 没有一棍子打死,完全使用$,当出现$命名冲突的时候可以使用jQuery代替

3. 定义jQuery.fn=jQuery.prototype,代码写起来方便了很多,也有利于压缩

4. 没有使用elements属性,而是利用数组特性封装搜索结果集,在使用的时候更容易想到

5. 定义each函数用于遍历结果集

6. 提供extend函数用于向对象内部添加属性

穷人版jQuery Version2.0

看了大师的写法终于可以脱离贫下中农了

(function () {
var $ = (function () {
var $ = function (selector, context) {
return new $.prototype.init(selector, context);
} $.prototype.init = function (selector, context) {
var context = context || document;
var nodeList = context.querySelectorAll(selector);
this.length = nodeList.length;
for (var i = 0; i < this.length; i++) {
this[i] = nodeList[i];
}
return this;
} $.prototype.each = function (callback, args) {
var length = this.length, i = 0;
if (args) {
while (i < length) {
callback.call(this[i], args);
i += 1;
}
} else {
while (i < length) {
callback.call(this[i]);
i += 1;
}
}
return this;
} $.prototype.html = function (value) {
if (typeof value == 'string') {
this.each(function () {
this.innerHTML = value;
});
return this;
} else {
return this[0].innerHTML;
}
} $.prototype.init.prototype = $.prototype; return $;
})(); window.$ = $;
})();

最后

本文就是在总结我试图实现jQuery的过程,思绪结构有些混乱,希望不要误导读者。在读了很多次jQuery源码,加上网上很多博客解析才一步步把jQuery看清,jQuery的设计非常巧妙,常人很难一次想到实现方式,而且非常有前瞻性,任何时候都在使用jQuery命名空间或jQuery实例,防止了与未来JavaScript原生API的冲突,多研究研究受益匪浅。

贫下中农版jQuery的更多相关文章

  1. 迷你版jQuery——zepto核心源码分析

    前言 zepto号称迷你版jQuery,并且成为移动端dom操作库的首选 事实上zepto很多时候只是借用了jQuery的名气,保持了与其基本一致的API,其内部实现早已面目全非! 艾伦分析了jQue ...

  2. 简易版jquery

    最近写了一个简易版的jquery   github地址:https://github.com/jiangzhenfei/Easy-Jquery 完成的方法: 1.$('#id') 2.extend扩展 ...

  3. java版-JQuery上传插件Uploadify使用实例

    摘自:http://itindex.net/detail/47160-java-jquery-%E4%B8%8A%E4%BC%A0 运行效果: 包结构图: 后台JAVA逻辑: package com. ...

  4. 简易版jQuery——mQuery

    前面的话 虽然jQuery已经日渐式微,但它里面的许多思想,如选择器.链式调用.方法函数化.取赋值合体等,有的已经变成了标准,有的一直影响到现在.所以,jQuery是一个伟大的前端框架.前端世界日新月 ...

  5. JS对象深刻理解 - 1

    JavaScript创建对象   JavaScript 有Date.Array.String等这样的内置对象,功能强大使用简单,人见人爱,但在处理一些复杂的逻辑的时候,内置对象就很无力了,往往需要开发 ...

  6. JavaScript创建对象的写法

    JavaScript 有Date.Array.String等这样的内置对象,功能强大使用简单,人见人爱,但在处理一些复杂的逻辑的时候,内置对象就很无力了,往往需要开发者自定义对象.   对象是什么 从 ...

  7. jQuery整理

    近几日总是在用js写一些东西,jq用的反而少了,最近在工作中总是会用到不常用的jQuery方法,之前觉得可能用到的情况比较少,便没在意这些方法,结果吃了亏,现在准备重新总结一些jQuery中的一些常用 ...

  8. jQuery笔记总结

    来源于:http://blog.poetries.top/2016/10/20/review-jQuery/ http://www.jianshu.com/p/f8e3936b34c9 首先,来了解一 ...

  9. 仿照jquery封装一个自己的js库(一)

    所谓造轮子的好处就是复习知识点,加深对原版jquery的理解. 本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现.主要涉及知识点包 ...

随机推荐

  1. Entity Framework 6, database-first with Oracle

    Entity Framework 6, database-first with Oracle 转载自http://csharp.today/entity-framework-6-database-fi ...

  2. moss2003 sp3补丁安装

    安装完成以后自己在产品库里增加的存储过程不见了,自我保护?

  3. iOS遍历相册中的图片

    //获取相册的所有图片 - (void)reloadImagesFromLibrary { self.images = [[NSMutableArray alloc] init]; dispatch_ ...

  4. Gray Code

    Gray Code The gray code is a binary numeral system where two successive values differ in only one bi ...

  5. javaWeb开发小工具--MyCommonUtils

    MyCommonUtils 参考一些资料,写了这个工具类.在这个工具类中,主要实现了2个方法: 1.生成随机的序列号 uuid(): 2.将Map中的数据封装到javaBean对象中toBean(Ma ...

  6. 使用KeepAlived搭建MySQL高可用环境

    使用KeepAlived搭建MySQL的高可用环境.首先搭建MySQL的主从复制在Master开启binlog,创建复制帐号,然后在Slave输入命令   2016年7月25日   配置安装技巧:   ...

  7. 黑马程序员_Java基础:反射机制(Reflection)总结

    ------- android培训.java培训.期待与您交流! ---------- 反射在java中有非常重大的意义,它是一种动态的相关机制,可以于运行时加载.探知.使用编译期间完全未知的clas ...

  8. DIY操作系统(引文)

    构想根据<30天自制操作系统>以及<ORANGE’S:一个操作系统的实现>初步实现一个操作系统. 先开个头,以后的棋一步一步下.

  9. [UCSD白板题] Covering Segments by Points

    Problem Introduction You are given a set of segments on a line and your goal is to mark as few point ...

  10. 在Windows 8.1及IE 11中如何使用HttpWatch

    提示:HttpWatch现已更新至v9.1.8,HttpWatch v9.1及以上的版本现都已支持Windows 7,8,8.1和IE 11. 如果你的HttpWatch专业版授权秘钥允许进入vers ...