08月08, 2022

JS进阶(8)--对象混合与克隆

对象混合与克隆

对象混合

对象混合简单来说就是把几个不同的对象组合到一个对象中,比如:

let user = {
    name: "jack",
    age: 18,
    tel: "13898989891"
}
let stu = {
    name: "rose",
    score: 90,
    sex: "女"
}

要混合成一个对象,比如:

{ name: 'rose', age: 18, tel: '13898989891', score: 90, sex: '女' }

在现在ES6的基础上,其实十分的简单,可以使用扩展运算符...或者Object.assgin()函数都可一个办到

let u1 = { ...user, ...stu };
//注意Object.assign函数要实现混入,第一个参数最好是空对象,
//因为Object.assign函数会改变一个参数的值
let u2 = Object.assign({}, user, stu);

混入之后,形成了新的对象,这个时候,如果改变原来对象的值,不会对新对象产生影响

let user = {
    name: "jack",
    age: 18,
    tel: "13898989891"
}
let stu = {
    name: "rose",
    score: 90,
    sex: "女",
}

let u2 = Object.assign({}, user, stu);
stu.sex = "男"
console.log(u2);

上面将stu.sex的值,改为了,但是u2打印的sex值任然是

但是,如果我们的混入的对象,有更深的层次,这个时候,状况就会发生变化

let user = {
    name: "jack",
    age: 18,
    tel: "13898989891"
}
let stu = {
    name: "rose",
    score: 90,
    sex: "女",
    teacher: {
        name: "Mr.J",
        type: "java"
    }
}

let u2 = Object.assign({}, user, stu);
console.log(u2);
stu.sex = "男"
stu.teacher.name = "Miss.W";
console.log(u2);

这里打印很明显看到,teacher的名字也随之发生了变化,当然,这是属于对象克隆的问题了

对象克隆

由于众所周知的对象引用传递的问题,如果对象之间直接用=赋值,改变其中的一个对象的值,另外的也会发生变化。

要改变这个情况,我们当然可以使用扩展运算符...或者自己去写一个克隆函数就行了

let stu = {
    name: "rose",
    score: 90,
    sex: "女",
    teacher: {
        name: "Mr.J",
        type: "java"
    }
}

let s = { ...stu };
stu.name = "jack";
console.log(s);

改变name的值,新赋值的s对象的name并不会发生变化。

我们也可以自己写一个克隆函数:

function clone(obj) {
    var newObj = {};
    for (var prop in obj) {
        newObj[prop] = obj[prop];
    }
    return newObj;
}

let stu = {
    name: "rose",
    score: 90,
    sex: "女",
    teacher: {
        name: "Mr.J",
        type: "java"
    }
}

let s = clone(stu);
stu.name = "jack";
console.log(s);

但是,无论哪种,这种克隆还是只属于所谓的浅克隆,因为只要,克隆对象的层次再深一点,比如对象的属性是一个对象,或者对象的属性是一个数组,那么还是会造成引用传递的问题。

如果要避免这个问题,我们可以将clone克隆函数改写成下面这个样子

/**
 * 克隆对象
 * @param {any} obj 
 * @param {boolean} deep true深克隆,false浅克隆
 * @returns 克隆对象
 */
function clone(obj, deep) {
  if (Array.isArray(obj)) {
    // 如果是一个数组
    if (deep) {
      // 深度克隆
      var newArr = []; //定义一个空数组
      for (var i = 0; i < obj.length; i++) {
        // 循环obj的每一项
        newArr[i].push(arguments.callee(obj[i], deep)); // push数组
      }
    } else {
      return obj.slice(); // 复制数组
    }
  } else if (typeof obj === "object") {
    // 如果是一个普通对象
    var newObj = {}; // 定义一个对象
    for (var prop in obj) {
      // 循环obj
      if (deep) {
        // 如果是深度克隆的话,我们把这个属性的值再克隆一遍【递归】
        newObj[prop] = arguments.callee(obj[prop], deep);
      } else {
        newObj[prop] = obj[prop]; // 添加属性
      }
    }
    return newObj; // 返回
  } else {
    // 函数、原始类型
    return obj; // 直接返回 【递归的终止条件】
  }
}

再次使用clone函数,注意第二个参数要传true

let stu = {
  name: "rose",
  score: 90,
  sex: "女",
  teacher: {
    name: "Mr.J",
    type: "java",
  },
};

let s = clone(stu,true);
stu.teacher.name = "Miss.W";
console.log(s);

本文链接:http://www.yanhongzhi.com/post/js_ap_18.html

-- EOF --

Comments