jQuery中extend函数的实现原理详解


Posted in Javascript onFebruary 03, 2015

extend()是jQuery中一个重要的函数,作用是实现对对象的扩展, 它经常用于jQuery插件的开发,jQuery内部也使用它来扩展属性方法,如上篇文章中讲到的noConflict方法,就是用extend方法来扩展的。

jQuery中extend函数的实现原理详解

在jQuery的API手册中,我们看到,extend实际上是挂载在jQuery和jQuery.fn上的两个不同方法,尽管在jQuery内部jQuery.extend()和jQuery.fn.extend()是用相同的代码实现的,但是它们的功能却不太一样。来看一下官方API对extend的解释:

jQuery.extend(): Merge the contents of two or more objects together into the first object.(把两个或者更多的对象合并到第一个当中)

jQuery.fn.extend():Merge the contents of an object onto the jQuery prototype to provide new jQuery instance methods.(把对象挂载到jQuery的prototype属性,来扩展一个新的jQuery实例方法)

我们知道,jQuery有静态方法和实例方法之分, 那么jQuery.extend()和jQuery.fn.extend()的第一个区别就是一个用来扩展静态方法,一个用来扩展实例方法。用法如下:

jQuery.extend({
 sayhello:function(){
 console.log("Hello,This is jQuery Library");
 }
})
$.sayhello(); //Hello, This is jQuery Library
jQuery.fn.extend({
 check: function() {
 return this.each(function() {
 this.checked = true;
 });
 },
 uncheck: function() {
 return this.each(function() {
 this.checked = false;
 });
 }
})
$( "input[type='checkbox']" ).check(); //所有的checkbox都会被选择

注意两种调用插件的方式,一种是直接用$调用,另外一种是用$()调用,另外jQuery.extend()接收多个对象作为参数,如果只有一个参数,则把这个对象的属性方法附加到jQuery上,如果含有多个参数,则把后面的对象的属性和方法附加到第一个对象上。jQuery extend的实现源码:

jQuery.extend = jQuery.fn.extend = function() {
 var options, name, src, copy, copyIsArray, clone,
 target = arguments[0] || {},
 i = 1,
 length = arguments.length,
 deep = false;
 // Handle a deep copy situation
 if ( typeof target === "boolean" ) {
 deep = target;
 target = arguments[1] || {};
 // skip the boolean and the target
 i = 2;
 }
 // Handle case when target is a string or something (possible in deep copy)
 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
 target = {};
 }
 // extend jQuery itself if only one argument is passed
 if ( length === i ) {
 target = this;
 --i;
 }
 for ( ; i < length; i++ ) {
 // Only deal with non-null/undefined values
 if ( (options = arguments[ i ]) != null ) {
 // Extend the base object
 for ( name in options ) {
 src = target[ name ];
 copy = options[ name ];
 // Prevent never-ending loop
 if ( target === copy ) {
  continue;
 }
 // Recurse if we're merging plain objects or arrays
 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
  if ( copyIsArray ) {
  copyIsArray = false;
  clone = src && jQuery.isArray(src) ? src : [];
  } else {
  clone = src && jQuery.isPlainObject(src) ? src : {};
  }
  // Never move original objects, clone them
  target[ name ] = jQuery.extend( deep, clone, copy );
 // Don't bring in undefined values
 } else if ( copy !== undefined ) {
  target[ name ] = copy;
 }
 }
 }
 }
 // Return the modified object
 return target;
};

很大一堆代码,乍看起来难以理解,其实代码的大部分都是用来实现jQuery.extend()中有多个参数时的对象合并,深度拷贝问题,如果去掉这些功能,让extend只有扩展静态和实例方法的功能,那么代码如下:

jQuery.extend = jQuery.fn.extend = function(obj){
 //obj是传递过来扩展到this上的对象
 var target=this;
 for (var name in obj){
 //name为对象属性
 //copy为属性值
 copy=obj[name];
 //防止循环调用
 if(target === copy) continue;
 //防止附加未定义值
 if(typeof copy === 'undefined') continue;
 //赋值
 target[name]=copy;
 }
 return target;
}

下面再来对extend方法进行注释解释:

jQuery.extend = jQuery.fn.extend = function() {
 // 定义默认参数和变量
 // 对象分为扩展对象和被扩展的对象 
 //options 代表扩展的对象中的方法
 //name 代表扩展对象的方法名
 //i 为扩展对象参数起始值
 //deep 默认为浅复制
 var options, name, src, copy, copyIsArray, clone,
 target = arguments[0] || {},
 i = 1,
 length = arguments.length,
 deep = false;
 //当第一个参数为布尔类型是,次参数定义是否为深拷贝
 //对接下来的参数进行处理
 if ( typeof target === "boolean" ) {
 deep = target;
 target = arguments[1] || {};
 // 当定义是否深拷贝时,参数往后移动一位
 i = 2;
 }
 // 如果要扩展的不是对象或者函数,则定义要扩展的对象为空
 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
 target = {};
 }
 // 当只含有一个参数时,被扩展的对象是jQuery或jQuery.fn
 if ( length === i ) {
 target = this;
 --i;
 }
 //对从i开始的多个参数进行遍历
 for ( ; i < length; i++ ) {
 // 只处理有定义的值
 if ( (options = arguments[ i ]) != null ) {
 // 展开扩展对象
 for ( name in options ) {
 src = target[ name ];
 copy = options[ name ];
 // 防止循环引用
 if ( target === copy ) {
  continue;
 }
 // 递归处理深拷贝
 if ( deep && copy &&; ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
  if ( copyIsArray ) {
  copyIsArray = false;
  clone = src && jQuery.isArray(src) ? src : [];
  } else {
  clone = src && jQuery.isPlainObject(src) ? src : {};
  }
  target[ name ] = jQuery.extend( deep, clone, copy );
 // 不处理未定义值
 } else if ( copy !== undefined ) {
  //给target增加属性或方法
  target[ name ] = copy;
 }
 }
 }
 }
 //返回
 return target;
};

