JS数据类型与拷贝

一、JS的基本数据类型

  • 基本数据类型:String,Boolean,Number,Undefined,Null;
  • 引用数据类型:Object(Array,Date,RegExp,Function);
  • 基本数据类型和引用数据类型的区别:
    1、保存位置不同:基本数据类型保存在栈内存中,引用数据类型保存在堆内存中,然后在栈内存中保存了一个对堆内存中实际对象的引用,即数据在堆内存中的地址,JS对引用数据类型的操作都是操作对象的引用而不是实际的对象,例如复制的实质是复制了地址,因而它们指向了同一个堆内存对象;
    为什么基本数据类型保存在栈中,而引用数据类型保存在堆中?
    1)堆比栈大,栈比堆速度快;
    2)基本数据类型比较稳定,而且相对来说占用的内存小;
    3)引用数据类型大小是动态的,而且是无限的,引用值的大小会改变,不能把它放在栈中,否则会降低变量查找的速度,因此放在变量栈空间的值是该对象存储在堆中的地址,地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响;
    4)堆内存是无序存储,可以根据引用直接获取;

按引用访问:js不允许直接访问保存在堆内存中的对象,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值;

2、基本数据类型使用typeof可以返回其基本数据类型,但是NULL类型会返回object,因此null值表示一个空对象指针;引用数据类型使用typeof会返回object,此时需要使用instanceof来检测引用数据类型;

3、引用数据类型 (注意,例如new Boolean(false)和false是不===的,但是经过JSON转换后,丢失了constructor,变为普通的false)

  • ES6新增数据类型:Map,Set,Generator,Symbol
  • 本地对象:ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”,即本地对象就是 ECMA-262 定义的类(引用类型);
  • 宿主对象:宿主”就是我们网页的运行环境,即“操作系统”和“浏览器”,所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象,所有的BOM和DOM对象都是宿主对象,因为其对于不同的“宿主”环境所展示的内容不同,即ECMAScript官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过ECMAScript程序创建的对象;
  • JS内置对象:是指JS语言自带的一些对象,供开发者使用,这些对象提供了一些常用的或是最基本而必要的功能;
    1、Arguments:函数参数集合;
    2、Array对象:length,instanceof,isArray(),toString()返回字符串,valueOf()返回数组的值,join()可以将数组转为字符串,push(),pop(),shift(),unshift(),reverse(),sort(),
    slice(),splice(),indexOf(),lastIndexOf(),迭every(),filter(),forEach(),map(),some(),
    归并方法reduce(),reduceRight();
    3、Boolean:布尔对象;
    4、Error:异常对象;
    5、Number:数值对象;
    6、String对象:length,charAt()返回指定位置的字符,concat(),slice(),subString(),
    subStr(),indexOf(),lastIndexOf(),trim(),toLowerCase(),toUpperCase(),split(),
    text.match(),text.splice();
    7、Date对象:toUTCstring(),getTime();
    8、RegExp对象:test();
    9、Function对象:arguments,this,apply(this,arguments),call(this,num1,num2);
    10、Math对象:min(),max(),ceil(),floor(),round(),random();
    11、Global对象:encodeURI,encodeURIComponent,parseInt(),eval();
    12、Object对象:prototype,constructor;
  • 基本包装类型:Boolean,Number,String
    为了便于操作“基本类型值”,JS 提供了 三个 特殊的引用类型:Boolean、Number、String。这些类型和其他引用类型相似,但同时 也具备与各自基本类型相应的特殊行为。实际上:每当读取一个基本类型值的时候, “后台就会创建一个对应的基本包装类型的对象”,从而能够调用一些方法来操作这些数据。
    当通过直接赋值方式创建时,具有基本类型的性质,无法动态添加属性和方法。
    当通过New修饰符创建时,属于引用类型,可以动态添加属性和方法。

二、赋值、浅拷贝与深拷贝

赋值

赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,两个对象是完全联动的。

浅拷贝

按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

  1. Object.assign()和数组的slice()concat() 仅对基本类型数据进行了深拷贝
  2. 扩展运算符
let bar = {...baz};
  1. 遍历赋值
function shallowCopy(src) {
   var dst = {};
   for (var prop in src) {
       if (src.hasOwnProperty(prop)) {
           dst[prop] = src[prop];
       }
   }
   return dst;
}
深拷贝

创造一个一模一样的对象,新对象跟原对象不共享内存,修改不会互相影响。

  1. JSON.parse(JSON.stringify()) 可以部分实现深拷贝,但会失去constructor,凡是undefined、function、symbol、Map, Set, RegExp, Date, ArrayBuffer 等内置类型在进行序列化时会丢失,Boolean、Number、String等基本包装类型会变成普通的基本数据类型。此外不能处理多重嵌套。
let a = {};
let b = {a};
a.b = b;
let copy = JSON.parse(JSON.stringify(a));//报错
  1. 采用函数库lodash提供的_.cloneDeep进行深拷贝
var obj2 = _.cloneDeep(obj1);
  1. 采用jquery提供的$.extend进行深拷贝
var obj2 = $.extend(true, {}, obj1);
  1. 利用MessageChannel发送并接收消息,作为复制结果。缺点是该操作为异步,且依然不支持function。
function structuralClone(obj) {
    return new Promise(resolve =>{
        const {port1, port2} = new MessageChannel();
        //两个port可以互相通过onmessage ,监听到对方postMessage的内容
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
    })
}
const obj = /* ... */;
structuralClone(obj).then(res=>{
     console.log(res);
})
  1. 利用history api中的history.state传递并接收数据,作为复制结果。该方法是同步的,但有些浏览器对调用频率有限制。
  2. 遍历对象、数组直到都是基本数据类型,然后再进行复制
   // 定义检测数据类型的功能函数
   function checkedType(target) {
     return Object.prototype.toString.call(target).slice(8, -1)
   }
   // 实现深度克隆---对象/数组
   function clone(target) {
     // 判断拷贝的数据类型
     // 初始化变量result 成为最终克隆的数据
     let result, targetType = checkedType(target)
     if (targetType === 'object') {
       result = {}
     } else if (targetType === 'Array') {
       result = []
     } else {
       return target
     }
     // 遍历目标数据
     for (let i in target) {
       // 获取遍历数据结构的每一项值。
       let value = target[i]
       // 判断目标结构里的每一值是否存在对象/数组
       if (checkedType(value) === 'Object' ||
         checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
         // 继续遍历获取到value值
         result[i] = clone(value)
       } else { 
        // 获取到value值是基本的数据类型或者是函数。
         result[i] = value;
       }
     }
     return result
   }

   // 定义检测数据类型的功能函数
   function checkedType(target) {
     return Object.prototype.toString.call(target).slice(8, -1)
   }
   // 实现深度克隆---对象/数组
   function clone(target) {
     // 判断拷贝的数据类型
     // 初始化变量result 成为最终克隆的数据
     let result, targetType = checkedType(target)
     if (targetType === 'object') {
       result = {}
     } else if (targetType === 'Array') {
       result = []
     } else {
       return target
     }
     // 遍历目标数据
     for (let i in target) {
       // 获取遍历数据结构的每一项值。
       let value = target[i]
       // 判断目标结构里的每一值是否存在对象/数组
       if (checkedType(value) === 'Object' ||
         checkedType(value) === 'Array') { 
          // 对象/数组里嵌套了对象/数组
          // 继续遍历获取到value值
         result[i] = clone(value)
       } else { 
         // 获取到value值是基本的数据类型或者是函数。
         result[i] = value;
       }
     }
     return result
   }

你可能感兴趣的:(JS数据类型与拷贝)