一:前言
主要是将jQuery里的实现框架提取出来了,常用用法都提取了一个例子,这里是$.ajax和$("#id").html(..)的例子,这里就直接上源码了,然后代码里加了自己的理解总结;
如果想了解jQuery实现原理的朋友可以参考看看;注意用ajax时跨域的问题,否则status老是0;
二:代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello world</title> </head> <script> // 这里省略很多的判断,只是做一个jQuery的实现演示 (function (global, factory) { factory(global); // TODO 引用此js脚本后立刻会执行的方法 })(window, function(global, noGlobal){ // TODO 此匿名方法是引入脚本后真正的实现逻辑所在; var version = "1.0", // jQuery版本 // TODO 重要部分,注意在jQuery(..)之前内部的jQuery.fn.init是可以的,只要在执行jQuery(..)时这些定义已经存在即可; jQuery = function (selector, context) { // TODO function了内部的实现可以先不考虑是否满足条件,因为它要在调用时浏览器才会判断这些定义是否存在,只需要在调用前做好这些工作即可 return new jQuery.fn.init(selector, context); }; // 结束var // 这句代码的执行肯定先于jQuery(..),因此上面的jQuery.fn不会报undefined // 通过JSON对象直接一次性给jQuery赋“实例”属性 jQuery.fn = jQuery.prototype = { jquery:version, constructor:jQuery, length:0 // 后面还有很多jQuery的“实例方法”定义,这里只是做demo就不演示了,感兴趣的可以自己看jQuery源码 } // TODO 通过后面执行extend方法来为jQuery声明“静态成员方法“如ajax jQuery.extend = jQuery.fn.extend = function () { // 这里暂且认为调用extend时的参数都是单个json对象或没有参数,target要么等于json对象要么是“空json对象” var options = arguments[0] || {}, // TODO 通过arguments来获取调用extend方法传来的参数 target = this, //TODO 就是jQuery类/方法对象或jQuery.fn(通过jQuery.extend(..)是定义静态成员,jQuery.fn.extend(..)是实例成员 name, copy; for(name in options){ copy = options[name]; // TODO 可能需要添加到jQuery类的“静态方法或变量” if(target === copy){ continue; } if(copy !== undefined) { target[name] = copy; //TODO 将调用extend方法中JSON对象里的如ajax:function...的ajax作为“静态成员方法”赋值给jQuery } } } var rootjQuery, idSelectorExpr = /^(?:#([w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { // TODO 用jQuery时一般不会主动用到后面两个参数,这里暂且不管 var elem, match; // 处理如: $(""), $(null), $(undefined)【包括了$()】, $(false) if ( !selector ) { console.log("不合法的调用方式"); return this; } if(typeof selector === "string"){ // TODO $("#id")/$(":type")等都会来这里,这里先只做$("#id")的情况 // 通过pattern来匹配selector,然后match就是匹配到的字符串,其中match[0]就是group(0),若第一个子表达式没有匹配到则match[1]为null match = idSelectorExpr.exec(selector); // $("#ids")得到match[1]为ids } if(match && !context){ elem = document.getElementById(match[1]); // 不用管match[1]是否有效 if(elem){ this[0] = elem; this.length = 1; } return this; // 返回new jQuery.fn.init(...)对象; } } init.prototype = jQuery.fn; // TODO 对fn的添加也会应用到init.prototype上,jQuery.fn.init某种程度上说就等价于jQuery // TODO 执行jQuery.extend方法来给jQuery定义静态成员 jQuery.extend({ ajax:function (url, options) { // 这里暂且规定用的时候就是$.ajax({..}); if(typeof url === "object"){ // TODO 说明$.ajax(..)时只传了一个参数且为JSON对象 options = url; url = undefined; } if(typeof url === "string" && typeof options === "undefined"){ console.error("不合法的使用"); } options = options || {}; // TODO 防止只有一个参数且为null // TODO 这里对ajax做简单实现,注意这个url和引用js或css时的路径一样当前路径就是当前网页的url地址,而根路径则是ip:port/ // JSON对象支持的属性:type/url/data/dataType/success/error/async/timeout/headers/beforeSend // TODO 这里暂且只支持XMLHttpRequest,type暂且只支持GET和POST var type = options["type"], url = options["url"], data = options["data"], dataType = options["dataType"], success = options["success"], error = options["error"], async = options["async"], timeout = options["timeout"], headers = options["headers"]; // TODO 进行一个简单的校验 if(type === "get" || type === "GET"){ data = null; } async = async || false; var xhr = new XMLHttpRequest(); xhr.open(type, url, async); // 通过请求行数据建立HTTP连接 if(dataType === undefined){ // TODO 设置header if(type === "post" || type === "POST"){ xhr.setRequestHeader("Content-Type", "application/octet-stream"); } }else{ headers.dataType = dataType; } headers = headers || {}; for(var name in headers){ xhr.setRequestHeader(name, headers[name]); } // 每次变化时都会触发 xhr.onreadystatechange = function () { if(xhr.readyState === 4){ // TODO 4表示request请求是完成状态(包括200/500/404等等) if(timer){ // timer只要在执行这个方法之前生成即可; clearTimeout(timer); // js原生方法,此时已经完成 } if(xhr.status === 200){ // 暂定认为只有200时才是success if(success){ success(xhr.responseText); // 暂定success的参数只能是响应体数据 } }else if(xhr.status >= 400 && xhr.status < 600){ // 暂定这个区间的状态码认为是error if(error){ error(); // 暂定error没有参数 } } } } // TODO 设置超时时间 var timer; if(typeof timeout === "number" && timeout > 0){ timer = setTimeout(function () { // setTimeout是js里的原生方法 if(xhr) { xhr.abort(); // 据说会重置readyState为0,当是暂不清楚时候也会触发onreadystatechange事件 } alert("HTTP请求超时"); }, timeout); } xhr.send(data); // TODO 通过继续发送请求头和请求体执行完整的HTTP请求 } }); // TODO 通过jQuery.fn.extend(..)给jQuery定义实例成员(注意jQuery.fn最终会赋值给jQuery.fn.init.prototype,这里实际上是给init对象创建实例成员 jQuery.fn.extend({ html:function (value) { var elem = this[0] || {}, // elem是通过$("#id")获得的元素 l = this.length; // TODO 这个this就是 new jQuery.fn.init(..)产生的init类对象 if(l < 1){ console.log("有bug"); } if(value === undefined){ return elem.innerHTML; }else{ elem.innerHTML = value; } } }); // TODO 使得外部可以直接$("#id")或$.ajax(..) if(!noGlobal){ window.jQuery = window.$ = jQuery; } }); </script> <body> <h1>Index page</h1> <p id="hh">uuuuu</p> <button onclick="(function () { $('#hh').html('ii999'); })()">调用修改$("id").html()</button> <button onclick="(function () { $.ajax({ url:'http://localhost:8090/test.html', type:'GET', async:true, success:function(data){ var msg = 'silentdoer' + data; alert(msg); } }); })()">调用ajax</button> </body> </html>