首页
学习
活动
专区
圈层
工具
发布

jQuery原理(入口函数)

基本结构

jQuery的基本结构如下:

代码语言:txt
复制
(function (window, undefiend) {
    var jQuery = function () {
        return new jQuery.fn.init()
    }
    jQuery.prototype = {
        constructor: jQuery
    }

    jQuery.fn.init.prototype = jQuery.prototype
    window.jQuery = window.$ = jQuery
})(window);
/* 其中fn指代的是原型,因此以上结构可以转换成以下结构 */
(function (window, undefiend) {
    var jQuery = function () {
        return new kjQuery.prototype.init();
    };
    jQuery.prototype = {
        constructor: jQuery,
    };

    jQuery.prototype.init.prototype = jQuery.prototype;

    window.jQuery = window.$ = jQuery;
})(window);
  1. jQuery的本质是一个闭包

为了避免多个框架的冲突

  1. jQuery如何让外界访问内部定义的局部变量
代码语言:txt
复制

windows.xx = xxx

代码语言:txt
复制
  1. jQuery为什么要给自己传递一个window参数
  • 为了方便后期压缩代码
  • 为了提升查找的效率
  1. jQuery为什么要给自己传递一个undefiend参数
    • 为了方便后期压缩代码
    • IE9以下的浏览器undefined可以被修改,为了保证内部使用的undefined不被修改,所以需要接收一个正确的undefined

入口函数测试

  1. 传入 '' null undefined NaN 0 false

返回空的jQuery对象

代码语言:txt
复制

console.log($())

console.log($(''))

console.log($(null))

console.log($(undefined))

console.log($(NaN))

console.log($(0))

console.log($(false))

代码语言:txt
复制
image-20200621153915653
  1. 字符串
  • 代码片段
代码语言:txt
复制
 会将创建好的DOM元素存储到jQuery对象中返回
代码语言:txt
复制
 ```javascript
代码语言:txt
复制
 console.log($('<p>1</p><p>2</p><p>3</p>'));
代码语言:txt
复制
 ```
