VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > JavaScript教程 >
  • 引用、浅拷贝及深拷贝 到 Map、Set(含对象assign、freeze方法、WeakMap、WeakSet及数

 

从引用聊到深浅拷贝,从深拷贝过渡到ES6新数据结构Map及Set,再到另一个map即Array.map()和与其类似的Array.flatMap(),中间会有其他相关话题,例如Object.freeze()Object.assign()等等。

前言

一边复习一边学习,分清引用与深浅拷贝的区别,并实现浅拷贝与深拷贝,之后通过对深拷贝的了解,拓展到ES6新数据结构Map及Set的介绍,再引入对另一个数组的map方法的使用与类似数组遍历方法的使用。通过一条隐式链将一长串知识点串联介绍,可能会有点杂,但也会有对各知识点不同之处有明显区分,达到更好的记忆与理解。

引用、浅拷贝及深拷贝

  • 引用

    通常在介绍深拷贝之前,作为引子我们会看见类似以下例子:

    var testArr = [0, [1, 2]] var secArr = testArr secArr[0] = 'x' console.log(testArr) // [ 'x', [ 1, 2 ] ] 复制代码
  • 浅拷贝

    对于浅拷贝,其与引用的区别,我们一边实现浅拷贝,之后进行对比再解释,实现如下:

    var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = Object.assign({}, testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' console.log(testObj) // { name: 'currName',                      //   nums: [ '一', [ '二', '三' ] ],                      //   objs: { innerObj: 'changedContent' } } console.log(secObj) // { name: 'changedName',                     //   nums: [ '一', [ '二', '三' ] ],                     //   objs: { innerObj: 'changedContent' } } 复制代码
  • Object.freeze()

    freeze方法其效果在有一定程度与浅拷贝相同,但效果上还要比拷贝多上一层,即freeze冻结,但因为该方法自身 内部属性,该方法的名称又可以称为“浅冻结”,对于第一层数据,如浅拷贝一般,不可被新对象改变,但被freeze方法冻结过的对象,其自身也无法添加、删除或修改其第一层数据,但因为“浅冻结”这名称中浅的这一明显属性,freeze方法对于内部如果存在更深层的数据,是可以被自身修改,且也会被“=”号所引用给新的变量。

    简单使用如下:

    function deepCopy(content) {   var retObj = {}   for (const key in content) {     if (content.hasOwnProperty(key)) {       retObj[key] = typeof content[key] === 'object'        ? deepCopy(content[key])       : content[key];     }   }   return retObj }  var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } var secObj = deepCopy(testObj) secObj.name = 'changedName' secObj.nums[0] = '一' secObj.nums[1] = ['二', '三'] secObj.objs['innerObj'] = 'changedContent' secObj.age = 18 console.log(testObj) // { name: 'currName',                      //   nums: [ 1, [ 2, 3 ] ],                      //   objs: { innerObj: 'content' } } console.log(secObj) // { name: 'changedName',                     //   nums: { '0': '一', '1': [ '二', '三' ] },                     //   objs: { innerObj: 'changedContent' },                     //   age: 18 } 复制代码
  • Version 2: 完善数组与对象组合嵌套的情况

    此时对于内部存在的数组来说,会被转化为对象,键为数组的下标,值为数组的值,被存储在新的对象中,因此有了我们完善的第二版。

    function objToMap (object) {   let map = new Map()   for (const key in object) {     if (object.hasOwnProperty(key)) {       map.set(key, object[key])     }   }   return map } var testObj = {   'name': 'currName',   'nums': [1, [2, 3]],   'objs': {     'innerObj': 'content'   } } let map = objToMap(testObj) map.set('name', 'changedName') console.log(testObj) // { name: 'currName',                      //   nums: [ 1, [ 2, 3 ] ],                      //   objs: { innerObj: 'content' } } console.log(map) // Map {                  // 'name' => 'changedName',                  // 'nums' => [ 1, [ 2, 3 ] ],                  // 'objs' => { innerObj: 'content' } } 复制代码
  • Map自身方法介绍

    含增删改查方法:set、get、has、delete;

    遍历方法:keys、values、entries、forEach;

    其他方法:size、clear。

    需要注意的是forEach方法还可以接受第二个参数,改变第一个参数即回调函数的内部this指向。

    let n = 100 let map = new Map() function upStairs (n) {   if (n === 1) return 1   if (n === 2) return 2   if (map.has(n)) return map.get(n)   let ret = upStairs(n - 1) + upStairs(n - 2)   map.set(n, ret)   return ret } console.log(upStairs(n)) // 573147844013817200000 复制代码
  • WeakMap

    本节介绍在ES6中,与Map相关且一同发布的WeakMap数据结构。

    • WeakMap与Map区别

      WeakMap与Map主要有下图三个区别:

      区别 Map WeakMap
      “键”类型: 任何类型 Object对象
      自身方法: 基本方法:set、get、has、delete;
      遍历方法:keys、values、entries、forEach;
      其他方法:size、clear。
      基本方法:set、get、has、delete。
      键引用类型: 强引用 弱引用

      此处我们对强弱引用进行简单介绍:弱引用在回收机制上比强引用好,在“适当”的情况将会被回收,减少内存资源浪费,但由于不是强引用,WeakMap不能进行遍历与size方法取得内部值数量。

    • WeakMap自身方法

      含增删改查方法:set、get、has、delete。

      <!--示例一--> let element = document.getElementById('box') let wMap = new WeakMap() wMap.set(element, {clickCount: 0}) element.addEventListener('click', () => {   let countObj = wMap.get(element)   countObj.clickCount++    console.log(wMap.get(element).clickCount) // click -> n+=1 })  <!--示例二--> const _age = new WeakMap() const _fn = new WeakMap() class Girl {   constructor (age, fn) {     _age.set(this, age)     _fn.set(this, fn)   }   changeAge () {     let age = _age.get(this)     age = age >= 18 ? 18 : null     _age.set(this, age)      _age.get(this) === 18     ? _fn.get(this)()     : console.log('error')   } }  const girl = new Girl(25, () => console.log('forever 18 !')) girl.changeAge() // forever 18 ! 复制代码
  • Set

    介绍完ES6新增的Map与WeakMap数据结构,我们继续介绍一同新增的Set数据结构。

    Set之于Array,其实有点像Map之于Object,Set是在数组的数据结构基础上做了一些改变,新出的一种类似于数组的数据结构,Set的成员的值唯一,不存在重复的值。以下将对Set数据结构作一些简单的介绍。

    • Set与Array之间的相互转换

      Set可以将具有Iterable接口的其他数据结构作为参数用于初始化,此处不止有数组,但仅以数组作为例子,单独讲述一下。

      let arr = [1, 2, 3, 3] let set = new Set(arr) // 增删改查 set.add(4) console.log(set) // Set { 1, 2, 3, 4 } set.delete(3) console.log(set) // Set { 1, 2, 4 } console.log(set.has(4)) // true  // 遍历方法 因为在Set结构中没有键名只有健值,所以keys方法和values方法完全一致 console.log(set.keys()) // [Set Iterator] { 1, 2, 4 } console.log(set.values()) // [Set Iterator] { 1, 2, 4 }  for (const item of set.entries()) {   console.log(item) //[ 1, 1 ]                     // [ 2, 2 ]                     // [ 4, 4 ] }  const obj = {   name: 'objName' } set.forEach(function (key, value) {   console.log(key, value, this.name) // 1 1 'objName'                                      // 2 2 'objName'                                      // 4 4 'objName' }, obj)  // 其他方法 console.log(set.size) // 3 set.clear() console.log(set) // Set {} 复制代码
    • Set应用场景

      因为扩展运算符...对Set作用,再通过Array遍历方法,很容易求得并集、交集及差集,也可以通过间接使用Array方法,构造新的数据赋给Set结构变量。

      let obj1 = {'name': 1} let obj2 = {'name': 2} let wSet = new WeakSet() wSet.add(obj1).add(obj2) console.log(wSet.has(obj2)) // true wSet.delete(obj2) console.log(wSet.has(obj2)) // false 复制代码
    • WeakSet应用场景

      对于WeakSet的应用场景,其与WeakMap类似,因为弱引用的优良回收机制,WeakSet依旧可以存放DOM节点,避免删除这些节点后引发的内存泄漏的情况;也可以在构造函数和类中存放实例this,同样避免删除实例的时候产生的内存泄漏的情况。

      // flat const testArr = [1, 2, [3, [4]]] const flatArr = testArr.flat() console.log(flatArr) // [1, 2, 3, Array(1)] -> 0: 1                      //                        1: 2                      //                        2: 3                      //                        3: [4]  const arr = [1, 2, 3] // map const mapArr = arr.map(x => x * 2) console.log(mapArr) // [2, 4, 6]  arr.map((item, index, arr) => {   console.log(item, index, arr) // 1 0 [1, 2, 3]                                 // 2 1 [1, 2, 3]                                 // 3 2 [1, 2, 3] })  // flatMap // arr.flatMap(x => [x * 2]) === arr.map(x => x * 2) const flatMapArr = arr.flatMap(x => [x * 2]) console.log(flatMapArr) // [2, 4, 6] 复制代码
    • Array.reduce()

      Array.reduce() —— reduce方法与map最大的不同是不返回新的数组,其返回的是一个计算值,参数为回调函数与回调函数参数pre初始值,回调函数中参数为pre与next,当在默认情况时,pre为数组中第一个值,next为数组中第二个值,回调函数返回值可以滚雪球般更改pre值;而当index设置数值后,pre初始值为参数值,next从数组中第一个值一直取到数组最后一位。

      例子如下:

      const arr = [1, 2, 3, 4, 5] const obj = {num: 3}  // filter const filterArr = arr.filter(function (value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]                           // 3 [1, 2, 3, 4, 5]                           // 4 [1, 2, 3, 4, 5]   return value > this.num }, obj) console.log(filterArr) // [4, 5]  // find const findResult = arr.find(function (value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]                           // 3 [1, 2, 3, 4, 5]   return value > this.num }, obj) console.log(findResult) // 4  // findIndex const findIndexResult = arr.findIndex(function (value) { return value > this.num }, obj) console.log(findIndexResult) // 3 复制代码
    • Array.includes()

      Array.includes() —— 返回值为Boolean值,其可以简单快捷的判断数组中是否含有某个值。其第一个参数为需要查找的值,第二个参数为开始遍历的位置,遍历位置起始点默认为0。相比于indexOf、filter、find及findIndex方法,includes方法更简单快捷返回Boolean值进行判断,其二对于数组中NaN值,includes可以识别到NaN。

      // every const arr = [1, 2, 3, 4, 5] const obj = { num: 3 } const everyResult = arr.every(function(value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]   return value < this.num }, obj) console.log(everyResult) // false  // some const someResult = arr.some(function(value, index, arr) {   console.log(index, arr) // 0 [1, 2, 3, 4, 5]                           // 1 [1, 2, 3, 4, 5]                           // 2 [1, 2, 3, 4, 5]                           // 3 [1, 2, 3, 4, 5]   return value > this.num }, obj) console.log(someResult) // true 


相关教程