`
天梯梦
  • 浏览: 13732456 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

jQuery:从零开始,DIY一个jQuery(1)

 
阅读更多

从本篇开始会陪大家一起从零开始走一遍 jQuery 的奇妙旅途,在整个系列的实践中,我们会把 jQuery 的主要功能模块都了解和实现一遍。

 

这会是一段很长的历程,但也会很有意思 —— 作为前端领域的经典之作,jQuery 里有着太多奇思妙想,如果能够深入理解它,对于我们稳固js基础、提升前端大法技能来说大有裨益。

 

另外,本系列的相关代码均可以从 我的github 上获取到(DIY-A-jQuery-master)。

 

1. 免 new 实现

我们在使用很多插件的时候,都需要使用 new XXX() 的写法来实例化一个引用:

var list = new Slip(document.getElementById('slip'), {
  //options
});

 

jQuery 同样作为一个面向对象的工具库,在我们创建一个实例时却无需使用 new 语法,节省了一些代码量:

var $div = $('div');
//不需要如下写法:
//var $div = new $('div');

 

这种便捷的形式依赖了工厂模式,其实现非常简单,把 new 封装在库内即可,让每次调用 jQuery() 时自行在内部进行一次实例化:

(function() {
    var _jQuery = window.jQuery,
        _$ = window.$;

    var version = "0.0.1",
        jQuery = function(selector) {
            console.log(document.querySelector(selector))
        };

    jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };

    window.$ = window.jQuery = function(selector) {
        return new jQuery(selector);  //notice here~
    };
})();

 

留意这里我们走的 IIFE 形式,让 jQuery 代码库形成自己的作用域,避免污染外部变量。

于是乎以上就是咱写的第一个 JQ 雏形,简单跑一下:

<div></div>
<script>
    var $div = $('div');  //<div></div>
    console.log($div.jquery);  //0.0.1
</script>

 

别忘了后续我们还希望能通过 $.extend / $.fn.extend 来扩展 JQ 的静态方法和原型方法,我们把出口方法抽出来增加这个 extend 的API:

function Factory(selector){  //抽出构造函数
        return new jQuery(selector);
    }

    Factory.fn = jQuery.prototype;

    Factory.extend = Factory.fn.extend = function(){
        console.log(this)
    };

    window.$ = window.jQuery = init;

 

这样我们也能直接通过 $.fn.jquery 来获取当前 JQ 版本号了。

 

如果希望可以通过 $.prototype 直接访问 jQuery 的原型对象,再修改下这句代码即可:

Factory.prototype = Factory.fn = jQuery.prototype;

 

 

2. 写法优化

事实上我们不太喜欢再写多一个冗余的 Factory 构造函数来作为 window.jQuery 的引用,也不喜欢(在模块内部)使用 Factory.extend() 来扩展 JQ,它听起来和 JQ 没有半毛钱关系。

 

如果可以,直接把 jQuery 方法作为接口输出,且在模块内部能以 jQuery.extend()  的形式来调用扩展接口,这样的形式更佳。

 

也就是说我们希望代码应该是这样写的:

    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };
    window.jQuery = window.$ = jQuery;

 

“直接把 jQuery 方法作为接口输出”意味着我们要把工厂模式挪入 jQuery 方法中,显然我们不能这样改:

    var version = "0.0.1",
        jQuery = function (selector) {
            return new jQuery(selector);
        };

 

这样死循环了,调用栈会直接爆掉~

 

于是我们可以抽出一个 init 方法来做初始化处理(比如简单地注入检索到的元素到JQ对象中),把 jQuery 方法中的内容更改为 return new init(selector) 就行了。

 

保证两个前提:

1. this 指向 jQuery 上下文

2. 其原型指向 jQuery 的原型

 

 

第一点很好理解,方便我们直接在 init 方法中通过对 this 的操作来处理 JQ 实例上下文,如:

  //注入元素到 JQ 实例对象中
  this[0] = elem;
  this.length = 1;

 

 

针对这点,我们不妨把 init 作为 jQuery.prototype 的属性方法来实现:

var version = "0.0.1",
        jQuery = function(selector) {
            return new jQuery.fn.init()  //修改点1
        };

    //方便我们使用 jQuery.fn 来引用 jQuery 原型对象
    jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };

     //修改点2 —— init 作为原型方法,确保 this 指向正确
    jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };

    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };

    window.$ = window.jQuery = jQuery;

 

 

然而这时候存在一个问题 —— JQ实例对象无法访问原型属性/方法:

    var $div = $('div');
    console.log($div.jquery);  //undefined

 

 

原因很简单——我们还未实现上述提及的第二个前提——“init 原型指向 jQuery 的原型”

 

在 js 中,实例的内部原型(__proto__)总是指向其构造函数的原型(prototype),而经过我们这番修改,JQ实例的构造函数已经变成了 jQuery.fn.init ,而其原型并非指向 jQuery 的原型,这导致 JQ 实例无法顺其原型链爬取到 jQuery.prototype。

 

要实现这个条件,只需要做小小改动——把 jQuery.fn.init 的原型指向 jQuery 的原型(jQuery.prototype / jQuery.fn)即可:

var init = jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };

    init.prototype = jQuery.fn;  //修改点

 

 

这里贴下完整代码:

var version = "0.0.1",
        jQuery = function(selector) {
            return new jQuery.fn.init(selector)
        };

    jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };

    var init = jQuery.fn.init = function( selector, context, root ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };

    init.prototype = jQuery.fn;

    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };

    window.$ = window.jQuery = jQuery;

 

 

 

3. 链式写法实现

JQ 里一个很大的亮点是,它支持链式写法,调用起来非常方便:

$('div').removeClass('hide').css('width', '100px')

 

 

其实现其实非常简单 —— 确保每个调用的方法尾部均返回自身即可,这里我们新增两个实例方法做示例:

jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery,
        setBackground: function(){
            this[0].style.background = 'yellow';
            return this  //返回自身引用
        },
        setColor: function(){
            this[0].style.color = 'blue';
            return this  //返回自身引用
        }
    };

    var init = jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return this; 
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
            return this;
        }
    };

 

 

链式调用:

<div>hello world</div>

<script>
    var $div = $('div');
    $div.setBackground().setColor();
</script>

 

 

效果如下,杠杠的:

jQuery:从零开始,DIY一个jQuery(1)

 

4. 冲突处理

存在某些情况,用户可能并不想拿 window.$ 甚至 window.jQuery 来引用 JQ 接口,或者已经有其它库使用了 window.$ 这个变量,如果我们粗暴地改变其引用肯定是不合理的。

 

so 我们来实现 JQ 中冲突处理的静态接口 jQuery.noConflict,这意味着在代码段开始时,就得先保存下当前 window.$ 和 window.jQuery 两个变量:

(function(){
    var _jQuery = window.jQuery,
        _$ = window.$;

    //var version = "0.0.1"......
})()

 

 

然后是实现 noConflict 方法,退耕还林,把保存的变量吐回去即可:

(function(){
    var _jQuery = window.jQuery,
        _$ = window.$;

    //var version = "0.0.1"......

    jQuery.noConflict = function( deep ) {
        //确保window.$没有再次被改写
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

        //确保window.jQuery没有再次被改写
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }

        return jQuery;  //返回 jQuery 接口引用
    };

    window.jQuery = window.$ = jQuery;
})();

 

 

deep 参数类型为 Boolean,若为真,表示要求连window.jQuery 变量都需要吐回去。

留意在尾部我们返回了 jQuery 的接口引用,这意味着我们可以以

var $$$ = jQuery.noConflict()

 

 

的形式来把它赋予新的变量。

 

接着在外部运行如下代码:

<head>
    <meta charset="UTF-8">
    <title>DIY A JQ</title>
    <script>
        $ = 'old $';
        jQuery = 'old JQ'
    </script>
    <script src="jQuery.js"></script>
</head>
<body>

<div>hello world</div>

<script>
    var $div = $('div');
    $div.setBackground().setColor();

    var $$$ = $.noConflict(true);
    console.log($); 
    console.log(jQuery); 
    console.log($$$); 
</script>

 

 

输出如下:

jQuery:从零开始,DIY一个jQuery(1)

 

第一篇就写到这里,相关的代码可以从 我的github 上下载(DIY-A-jQuery-master)到。

下次我们会试着实现模块化的写法,并与时俱进,改用 ES6解构赋值语法 + Rollup 来进行打包以减少可能存在的冗余代码段。

共勉~

 

转自:http://www.cnblogs.com/vajoy/p/5510743.html

 

更多参考:

jQuery: 操作select option方法集合

jQuery: 插件开发模式详解 $.extend(), $.fn, $.widget()

AngularJS jQuery 共存法则

 

下一篇:jQuery:从零开始,DIY一个jQuery(2)

 

本文转自: jQuery:从零开始,DIY一个jQuery(1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    从零开始学jquery

    【从零开始学jQuery】这个教程旨在帮助初学者全面理解并掌握jQuery这一强大的JavaScript库。jQuery简化了HTML文档遍历、事件处理、动画设计和Ajax交互,是Web开发中的常用工具。 ### (一) 开天辟地入门篇 在入门...

    从零开始学JQuery.pdf

    从零开始学JQuery

    jQueryEasyUI从零开始学源码part1

    本教程“jQueryEasyUI从零开始学源码part1”旨在帮助初学者深入理解jQuery EasyUI的工作原理,通过源码分析来提升开发技能。 首先,我们需要了解jQuery EasyUI的基础概念。jQuery是一个轻量级的JavaScript库,简化...

    从零开始学习jQuery

    ### 从零开始学习jQuery——综合知识点解析 #### 一、引言 随着Web技术的发展,JavaScript已经成为前端开发中不可或缺的一部分。为了简化JavaScript的使用,提高开发效率,jQuery应运而生。本文将从零开始,详细...

    Ajax入门:从零开始学习jQuery pdf

    从零开始学习jQuery pdf,第一篇:开天辟地入门篇;第二篇:jQuery最重要的部分——万能的选择器;第三章:管理Jquery包装集;第四章:使用jQuery操作元素的属性与样式;第五篇:事件与事件对象……  通过一步步的...

    Beginning jQuery:From the Basics of jQuery to Writing your Own Plug-ins

    这本书旨在帮助读者从零基础开始,逐步掌握jQuery的核心概念,并进阶到能够独立编写自己的jQuery插件。在深入探讨这个强大的JavaScript库的过程中,作者不仅提供了理论知识,还附带了丰富的源代码示例,使得学习过程...

    从零开始学习jQuery pdf

    从零开始学习jQuery pdf,第一篇:开天辟地入门篇;第二篇:jQuery最重要的部分——万能的选择器;第三章:管理Jquery包装集;第四章:使用jQuery操作元素的属性与样式;第五篇:事件与事件对象……

    《jQuery: Novice to Ninja》- 2017 英文原版

    jQuery: Novice to Ninja is your fast track to mastering jQuery—the all-conquering JavaScript framework. Used by over half the world’s top 10,000 websites, jQuery is the fastest, most efficient way ...

    从零开始学习JQuery

    每篇文章都旨在帮助读者从零开始,逐步深入学习和掌握jQuery的各种使用技巧。 ### 开发环境搭建 学习jQuery的第一步是搭建开发环境。在Visual Studio中可以方便地配合使用jQuery,可以通过包管理器如NuGet安装...

    从零开始学习jQuery系列教程

    本系列文章将带您进入jQuery的精彩世界, 其中有很多作者...本篇文章是入门第一篇, 主要是简单介绍jQuery, 通过简单示例指导大家如何编写jQuery代码以及搭建开发环境. 详细讲解了如何在Visual Studio中配合使用jQuery.

    JQuery教程-从零开始学习jQuery

    ### JQuery教程:从零开始学习JQuery #### 开天辟地入门篇 ##### 摘要 本篇文章作为系列教程的开端,旨在为初学者提供一个全面了解JQuery的起点。无论你是刚接触编程的新手还是已经有了一定前端经验的开发者,都...

    jQuery从零开始

    这个“jQuery从零开始”的教程将带你逐步了解和掌握jQuery的核心概念和实用技巧。 ### 1. jQuery基础知识 - **选择器**:jQuery的选择器类似于CSS,用于选取页面上的HTML元素。例如,`$("#id")` 选择ID为"id"的...

    从零开始学习jquery教程

    从零开始学习jquery教程,非常值得学习

    jQuery使用手册 jquery入门教程

    从零开始学习jQuery (一) 开天辟地入门篇 从零开始学习jQuery (二) 万能的选择器 从零开始学习jQuery (三) 管理jQuery 包装集 从零开始学习jQuery (四) 使用jQuery 操作元素的属性与样式 从零开始学习jQuery (五) ...

Global site tag (gtag.js) - Google Analytics