迅雷面试题:深拷贝对象,除了原型上的属性?

来源:互联网 发布:node readfilesync 编辑:程序博客网 时间:2024/05/16 19:16

第一种:深拷贝原型上的属性

function clone(Obj) {  var buf;  if (Obj instanceof Array) {    buf = [];  // 创建一个空的数组    var i = Obj.length;    while (i--) {      buf[i] = clone(Obj[i]);    }    return buf;  } else if (Obj instanceof Object){    buf = {};  // 创建一个空对象    for (var k in Obj) {  // 为这个对象添加新的属性      buf[k] = clone(Obj[k]);    }    return buf;  }else{    return Obj;  }}//测试function O() {  this.yyy = 'yyy';}function X() {  this.xxx = 'xxx';}X.prototype = new O();x = new X();var obj = { a : {k1:'a'}, b : 'b',x:x};var obj2 = clone(obj);obj.x.yyy = 'zzz'console.log(obj2);console.log(obj2.x.yyy);

以上对吗,对象x具有原型上的属性,通过函数clone也深拷贝了原型上的属性;测试可知
改变对象obj上的原型属性,obj2不变。

obj.x.yyy = 'zzz'

第二种:不深拷贝原型上的属性(以及构造函数非和构造函数非Object的对象)

$ = function() {  var copyIsArray,    hasOwn = Object.prototype.hasOwnProperty,    isPlainObject = function(obj) {      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) {      var name,copy,clone;      for (name in options) {        src = target[name];        copy = options[name];        if (target === copy) { continue; } //为了避免无限循环        if (deep && copy          && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {          if (copyIsArray) {//如果copy是数组            copyIsArray = false;//回归初始值            clone = src && Array.isArray(src) ? src : [];//因为这是对象合并,所以可能target[name]是存在的,存在则不需要拷贝。          } else {            clone = src && isPlainObject(src) ? src : {};          }          target[name] = extend(deep, clone, copy);        } else if (copy !== undefined) {          target[name] = copy;        }      }      return target;    };  return { extend : extend };}();obj1 = { a : 'a', b : 'b', y : '1'  };obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };var combineObj = $.extend(true,obj1,obj2);console.log(combineObj);

注意:(1)关键代码是

isPlainObject = function(obj) {      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);    },

(2)把递归的写法搞清楚

第三种:不深拷贝原型上的属性(本题答案)

//判断是对象var isObject = function(obj){    return Object.prototype.toString.call(obj) === '[object Object]'}//判断是数组var isArray = Array.isArray || function(obj){    return Object.prototype.toString.call(obj) === '[object Array]'}//获取对象不在原型上的键var get_keys = function(obj){    var keys = [];    for(key in obj){        if(hasOwnProperty.call(obj,key)){            keys.push(key);        }    }    return keys;}//实现深拷贝var deepCopy = function(obj){    var temp ;    if(isArray(obj)){        temp = [];        for(var i = 0 ; i < obj.length ; i++){            if(isArray(obj[i])||isObject(obj[i])){                //当前项是数组或者对象,递归调用                temp.push(deepCopy(obj[i]));            }else{                temp.push(obj[i]);            }        }    }else if(isObject(obj)){        temp = {};        var keys = get_keys(obj);        var length = keys.length ;        for(var i = 0 ; i < length ; i++){            //将对象值递归,结果赋值到新创建的temp对象的key[i]属性。            temp[keys[i]] = deepCopy(obj[keys[i]])        }    }else{        //传入其他类型直接返回。        return obj;    }    return temp ;}

注意:(1)主要是hasOwnProperty的用法:该方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只有在只在给定属性存在于对象实例中时,才会返回true。
(2)第2种和第3种的区别:第2种是只要对象的构造函数不是object,则不进行深拷贝,而第3种是对象的构造函数可以是非Object,不深拷贝对象原型上的属性,只拷贝实例上的属性。
举例如下:

function O() {  this.yyy = 'yyy';}function X() {  this.xxx = 'xxx';}X.prototype = new O();x = new X();//var obj = { a : {k1:'a'}, b : 'b',x:x };var obj = { a : {k1:'a'}, b : 'b',x:x};var obj2 = deepCopy(obj);obj.x.xxx = 'zzz'obj.x.yyy = 'zzz'console.log("obj ",obj); //{ a: { k1: 'a' }, b: 'b', x: O { xxx: 'zzz', yyy: 'zzz' } }console.log("obj2 ",obj2); //{ a: { k1: 'a' }, b: 'b', x: { xxx: 'xxx' } }console.log(obj2.x.xxx); //xxx 已经深拷贝,值不变console.log(obj2.x.yyy); //undefinedconsole.log(x.hasOwnProperty("xxx"));//true

(3)deepCopy 函数是直接不拷贝(跳过)相关对象,而$.extend对于不满足isPlainObject的对象,则执行浅拷贝(即 target[name] = copy;)。

第四种:不深拷贝原型上的属性($.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的注释:如果obj=null,则返回String(obj),即null,    type = function(obj) {      return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";    },    isWindow = function(obj) {      return obj && typeof obj === "object" && "setInterval" in obj;    },    //如果浏览器有内置的Array.isArray 实现,就使用浏览器自身的实现方式,否则将对象转为String,看是否为"[object Array]"。(1.这里所说的将对象转为String即toString.call(obj) 2.考虑了兼容性 )    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) {      var name,copy,clone;      for (name in options) {        src = target[name];        copy = options[name];        if (target === copy) { continue; } //为了避免无限循环        //以下if将copy是对象和数组合并在一起        if (deep && copy          && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {          if (copyIsArray) {//如果copy是数组            copyIsArray = false;//回归初始值            clone = src && isArray(src) ? src : [];//因为这是对象合并,所以可能target[name]是存在的,存在则不需要拷贝。          } else {            clone = src && isPlainObject(src) ? src : {};          }          target[name] = extend(deep, clone, copy);        } else if (copy !== undefined) {//说明copy既不是对象,也不是数组,是基本类型,        直接赋值          target[name] = copy;        }      }      return target;    };  return { extend : extend };}();//测试function O() {  this.yyy = 'yyy';}function X() {  this.xxx = 'xxx';}X.prototype = new O();x = new X();obj1 = { a : 'a', b : 'b', y : '1'  };obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };var combineObj = $.extend(true,obj1,obj2);console.log(combineObj);
原创粉丝点击