extend()函数是jQuery的基础函数之一,作用是扩展现有的对象

<script type="text/javascript" src="jquery-1.5.2.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = { x : { xxx : 'xxx', yyy : 'yyy' }, y : 'y' }; $.extend(true, obj1, obj2); alert(obj1.x.xxx); // 得到"xxx" obj2.x.xxx = 'zzz';
alert(obj2.x.xxx); // 得到"zzz"
alert(obj1.x.xxx); // 得带"xxx"
</script> 说明:
$.extend(true, obj1, obj2)表示以obj2中的属性扩展对象obj1,第一个参数设为true表示深复制。
虽然obj1中原来没有"x"属性,但经过扩展后,obj1不但具有了"x"属性,而且对obj2中的"x"属性的修改也不会影响到obj1中"x"属性的值,这就是所谓的“深复制”了。

浅复制的实现

如果仅仅需要实现浅复制,可以采用类似下面的写法:
$ = {
extend : function(target, options) {
for (name in options) {
target[name] = options[name];
}
return target;
}
}; 也就是简单地将options中的属性复制到target中。我们仍然可以用类似的代码进行测试,但得到的结果有所不同(假设我们的js命名为“jquery-extend.js”):
<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = { x : { xxx : 'xxx', yyy : 'yyy' }, y : 'y' };
$.extend(obj1, obj2);
alert(obj1.x.xxx); // 得到"xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx); // 得到"zzz"
alert(obj1.x.xxx); // 得带"zzz"
</script> obj1中具有了"x"属性,但这个属性是一个对象,对obj2中的"x"的修改也会影响到obj1,这可能会带来难以发现的错误。
深复制的实现

如果我们希望实现“深复制”,当所复制的对象是数组或者对象时,就应该递归调用extend。如下代码是“深复制”的简单实现:
$ = {
extend : function(deep, target, options) {
for (name in options) {
copy = options[name];
if (deep && copy instanceof Array) {
target[name] = $.extend(deep, [], copy);
} else if (deep && copy instanceof Object) {
target[name] = $.extend(deep, {}, copy);
} else {
target[name] = options[name];
}
}
return target;
}
};

具体分为三种情况:
1. 属性是数组时,则将target[name]初始化为空数组,然后递归调用extend;
2. 属性是对象时,则将target[name]初始化为空对象,然后递归调用extend;
3. 否则,直接复制属性。

测试代码如下:
<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = { x : { xxx : 'xxx', yyy : 'yyy' }, y : 'y' };
$.extend(true, obj1, obj2);
alert(obj1.x.xxx); // 得到"xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx); // 得到"zzz"
alert(obj1.x.xxx); // 得到"xxx"
</script>

现在如果指定为深复制的话,对obj2的修改将不会对obj1产生影响了;不过这个代码还存在一些问题,比如“instanceof Array”在IE5中可能存在不兼容的情况。jQuery中的实现实际上会更复杂一些。
更完整的实现

下面的实现与jQuery中的extend()会更接近一些:
$ = function() {
var copyIsArray,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty; class2type = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Object]' : 'object'
}, type = function(obj) {
return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
}, isWindow = function(obj) {
return obj && typeof obj === "object" && "setInterval" in obj;
}, isArray = Array.isArray || function(obj) {
return type(obj) === "array";
}, isPlainObject = function(obj) {
if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
return false;
} if (obj.constructor && !hasOwn.call(obj, "constructor")
&& !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
} var key;
for (key in obj) {
} return key === undefined || hasOwn.call(obj, key);
}, extend = function(deep, target, options) {
for (name in options) {
src = target[name];
copy = options[name]; if (target === copy) { continue; } if (deep && copy
&& (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && isArray(src) ? src : []; } else {
clone = src && isPlainObject(src) ? src : {};
} target[name] = extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
} return target;
}; return { extend : extend };
}();

首先是 $ =  function(){...}();这种写法,可以理解为与下面的写法类似:

func = function(){...};
$ =  func();

也就是立即执行函数,并将结果赋给$。这种写法可以利用function来管理作用域,避免局部变量或局部函数影响全局域。另外,我们只希望使用者调用$.extend(),而将内部实现的函数隐藏,因此最终返回的对象中只包含extend:

return { extend : extend };

接下来,我们看看extend函数与之前的区别,首先是多了这句话:

if (target === copy) { continue; }

这是为了避免无限循环,要复制的属性copy与target相同的话,也就是将“自己”复制为“自己的属性”,可能导致不可预料的循环。

然后是判断对象是否为数组的方式:

type = function(obj) {
        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
   },
   isArray = Array.isArray || function(obj) {
        return type(obj) === "array";
    }

如果浏览器有内置的Array.isArray 实现,就使用浏览器自身的实现方式,否则将对象转为String,看是否为"[object Array]"。
最后逐句地看看isPlainObject的实现:

if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
    return false;
}

如果定义了obj.nodeType,表示这是一个DOM元素;这句代码表示以下四种情况不进行深复制:
1. 对象为undefined;
2. 转为String时不是"[object Object]";
3. obj是一个DOM元素;
4. obj是window。
之所以不对DOM元素和window进行深复制,可能是因为它们包含的属性太多了;尤其是window对象,所有在全局域声明的变量都会是其属性,更不用说内置的属性了。

接下来是与构造函数相关的测试:

 if (obj.constructor && !hasOwn.call(obj, "constructor")
                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
        return false;
    }

如果对象具有构造函数,但却不是自身的属性,说明这个构造函数是通过prototye继承来的,这种情况也不进行深复制。这一点可以结合下面的代码结合进行理解:

var key;
        for (key in obj) {
        }
        return key === undefined || hasOwn.call(obj, key);

这几句代码是用于检查对象的属性是否都是自身的,因为遍历对象属性时,会先从自身的属性开始遍历,所以只需要检查最后的属性是否是自身的就可以了。
这说明如果对象是通过prototype方式继承了构造函数或者属性,则不对该对象进行深复制;这可能也是考虑到这类对象可能比较复杂,为了避免引入不确定的因素或者为复制大量属性而花费大量时间而进行的处理,从函数名也可以看出来,进行深复制的只有"PlainObject"。
如果我们用如下代码进行测试:

<script type="text/javascript" src="jquery-1.5.2.js"></script>
<script>
function O() {
this.yyy = 'yyy';
} function X() {
this.xxx = 'xxx';
} X.prototype = new O(); x = new X(); obj1 = { a : 'a', b : 'b' };
obj2 = { x : x };
$.extend(true, obj1, obj2); alert(obj1.x.yyy); // 得到"xxx"
obj2.x.yyy = 'zzz';
alert(obj1.x.yyy); // 得到"zzz"
</script>

可以看到,这种情况是不进行深复制的。
总之,jQuery中的extend()的实现方式,考虑了兼容浏览器的兼容,避免性能过低,和避免引入不可预料的错误等因素。
详细出处参考:http://www.jb51.net/article/39288.htm

初学JQuery笔记的更多相关文章

  1. jquery笔记之属性选择器 查找以某种条件开头的页面元素

    jquery笔记之属性选择器 查找以某种条件开头的页面元素 转载:http://www.blogbus.com/amyqiong-logs/78340326.html $("div[id]& ...

  2. Jquery笔记之第二天

    Jquery笔记之第二天 jQuery - 获取内容和属性 获得内容 - text().html() 以及 val() <script> $(document).ready(functio ...

  3. Jquery笔记和ajax笔记

    Jquery笔记:jQuery是一个JavaScript函数库,专为事件处理设计 1.jQuery的引入 <script text="type/javascript" src ...

  4. 前端:jQuery笔记

    前端:jQuery笔记 此系列文章乃是学习jQuery的学习笔记. Asp.net MVC Comet推送 摘要: 一.简介 在Asp.net MVC实现的Comet推送的原理很简单. 服务器端:接收 ...

  5. Python全栈之jQuery笔记

    jQuery runnoob网址: http://www.runoob.com/jquery/jquery-tutorial.html jQuery API手册: http://www.runoob. ...

  6. jQuery笔记之 Ajax回调地狱

    本次演示回调地狱: 模拟电影网站,判断用户是否为该网址的vip用户(最高权限为vip) 如果vpi就展示出vip电影,点击相应的电影显示出该电影的详细介绍 ---------------------- ...

  7. JQuery笔记汇总

    jQuery相关资料 官网: jQuery官网 在线API: jQuery在线API W3School:W3School-jQuery教程(中文版哦) 下载jQuery:jQuery各版本下载 jQu ...

  8. jquery笔记(基础知识)

    最近在学jquery,做点小笔记 语法: $(this).hide() - 隐藏当前元素 $("p").hide() - 隐藏所有 <p> 元素 $("p . ...

  9. 初学redux笔记,及一个最简单的redux实例

    categories: 笔记 tags: react redux 前端框架 把初学redux的一些笔记写了下来 分享一个入学redux很合适的demo, 用redux实现计数器 这是从阮一峰老师git ...

随机推荐

  1. Use Visual studio 2010 build Python2.7.10

    http://p-nand-q.com/python/building-python-27-with-vs2010.html

  2. NIO源码阅读

    自己对着源码敲一遍练习,写上注释.发现NIO编程难度好高啊..虽然很复杂,但是NIO编程的有点还是很多: 1.客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECTION等待后续结 ...

  3. bootstrap-提示标签、提示框

    提示标签: <body> <div class="container"> <div class="row"> <div ...

  4. WinRAR压缩操作帮助类

    //------------------------------------------------------------------------------------- // All Right ...

  5. 最小安装模式下Centos7.*网卡启动配置

    最小安装模式下的Centos7.*系统默认情况下,网卡是不启动的.为了解决联网问题,自己搜集了点资料,成功连接了网络.并梳理了下处理过程. 1.首先运行ip addr命令,查看配置文件的名称.有的文章 ...

  6. [DFNews] Touch ID不是神话,指模依旧能搞定。

    扫描制作翻模,使用含石墨硅胶压膜,前者复制指纹纹路,后者欺骗活体检测.

  7. 转 @html.ActionLink的几种参数格式

    一 Html.ActionLink("linkText","actionName") 该重载的第一个参数是该链接要显示的文字,第二个参数是对应的控制器的方法, ...

  8. 流量咪教你挖到5G免费流量

    自从出现了“抢红包”这个伟大的发明,身边的小伙伴们人人都练就了“左手右手一个快动作”的技能.然而,抢红包只有反应快还不够,还要避免下面这样的悲剧! 为了避免各位小主,因为流量不足而输在抢红包的起跑线上 ...

  9. 安装boost库(Windows)

    1. 下载 官网:http://www.boost.org/ 主页:https://sourceforge.net/projects/boost/ 当前最新版:V1.61.0 2. 编译 首先,生成b ...

  10. (转)MySQL配置文件mysql.ini参数详解、MySQL性能优化

    本文转自:http://www.cr173.com/html/18331_1.html my.ini(Linux系统下是my.cnf),当mysql服务器启动时它会读取这个文件,设置相关的运行环境参数 ...