弄懂了jQuery扩展的原理,相信以后再也不用为编写jQuery插件而烦恼了。

Javascript 相关文章推荐
Javascript 各浏览器的 Javascript 效率对比
Jan 23 Javascript
jquery左右滚动焦点图banner图片鼠标经过显示上下页按钮
Oct 11 Javascript
深入理解JQuery keyUp和keyDown的区别
Dec 12 Javascript
jquery文档操作wrap()方法实例简述
Jan 10 Javascript
基于Bootstrap里面的Button dropdown打造自定义select
May 30 Javascript
json与jsonp知识小结(推荐)
Aug 16 Javascript
全面解析node 表单的图片上传
Nov 21 Javascript
jQuery Json数据格式排版高亮插件json-viewer.js使用方法详解
Jun 12 jQuery
vue 2.8.2版本配置刚进入时候的默认页面方法
Sep 21 Javascript
Vue 刷新当前路由的实现代码
Sep 26 Javascript
vue中实现高德定位功能
Dec 03 Javascript
antd vue 刷新保留当前页面路由,保留选中菜单,保留menu选中操作
Aug 06 Javascript
jQuery中noconflict函数的实现原理分解
Feb 03 #Javascript
jQuery中的pushStack实现原理和应用实例
Feb 03 #Javascript
JavaScript闭包详解
Feb 02 #Javascript
js实现浏览器窗口大小被改变时触发事件的方法
Feb 02 #Javascript
javascript的switch用法注意事项分析
Feb 02 #Javascript
jQuery实现长按按钮触发事件的方法
Feb 02 #Javascript
jQuery实现跟随鼠标运动图层效果的方法
Feb 02 #Javascript
You might like
php数组函数序列之ksort()对数组的元素键名进行升序排序,保持索引关系
2011/11/02 PHP
解析php中static,const与define的使用区别
2013/06/18 PHP
PHP中auto_prepend_file与auto_append_file用法实例分析
2014/09/22 PHP
Laravel5中contracts详解
2015/03/02 PHP
laravel 5.3 单用户登录简单实现方法
2019/10/14 PHP
PHP常用header头定义代码示例汇总
2020/08/29 PHP
document.getElementById方法在Firefox与IE中的区别
2010/05/18 Javascript
JavaScript高级程序设计 学习笔记 js高级技巧
2011/09/20 Javascript
jQuery弹性滑动导航菜单实现思路及代码
2013/05/02 Javascript
struts2+jquery组合验证注册用户是否存在
2014/04/30 Javascript
JavaScript indexOf方法入门实例(计算指定字符在字符串中首次出现的位置)
2014/10/17 Javascript
js实现简单的联动菜单效果
2015/08/19 Javascript
BootStrap tab选项卡使用小结
2020/08/09 Javascript
BootStrap表单控件之复选框checkbox和单选择按钮radio
2017/05/23 Javascript
vue脚手架vue-cli的学习使用教程
2017/06/06 Javascript
Angular PWA使用的Demo示例
2019/01/31 Javascript
JavaScript数组去重的方法总结【12种方法,号称史上最全】
2019/02/28 Javascript
[56:18]VGJ.S vs Secret 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python中import导入上一级目录模块及循环import问题的解决
2016/06/04 Python
python的concat等多种用法详解
2018/11/28 Python
python中有关时间日期格式转换问题
2019/12/25 Python
django-csrf使用和禁用方式
2020/03/13 Python
Python matplotlib可视化实例解析
2020/06/01 Python
Python 跨.py文件调用自定义函数说明
2020/06/01 Python
基于Python组装jmx并调用JMeter实现压力测试
2020/11/03 Python
python3从网络摄像机解析mjpeg http流的示例
2020/11/13 Python
HTML5和以前HTML4的区别整理
2013/10/20 HTML / CSS
NUK奶瓶美国官网:NUK美国
2016/09/26 全球购物
哥德堡通行证:Gothenburg Pass
2019/12/09 全球购物
如何判断一段程序是由C 编译程序还是由C++编译程序编译的
2013/08/04 面试题
售后客服工作职责
2014/06/16 职场文书
无刑事犯罪记录证明
2014/09/18 职场文书
教师自查自纠工作情况报告
2014/10/29 职场文书
幼儿园辞职书
2015/02/26 职场文书
2016重阳节红领巾广播稿
2015/12/18 职场文书
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python