zoukankan      html  css  js  c++  java
  • 贫下中农版jQuery

    之前写过一篇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的冲突,多研究研究受益匪浅。

  • 相关阅读:
    selenium基础(鼠标和键盘事件)
    Java:面向对象三大特征
    Java:面向对象(上)
    Java:数组
    Java:方法
    Java基础:程序结构控制
    Java基础:用户交互Scanner
    Java基础语法(下)
    Jenkins(Extended E-mail Notification)邮箱配置正确但是并没有发送邮件
    接口自动化测试与Jenkins集成(Freestyle project任务版)
  • 原文地址:https://www.cnblogs.com/dolphinX/p/3270779.html
Copyright © 2011-2022 走看看