0
点赞
收藏
分享

微信扫一扫

前端知识梳理(Vue与JS篇)

1、深拷贝与浅拷贝

1.1 什么是深拷贝与浅拷贝

在了解深浅拷贝之前我们需要了解一下都有哪些数据类型 js中数据类型主要分为两大类:基本数据类型与引用数据类型

基本数据类型(值类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol 引用数据类型:对象(Object)、数组(Array)、函数(Function)

基本数据类型:数据直接存储在栈(stack)中的数据。 引用数据类型:地址直接存储在栈(stack)中,真实的数据存储在堆内存中。当需要取引用类型数据时,会先检索栈中地址,取得地址后再去堆中获取真实的数据。

浅拷贝:会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用地址,当引用地址指向的值改变时也会跟着变化。 对于复杂数据类型来说:浅拷贝后的变量改变影响旧变量,未实现完全的隔离。 但对于基本数据类型数据没有啥影响 浅拷贝方法:for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 、=(赋值操作)等

深拷贝:深拷贝和浅拷贝是针对复杂数据类型(对象及数组)来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。
深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

1.2 浅拷贝实现方式

浅拷贝是我们在使用基础数据类型时最常用到的一个方式,浅拷贝方法:for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 、=(赋值操作)等

1、Object.assign

这里我们可以看到当只有一层的时候,修改一个变量另一个不受影响 但是二层之后,修改一个变量的值另一个也受到影响,因此是浅拷贝(以下几个方法也是同理)

let arr = ['张三',{name: '小明'}, {name: '小红'}]
let arr1 = Object.assign({},arr)
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);

a34f747e91924818895f36bb8db9bfe9.png

2、扩展运算符 ...

let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = [...arr]
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);

8be31681544740eaa80e03feda5c60c6.png

3、Array.prototype.slice()

let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = arr.slice()
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);

616424feb11d407bbf3825c0d95c3db0.png

4、Array.prototype.concat()

let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = arr.concat()
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);

18b8d26e978442fd86e256fe10ba08fb.png

5、for in 与 =(赋值操作)

let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = []
for (let key in arr) {
      arr1[key] = arr[key];
}
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'd
console.log(arr, arr1);

89a9e17bf4df44adbf434e8875ef7bf3.png

1.3 深拷贝实现方式

深拷贝主要针对的是复杂数据类型(对象、数据、数组对象) 深拷贝常用的实现:JSON.parse(JSON.stringify(obj)) 、递归实现、函数库lodash

1.3.1 JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj))是最简单的实现深拷贝的方法,直接拿来就能用 但是也有一个固有的缺陷:

1、对象的属性值是函数时,无法拷贝 2、原型链上的属性无法拷贝 3、会忽略undefined、symbol 4、不能处理RegExp、Date类型数据 

let arr = [{
        name:'小明'
      },{
        name:'小红'
      }]
    let arr1 = JSON.parse(JSON.stringify(arr))
    arr1[0].name = '我换名字了'
    console.log(arr,arr1);

实现效果:拷贝之后,变量之间不相互影响 6f44b572f9e040b896a1d11d68dca1f2.png

1.3.2 递归实现(尽量能手写

如果是基本数据类型就直接赋值操作,如果不是基本数据类型就继续递归调用

 // 递归方法
    function deepClone(source) {
      // [] => Array(基类) {} => Object(基类)
      // constructor 构造器
      const targetObj = source.constructor === Array ? [] : {}
      for (let keys in source) {
        // key是否存在
        if (Object.hasOwnProperty.call(source, keys)) {
          // 引用数据类型
          if (source[keys] && typeof source[keys] === 'object') {
            // 递归
            targetObj[keys] = deepClone(source[keys])
          } else {
            // 基础类 直接赋值
            targetObj[keys] = source[keys]
          }
        }
      }
      return targetObj
    }
    let arr = [{
      name: '小明'
    }, {
      name: '小红'
    }]
    let arr1 = deepClone(arr)
    arr1[0].name = '我换名字了'
    console.log(arr, arr1);

运行效果: f2e1d43895074c1a80ae78dbda3a8d72.png

1.3.3 使用函数库lodash中的cloneDeep

Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。 Lodash 的模块化方法 非常适用于:

  • 遍历 array、object 和 string
  • 对值进行操作和检测
  • 创建符合功能的函数

使用lodash

  • 1、安装lodash

npm i --save lodash

  • 2、按需引入

import _ from "lodash"

  • 3、使用
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = _.cloneDeep(arr)
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);

运行结果:两个变量互不影响是深拷贝 160741d2002f4ae2860984097da2c5ec.png 参考链接: Lodash中文文档

2、如何让谷歌浏览器支持小字体

谷歌浏览器默认的字体最小12px,如果不去调浏览器配置,小于12px的字体默认展示是12px。如何用代码实现

scale(参数)缩放实现小字体 参数:大于1放大;小于1缩小;等于1不变

<div class="parent">
    大字体
    <div class="small-font">小字体</div>
</div>

<style>
  .parent{
     font-size:12px;
  }
  .small-font{
     transform:scale(0.8);
     -webkit-transform:scale(0.8);
  }
</style>

3、字面量与Object.create()的区别

Object.create()静态方法以一个现有对象作为原型,创建一个新对象。

Object.create(proto, propertiesObject) proto 必填,可以是null也可以是对象 propertiesObject 不必填,默认属性有value、writable(不可写)、configurable(可配置)、enumerable(可枚举)

Object.create(null) 创建的对象无原型,等价于

o = Object.create(null);
// 等价于:
o = { __proto__: null };

Object.create(obj) 创建的对象以obj对象为原型,这样就可以保证新创建的对象和原有对象解耦,当我们操作对象b或者c时并不会影响原有数据 微信图片_20240313150249.png propertiesObject

o2 = Object.create(
  {},
  {
    p: {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true,
    },
  },
);
// 这与以下语句不等价:
// o2 = Object.create({ p: 42 })
// 后者将创建一个原型为 { p: 42 } 的对象。

propertiesObject设置只读的时候,对象中键值不可改 微信图片_20240313150249.png 个人总结: 1、Object.create(null)创建的对象,无原型比较纯洁 2、Object.create()可以实现一个继承目标且可高度配置的对象 3、字面量方式创建比较简洁

4、实现树形结构数据筛选

this.treeList = this.filterTree(this.listQuery.keyword, res.data)

filterTree(searchValue, searchTopicCatalogs) {

      let tempSearchArr = [];

      searchTopicCatalogs.forEach((treeNode) => {

        if (treeNode.baseMenuEntityList && treeNode.baseMenuEntityList.length > 0) {

          // 后序遍历
          let tempArr = this.filterTree(searchValue, treeNode.baseMenuEntityList)||[];

          // 当前树节点的子节点存在匹配字符,直接纳入数组
          if (tempArr.length > 0) {
            treeNode.baseMenuEntityList = tempArr
            tempSearchArr.push(treeNode);

          //当前树节点存在匹配字符
          }else {
            if (treeNode.menuName.indexOf(searchValue) >= 0) {
              tempSearchArr.push(treeNode);
            }
          }

        // 叶子节点
        }else {
          // 当前树节点存在匹配字符
          if (treeNode.menuName.indexOf(searchValue) >= 0) {
            tempSearchArr.push(treeNode);
          }
        }
      });
      return tempSearchArr;
    },

实现结果: 160741d2002f4ae2860984097da2c5ec.png

举报

相关推荐

0 条评论