js的深拷贝和浅拷贝

前言

上一篇一道面试题引发的js数据类型传参思考,研究了下js数据在内存中的存储,在最后自然而然的想到了深拷贝和浅拷贝,篇幅所限,就放在这里了。

何为浅拷贝

沿用上一篇的图

var obj1 = {x:1, y:2};
var obj2 = obj1;
obj2.x = 3;
console.log(obj1); // {x: 3, y: 2}
复制代码

形如上面这个例子,简单的把对象obj1赋值给obj2,他们在内存中都指向相同的堆区,改变任何一个都会影响另一个,这就是浅拷贝:赋值后的数据会相互影响

深拷贝

为了解决浅拷贝的问题,自然引申出了深拷贝:赋值后的数据彼此独立,不相互影响。即obj2拥有obj1的初始数据,但obj2的数据放在堆区的另一个位置。

深拷贝原理很简单:递归遍历对象,一一赋值

直接上完整版代码:

  function deepClone(from, to) {
    var record = {} // 记录引用,解决对象循环引用的问题
    function innerDP(from, to) {
      for (var i in from) {
        if (!from.hasOwnProperty(i)) {
          // for...in会遍历到原型链上的可枚举属性,hasOwnProperty只访问对象实例本身的属性
          continue
        }
        if (from[i] && typeof from[i] === 'object' && Object.keys(from[i]).length > 0 && !(record[i] && record[i] === from[i])) {
          to[i] = {}
          record[i] = from[i]
          innerDP(from[i], to[i])
        } else {
          to[i] = from[i]
        }
      }
    }

    innerDP(from, to)
  }

  var staff1 = {
    name: '张三',
    company: {
      title: '总监',
      cid: '10006',
      wage: 50000,
      service: [1, 2, 3, {level: 'gold'}],
    },
    age: undefined,
    x: null,
    y: '',
    getName: function () {
      return this.name
    }
  }
  var staff2 = {}
  deepClone(staff1, staff2)
  console.log('staff2拷贝staff1成功:', staff2)
  staff2.x = 3
  staff2.company.title = '程序猿'
  staff2.company.service[3].level = 'silver'
  console.log('staff2改变:', staff2)
  console.log('staff1是否被影响:', staff1)

  var btn1 = $('#btn1') // $('#btn1').context循环引用
  var btn2 = {}
  deepClone(btn1, btn2)
  // btn2.length = 2
  console.log(btn2)
  console.log(btn1)
}
复制代码

对象循环引用的问题

形如这样的循环引用对象:

var a = {};  var b = {a: a};  a.b = b; // a中有b,b中有a,无限嵌套
复制代码

深拷贝时会导致堆溢出报错:Uncaught RangeError: Maximum call stack size exceeded
所以deepClone采用了record记录:

!(record[i] && record[i] === from[i])
复制代码

类型判断

Object.prototype.toString.call方法

万物皆对象,Object.prototype.toString.call(value)返回的格式都是"[Object 类型]"

function valueType(value) {
  var str = Object.prototype.toString.call(value)
  str = str.split(' ')[1].replace(']', '')
  
  return str
}
console.log(valueType(1)); // "Number"  
console.log(valueType('1')); // "String"
console.log(valueType(undefined)); // "Undefined"  
console.log(valueType(false)); // "Boolean"  
console.log(valueType(Function)); // "Function"
console.log(valueType(null)); // "Null"
console.log(valueType([])); // "Array"
console.log(valueType([])); // "Object"
console.log(valueType($('#btn1'))); // "Object"
console.log(valueType($('#btn1').context)); // "HTMLDocument"
复制代码

typeof判断

typeof 1; // "number"
typeof '1'; // "string"
typeof undefined; // "undefined"
typeof false; // "boolean"
typeof Function; // "function"
typeof null; // "object"
typeof []; // "object"
typeof {}; // "object"
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值