js 中分為基本變量,和引用變量。在$.extend(),體現得非常深刻。
除了數組,對象,其他的變量都可以稱為基本變量。
var a = 3;
var b = a;
b = 4;
console.log(a); //3
console.log(b); //4
var a = {name:"strong"}
var b = a;
b.name = "Lee";
console.log(a.name) // Lee;
console.log(b.name) //Lee;
ps:可見,b屬性的變化,影響到了a , 這種賦值就是淺復制,而取消這種影響,就是深復制 ;
在jQuery源碼中,對于 對象 還有一個判斷函數 是 $.isPlainObject(),用于判斷此對象是否為“純對象”,純對象就是變量直接申請的對象。
var a = {
a:3
};
function cons(){
this.a = 3;
}
var b = new cons();
// a就是純對象,而b就不是。
以下為jquery中 isPlainObject 的源碼。
//此函數的核心,就是以object.prototype.constructor 屬性,來判斷是否為純對象。
//當 constructor是 Object() 時,為純對象;當為其他構造函數時就是非純函數。
isPlainObject: function( obj ) {
var proto, Ctor;
// Detect obvious negatives
// Use toString instead of jQuery.type to catch host objects
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
return false;
}
proto = getProto( obj ); //obj的原型
// Objects with no prototype (e.g., `Object.create( null )`) are plain
if ( !proto ) {
return true;
}
// Objects with prototype are plain if they were constructed by a global Object function
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
}
深層復制的基本原理就是將對象引用,變為基本變量賦值,這樣就不會出現對象引用后,修改對象屬性,造成原對象屬性污染。通俗一點,就是要把對象的屬性(此屬性為基本變量) 賦值給 另一個對象的相應屬性;
所以我們可以自己先寫一個較為簡單的 深層復制 對象擴展函數,
function extend(target,src){
for( i in src){
var a = target[i]?target[i]:{};
//判斷純對象,并遞歸或直接賦值;
target[i] = $.isPlainObject(src[i])?extend(a,src[i]):src[i];
}
return target;
}
ps:jquery中的深層復制只能對于純對象,以下特例不會進行深層復制的
function a(){
this.a = 3;
}
var b = {
c :3,
d : new a()
}
var e = {};
$.extend(e,b);
b當中的d屬性并不會深層復制給e,當e改變d屬性時會影響b.d 的值;
以下為jquery中extend的源碼:
jQuery.extend = jQuery.fn.extend = function() {
//arguments[0] -> boolean;
//arguments[1] -> target;
//arguments[2] -> src
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;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// 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
//like $().extend({a:3})
//另外一種形式$.extend(a,b) //jquery工具方法;
if ( i === length ) {
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
//recurse 遞歸吧
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = jQuery.isArray( copy ) ) ) ) {
if ( copyIsArray ) { // 此if,根據src 類型 , 改變clone;
copyIsArray = false;
clone = src && jQuery.isArray( src ) ? src : [];
} else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
}
// Never move original objects, clone them
//copy 是對象,且好幾層對象,遞歸復制,保障深層復制成功。
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;
};