代码语言:txt
复制
 ![image-20200621155130041](https://cdn.jsdelivr.net/gh/blogimg/HexoStaticFile2@latest/2020/06/21/a6a0ea7c8c2c10ed73b680c05b233104.png)
  • 选择器
代码语言:txt
复制
 会将找到的所有元素存储到jQuery对象中返回
代码语言:txt
复制
 ```javascript
代码语言:txt
复制
 console.log($('li'));
代码语言:txt
复制
 ```
代码语言:txt
复制
 ![image-20200621155220061](https://cdn.jsdelivr.net/gh/blogimg/HexoStaticFile2@latest/2020/06/21/dad40be6fbb0f9fde03f86b1fc55957d.png)
  1. 数组

会将数组中存储的元素依次存储到jQuery对象中立返回

代码语言:txt
复制

var arr = 1, 2, 3, 4, 5, 6;

console.log($(arr));

var likeArr = { 0: "lnj", 1: "33", 2: "male", length: 3 };

console.log($(likeArr));

代码语言:txt
复制
image-20200621155259905
  1. 其他类型(对象、DOM元素、基本数据类型等)

会将传入的数据存储到jQuery对象中返回

代码语言:txt
复制

function Person() { }

console.log($(new Person()));

console.log($(document.createElement('div')));

console.log($(123));

console.log($(true));

代码语言:txt
复制
image-20200621155438227

入口函数-代码实现

接收参数的实现:在创建时接收一个参数,并且传递给init即可。这样init就可以接收这个参数。

代码语言:txt
复制
var kjQuery = function (selector) {
    return new kjQuery.prototype.init(selector);
};

接下来的方法只需要在init中定义即可。

代码语言:txt
复制
kjQuery.prototype = {
    constructor: kjQuery,
    init: function (selector) {

    },
};
  1. 传入 '' null undefined NaN 0 false
代码语言:txt
复制

if (!selector) {

代码语言:txt
复制
   return this;

}

代码语言:txt
复制
  1. 处理函数

判断是否为函数,如果是则将传入的参数作为ready的回调函数传入。

代码语言:txt
复制

isFunction: function (selector) {

代码语言:txt
复制
   return typeof selector === "function";

},

代码语言:txt
复制
代码语言:txt
复制

// 2. 方法处理

else if (kjQuery.isFunction(selector)) {

代码语言:txt
复制
   kjQuery.ready(selector);

}

代码语言:txt
复制

ready函数中需要判断页面是否加载,由于IE不支持addEvem=ntListener方法添加事件,因此判断页面是否加载只能使用readyState判断页面加载状态。当页面加载完成后在进行添加事件的操作。

代码语言:txt
复制

ready: function (fn) {

代码语言:txt
复制
   // 判断DOM是否加载完毕
代码语言:txt
复制
   if (document.readyState == "complete") {
代码语言:txt
复制
       fn();
代码语言:txt
复制
       //   判断是否含有addEventListener方法
代码语言:txt
复制
   } else if (document.addEventListener) {
代码语言:txt
复制
       document.addEventListener("DOMContentLoaded", function () {
代码语言:txt
复制
           fn();
代码语言:txt
复制
       });
代码语言:txt
复制
   } else {
代码语言:txt
复制
       //   IE兼容
代码语言:txt
复制
       document.attachEvent("onreadystatechange", function () {
代码语言:txt
复制
           if ((document.readyState = "complete")) {
代码语言:txt
复制
               fn();
代码语言:txt
复制
           }
代码语言:txt
复制
       });
代码语言:txt
复制
   }

},

代码语言:txt
复制
  1. 字符串

在这里需要判断是否是字符串并且需要去掉字符串两端的空格。但判断是否为字符串后边可能也会用到,因此定义一个静态方法,通过类名调用。方便后边的操作

代码语言:txt
复制

kjQuery.isString = function (str) {

代码语言:txt
复制
   return typeof str === "string";

};

代码语言:txt
复制

去掉两端的空格在传入时去掉即可。同样定义一个静态方法

代码语言:txt
复制

// 0 去除字符串两端的空格

selector = kjQuery.trim(selector);

kjQuery.trim = function (str) {

代码语言:txt
复制
   if (!kjQuery.isString(str)) {
代码语言:txt
复制
       return str;
代码语言:txt
复制
   }
代码语言:txt
复制
   if (str.trim) {
代码语言:txt
复制
       return str.trim();
代码语言:txt
复制
   } else {
代码语言:txt
复制
       // IE兼容处理方案
代码语言:txt
复制
       return str.replace(/^\s+|\s+$/g, "");
代码语言:txt
复制
   }

};

代码语言:txt
复制
  • HTML代码
代码语言:txt
复制
 判断是否为HTML与判断字符串一样,后边也可能会用到,因此定义一个静态方法。
代码语言:txt
复制
 ```javascript
代码语言:txt
复制
 kjQuery.isHTML = function (str) {
代码语言:txt
复制
     return (
代码语言:txt
复制
         str.charAt(0) == "<" &&
代码语言:txt
复制
         str.charAt(str.length - 1) == ">" &&
代码语言:txt
复制
         str.length >= 3
代码语言:txt
复制
     );
代码语言:txt
复制
 };
代码语言:txt
复制
 ```
代码语言:txt
复制
 ```javascript
代码语言:txt
复制
 else if (kjQuery.isString(selector)) {
代码语言:txt
复制
     // 2.1 判断是否是代码片段
代码语言:txt
复制
     if (kjQuery.isHTML(selector)) {
代码语言:txt
复制
         // 1. 根据代码片段创建所有的元素
代码语言:txt
复制
         var temp = document.createElement("div");
代码语言:txt
复制
         temp.innerHTML = selector;
代码语言:txt
复制
         // 2. 创建好的一级元素添加到对象
代码语言:txt
复制
         for (var i = 0; i < temp.children.length; i++) {
代码语言:txt
复制
             this[i] = temp.children[i];
代码语言:txt
复制
         }
代码语言:txt
复制
         // 3. 给jQuery对象添加length属性
代码语言:txt
复制
         this.length = temp.children.length;
代码语言:txt
复制
         // 4. 返回加工好的this(jQuery)
代码语言:txt
复制
         return this;
代码语言:txt
复制
     }
代码语言:txt
复制
     // 2.2 判断是否是选择器
代码语言:txt
复制
 }
代码语言:txt
复制
 ```
代码语言:txt
复制
 > 其中第二三步可以修改为如下写法:
 >
代码语言:txt
复制
 > ```javascript
 > [].push.apply(this, temp.children);
 > ```
 >
代码语言:txt
复制
 > 通过apply改变push的this,这样就不会push到数组里,而是push到this(此时为kjQuery对象)中。
  • 选择器
代码语言:txt
复制
 ```javascript
代码语言:txt
复制
 // 2.2 判断是否是选择器
代码语言:txt
复制
 else {
代码语言:txt
复制
     // 1. 根据传入的选择器找到对应的元素
代码语言:txt
复制
     var res = document.querySelectorAll(selector);
代码语言:txt
复制
     // 2. 将找到的元素添加到kjQuery
代码语言:txt
复制
     [].push.apply(this, res);
代码语言:txt
复制
     // 3. 返回加工好的this
代码语言:txt
复制
     return this;
代码语言:txt
复制
 }
代码语言:txt
复制
 ```
  1. 数组

注意:但凡将自定义数组转换为真数组或伪数组都先转换为真数组

代码语言:txt
复制

else if (

代码语言:txt
复制
   typeof selector === "object" &&
代码语言:txt
复制
   "length" in selector &&
代码语言:txt
复制
   selector != window

) {

代码语言:txt
复制
   //   3.1 真数组
代码语言:txt
复制
   if ({}.toString.apply(selector) == "[object Array]") {
代码语言:txt
复制
       [].push.apply(this, selector);
代码语言:txt
复制
       return this;
代码语言:txt
复制
   }
代码语言:txt
复制
   //   3.2 伪数组
代码语言:txt
复制
   else {
代码语言:txt
复制
       // 将自定义的伪数组转换成真数组
代码语言:txt
复制
       var arr = [].slice.call(selector);
代码语言:txt
复制
       [].push.apply(this, arr);
代码语言:txt
复制
       return this;
代码语言:txt
复制
   }

}

代码语言:txt
复制

以上代码可优化为如下:

新增静态方法

代码语言:txt
复制

kjQuery.isObject = function (selector) {

代码语言:txt
复制
   return typeof selector === "object";

};

kjQuery.isWindow = function (selector) {

代码语言:txt
复制
   return selector === window;

};

kjQuery.isArray = function (selector) {

代码语言:txt
复制
   if (
代码语言:txt
复制
       kjQuery.isObject(selector) &&
代码语言:txt
复制
       !kjQuery.isWindow(selector) &&
代码语言:txt
复制
       "length" in selector
代码语言:txt
复制
   ) {
代码语言:txt
复制
       return true;
代码语言:txt
复制
   }
代码语言:txt
复制
   return false;

};

代码语言:txt
复制
代码语言:txt
复制

else if (kjQuery.isArray(selector)) {

代码语言:txt
复制
   var arr = [].slice.call(selector);
代码语言:txt
复制
   [].push.apply(this, arr);
代码语言:txt
复制
   return this;

}

代码语言:txt
复制
  1. 其他类型(对象、DOM元素、基本数据类型等)
代码语言:txt
复制

else {

代码语言:txt
复制
   this[0] = selector;
代码语言:txt
复制
   this.length = 1;
代码语言:txt
复制
   return this;

}

代码语言:txt
复制

真伪数组转换

  1. 真数组转换为伪数组
代码语言:txt
复制

var obj = {},arr=[];

[].push.apply(obj,arr);

代码语言:txt
复制
  1. 伪数组转换为真数组
代码语言:txt
复制

var obj = {};

var arr = [].slice.call(obj)

代码语言:txt
复制

入口函数-extend方法

通过extend方法来为对象或类添加方法。

  1. 调用extend方法,传入一个对象。
代码语言:txt
复制

function kjQuery() { }

kjQuery.extend({

代码语言:txt
复制
   isTest: function () {
代码语言:txt
复制
       console.log('test');
代码语言:txt
复制
   }

})

代码语言:txt
复制
  1. extend方法的实现中,遍历传入的对象,并将值添加到类身上作为类的方法。
代码语言:txt
复制

/ 为类和对象添加方法 /

kjQuery.extend = kjQuery.prototype.extend = function (obj) {

代码语言:txt
复制
   for (var key in obj) {
代码语言:txt
复制
       this[key] = obj[key];
代码语言:txt
复制
   }

};

/*

/ 为类添加方法 /

kjQuery.extend = function (obj) {

代码语言:txt
复制
   // this指向kjQuery
代码语言:txt
复制
   console.log(this);
代码语言:txt
复制
   for (var key in obj) {
代码语言:txt
复制
       // kjQuery[key] = obj[key]
代码语言:txt
复制
       this[key] = obj[key]
代码语言:txt
复制
   }

}

/ 为对象添加方法 /

kjQuery.extend.prototype = function (obj) {

代码语言:txt
复制
   // this指向实例化对象
代码语言:txt
复制
   console.log(this);
代码语言:txt
复制
   for (var key in obj) {
代码语言:txt
复制
       // kjQuery[key] = obj[key]
代码语言:txt
复制
       this[key] = obj[key]
代码语言:txt
复制
   }

}

代码语言:txt
复制
   */
代码语言:txt
复制
  1. 通过类名调用方法
代码语言:txt
复制

kjQuery.isTest()

代码语言:txt
复制
image-20200621191222466
  1. 通过对象调用方法
代码语言:txt
复制

var k = new kjQuery();

k.extend({

代码语言:txt
复制
   isDemo: function () {
代码语言:txt
复制
       console.log('demo')
代码语言:txt
复制
   }

})

k.isDemo()

代码语言:txt
复制
image-20200621193649802

因此可以将之前定义的静态方法改为如下写法:

代码语言:txt
复制
kjQuery.extend = kjQuery.prototype.extend = function (obj) {
    for (var key in obj) {
        this[key] = obj[key];
    }
};
kjQuery.extend({
    isString: function (str) {
        return typeof str === "string";
    },
    isHTML: function (str) {
        return (
            str.charAt(0) == "<" &&
            str.charAt(str.length - 1) == ">" &&
            str.length >= 3
        );
    },
    trim: function (str) {
        if (!kjQuery.isString(str)) {
            return str;
        }
        if (str.trim) {
            return str.trim();
        } else {
            // IE兼容处理方案
            return str.replace(/^\s+|\s+$/g, "");
        }
    },
    isObject: function (selector) {
        return typeof selector === "object";
    },
    isWindow: function (selector) {
        return selector === window;
    },
    isArray: function (selector) {
        if (
            kjQuery.isObject(selector) &&
            !kjQuery.isWindow(selector) &&
            "length" in selector
        ) {
            return true;
        }
        return false;
    },
    isFunction: function (selector) {
        return typeof selector === "function";
    },
    ready: function (fn) {
        // 判断DOM是否加载完毕
        if (document.readyState == "complete") {
            fn();
            //   判断是否含有addEventListener方法
        } else if (document.addEventListener) {
            document.addEventListener("DOMContentLoaded", function () {
                fn();
            });
        } else {
            //   IE兼容
            document.attachEvent("onreadystatechange", function () {
                if ((document.readyState = "complete")) {
                    fn();
                }
            });
        }
    },
});

完整代码

至此入口函数部分编写已经完成,下边是工具库的全部代码。

代码语言:txt
复制
(function (window, undefiend) {
  var kjQuery = function (selector) {
    return new kjQuery.prototype.init(selector);
  };
  kjQuery.prototype = {
    constructor: kjQuery,
    init: function (selector) {
      /*
        
        
        代码片段:会将创建好的DOM元素存储到jQuery对象中返回
        选择器: 会将找到的所有元素存储到jQuery对象中返回
        3.数组:
        会将数组中存储的元素依次存储到jQuery对象中立返回
        4.除上述类型以外的:
        会将传入的数据存储到jQuery对象中返回
    */
      // 0 去除字符串两端的空格
      selector = kjQuery.trim(selector);
      // 1.传入 '' null undefined NaN  0  false, 返回空的jQuery对象
      if (!selector) {
        return this;
      }
      // 2. 方法处理
      else if (kjQuery.isFunction(selector)) {
        kjQuery.ready(selector);
      }
      // 3.字符串:
      else if (kjQuery.isString(selector)) {
        // 2.1 判断是否是代码片段
        if (kjQuery.isHTML(selector)) {
          // 1. 根据代码片段创建所有的元素
          var temp = document.createElement("div");
          temp.innerHTML = selector;
          //   // 2. 创建好的一级元素添加到对象
          //   for (var i = 0; i < temp.children.length; i++) {
          //     this[i] = temp.children[i];
          //   }
          //   // 3. 给jQuery对象添加length属性
          //   this.length = temp.children.length;
          [].push.apply(this, temp.children);

          // this是kjQuery
          // 4. 返回加工好的this(jQuery)
        }
        // 2.2 判断是否是选择器
        else {
          // 1. 根据传入的选择器找到对应的元素
          var res = document.querySelectorAll(selector);
          // 2. 将找到的元素添加到kjQuery
          [].push.apply(this, res);
          // 3. 返回加工好的this
        }
      }
      // 3 数组
      else if (kjQuery.isArray(selector)) {
        /*
        //   3.1 真数组
        if ({}.toString.apply(selector) == "[object Array]") {
          [].push.apply(this, selector);
          return this;
        }
        //   3.2 伪数组
        else {
          // 将自定义的伪数组转换成真数组
          var arr = [].slice.call(selector);
          [].push.apply(this, arr);
          return this;
          // 但凡将自定义数组转换为真数组或伪数组都先转换为真数组
        }
        */
        var arr = [].slice.call(selector);
        [].push.apply(this, arr);
      }
      // 4. 其他类型
      else {
        this[0] = selector;
        this.length = 1;
      }
      return this;
    },
  };
  kjQuery.extend = kjQuery.prototype.extend = function (obj) {
    for (var key in obj) {
      this[key] = obj[key];
    }
  };
  kjQuery.extend({
    isString: function (str) {
      return typeof str === "string";
    },
    isHTML: function (str) {
      return (
        str.charAt(0) == "<" &&
        str.charAt(str.length - 1) == ">" &&
        str.length >= 3
      );
    },
    trim: function (str) {
      if (!kjQuery.isString(str)) {
        return str;
      }
      if (str.trim) {
        return str.trim();
      } else {
        // IE兼容处理方案
        return str.replace(/^\s+|\s+$/g, "");
      }
    },
    isObject: function (selector) {
      return typeof selector === "object";
    },
    isWindow: function (selector) {
      return selector === window;
    },
    isArray: function (selector) {
      if (
        kjQuery.isObject(selector) &&
        !kjQuery.isWindow(selector) &&
        "length" in selector
      ) {
        return true;
      }
      return false;
    },
    isFunction: function (selector) {
      return typeof selector === "function";
    },
    ready: function (fn) {
      // 判断DOM是否加载完毕
      if (document.readyState == "complete") {
        fn();
        //   判断是否含有addEventListener方法
      } else if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", function () {
          fn();
        });
      } else {
        //   IE兼容
        document.attachEvent("onreadystatechange", function () {
          if ((document.readyState = "complete")) {
            fn();
          }
        });
      }
    },
  });
  kjQuery.prototype.init.prototype = kjQuery.prototype;

  window.kjQuery = window.$ = kjQuery;
})(window);
举报
领券