一.2-extend-用法

Posted re-is-good

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一.2-extend-用法相关的知识,希望对你有一定的参考价值。

源码 285-347行

目录

一. extend前言 

二. extend用法

三: 源码解析

四: 深拷贝的小demo

 

一. extend前言 

extend 方法可以帮我们扩展jQuery的方法(jQuery内部也是通过这个方法来扩展大量的其它方法)

因此我认为extend在jQuery源码中是十分重要的。因此先说一下extend的实现。

 
 

二. extend用法

1. 扩展jQuery工具方法(即在$函数上添加方法)

$.extend({
    method: function(){
        console.log("method")
    }
})
// 调用
$.method(); // method
$().method();  // 报错

  

2. 扩展jQuery实例方法(即在$的原型上添加方法)

// fn是prototype的简写(见源码96行)
$.fn.extend({
    method2: function(){
        console.log("method2");
    }
})

// 调用
$.method2();    // 报错
$().method2();  // method2

  

3. 将其它对象扩展到某个对象身上

var obj = {};
$.extend(obj, {a: 1}, {b: 2});  
console.log(obj); // {a: 1, b: 2}
// 从第二个参数开始到最后一个参数,他们都会被扩展到obj对象身上。

  

4. 支持深浅拷贝

extend方法默认使用浅拷贝

如何进行深拷贝呢? 第一个实参为true即可

 

4.1. 浅拷贝的情况(extend默认)

var person = {
    info: {
        age: 18
    }
}
var obj = {}
$.extend(obj, person);
person.info.age = 20;
console.log(obj.info.age); // 20, 被顺带修改了

 

4.2. 深拷贝的情况

var person = {
    info: {
        age: 18
    }
}
var obj = {}
$.extend(true,obj, person);  // 第一个实参为true
person.info.age = 20;       // 修改了
console.log(obj.info.age); // 18,完全不受影响

 

 
 
 
 

三: 源码解析

第一行

// 285行 jQuery.extend = jQuery.fn.extend = function () {

这两个extend方法的实现其实是同一个,那么它是怎么判断用户是通过$.extend调用的,还是通过$.fn.extend来调用的。

很简单,通过this指向

 
 

286-290 定义一些变量

var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},  
// 如果没有传入参数,就给个默认值。避免报错
i = 1,
length = arguments.length,
deep = false;
 // 这是深浅拷贝的标志,false为浅拷贝,true为深拷贝

 

 

293-298 处理深浅拷贝的问题

if (typeof target === "boolean") { 
// 这里的 target是第一个参数的值
// 这种情况  $.extend(true, {"a":function(){}})});
    deep = target;                 
    target = arguments[1] || {}; 
      
// 参数转移。deep的值目前便是第一个参数的值,(即 true), 
// 而target便是第二个参数(即对象 {"a":function(){}})})

    i = 2;
// 这个i便是决定下面for循环的起始位置(for (var i=2; ))                        
}

 

 
 

306-309 如果只有一个对象时(那便是扩展jQuery工具方法或者扩展jQuery实例方法,见前面的第1,2个用法)

if (length === i) {
       target = this;
       --i;
}

如这种情况 $.extend(true, {"a": function(){}}) 或者 $.extend({"a": function(){}});

那么获取当前的this,这也解释了前面的疑问? 为什么$.extend和$.fn.exntend 的实现是同一个函数

 

如果是 $.extend(true, {"a": function(){}})的话, i的值是不是2。但for循环遍历的开始位置应该是1(for(var i=1;)); 因此需要--i;

如果是 $.extend({"a": function(){}});的话,i的值便是1.但for循环的开始位置应该是0(for(var i=0)); 因此也要 --i;

这里做的还是很巧妙的

 
 
 

311-343 下面的代码便是一个大的for循环啦

可以注意到上面做的一些变量i的操作都是为了这个for循环起始位置。(保证for循环的第一个元素是一个对象)

// 311行 for (; i < length; i++) {

 

313 防止用户传入null, undefined ,毕竟这玩意扩展不了

// 313行 注意options已经在前面定义了
// $(undefined, {}), for循环遍历时都要判断下元素是不是null或者undefined
if ((options = arguments[i]) != null) {

 

 

315-341

// 这里的options便是传入的一个个的对象。 例子 $.extend({a: function a(){}, b: function(){}})});
for (name in options) {
    // options 为 {a: function a(){}, b: function(){}})} 
    src = target[name];  // 第一次循环  src = $["a"]
    copy = options[name]; // 第一次循环 copy = function a(){};

    // Prevent never-ending loop
    if (target === copy) { 
    // 防止循环引用(这种情况 var obj = {}; $.extend(obj, {prop: obj}); )
    //    target: obj;  copy: obj。然后就会跳过循环,不用赋值了
        continue;    
    }                  

    
    if (deep && copy && 
        (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
        // 深拷贝 见下面分析
        
        // Don‘t bring in undefined values
    } else if (copy !== undefined) {
        // 浅拷贝
        target[name] = copy; // 直接赋值就行
    }
}

 

 
 

深拷贝的情况(源码325-335)

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 : {};
// 这里为什么不直接赋值? clone = []; 
// 有可能是这种情况 
// var obj = {a: {b: 1}}; $.extend(true, obj, {a: {c: 1}}); 如果直接clone = {}; 
// 那么 结果就是 {a: {c: 1}}。遇到相同的就被覆盖了。
    }

    // Never move original objects, clone them
    target[name] = jQuery.extend(deep, clone, copy);      // 递归调用

    // Don‘t bring in undefined values
}

 

 

什么样的情况才能进行深拷贝?

用户传入的第一个传入为 true(或者说truly, 即转布尔后为true)。

(copy)属性值要存在,不能说弄成这样的 $.extend({a: undefined});

(copy)属性值得是一个PlainObject(像 {a: 1}便是一个 PlainObject), 或者(copy) 属性值为 数组 (如 $.extend({a: [1, 2]}) )。

 
 

346行

return target;

返回这个target对象

 
 
 
 

四: 深拷贝的小demo

技术图片
function deepObjCopy(obj) {
    var copyObj = Array.isArray(obj) ? [] : {};
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (obj[key] && (typeof obj[key] === "object")) {
                // 递归调用, 如[], {}情况。typeof null虽然是object但是转boolean后为false
                copyObj[key] = deepObjCopy(obj[key]);
            } else {
                // 其它数据类型
                copyObj[key] = obj[key];
            }
        }
    }
    return copyObj;
}
var obj = { a: { b: 1 } };
var copyedObj = deepObjCopy(obj);
obj.a.b = 2;
console.log(copyedObj.a.b);  // 1

var obj = { a: [{ b: 1 }] };
var copyedObj = deepObjCopy(obj);
// console.log(copyedObj)
obj.a[0].b = 2;
console.log(copyedObj.a[0].b);  // 1
View Code

 

 

以上是关于一.2-extend-用法的主要内容,如果未能解决你的问题,请参考以下文章

extend的用法和短语例句

c_cpp 加载源图像固定用法(代码片段,不全)

SQL Select 语句的用法

JavaScript 片段

创建片段而不从 java 代码实例化它

Java中枚举的写法和用法