前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

深浅拷贝问点|博客系列

# 深浅拷贝问点

# 前言

前面系列即为前端面试系列(Front-end interview series), 主要内容是一些前端面试中经常被问到的题.

系列问答中没有繁琐的讲解过程, 力求保证面试者给予面试官一个简洁、具有重点的答案, 所以适合于有一定知识基础的前端童鞋👨‍🎓. 当然, 在每题的最后我也会贴上关于这一章节比较好文章, 以供大家更好的理解所提到的知识点.

请认准github地址: niubility-coding-js

# 一、面试部分

# 1. 有哪些属于浅拷贝 🤔️?

  1. Object.assign()方法
  2. 展开算法Spread ({...obj})
  3. 操作数组的方法slice()、concat()

# 2. 有哪些深拷贝的方式 🤔️?

  1. 比较流氓式的方式: JSON.parse(JSON.stringify(object))
  2. 自己实现一个深拷贝的方法
  3. 使用一些工具库里的方法, 比如lodash里的cloneDeep(), jquery里的extend().

# 3. JSON.parse(JSON.stringify(object))的缺点

  1. 会忽略属性值为undefined、symbol、函数的这三种情况, 也就是不进行拷贝
  2. 不能解决循环引用问题
  3. 不能正确的处理new Date
  4. 不能处理正则(为正则的时候, 拷贝过去为一个空对象{})

# 二、笔试部分

# 1. 手写一个Object.assign()函数实现

思路:

  1. 判断原生Object是否支持assign, 若是不存在的话则使用Object.defineProperty方法将该函数绑定到Object 中
  2. 判断传入的目标参数是否正确, 是否为null
  3. 使用Object()将目标参数转为对象, 并保存为to, 最终要返回这个to
  4. 使用 for..in 循环遍历出所有可枚举的自有属性。并复制给新的目标对象(使用 hasOwnProperty 获取自有属性,即非原型链上的属性)
  5. 在设计的时候需要在让该方法在严格模式下进行
function createAssign2 () {
	if (typeof Object.assign2 !== 'function') {
    Object.defineProperty(Object, 'assign2', {
      value: function (target) {
        "use strict";
        if (target == null) {
          throw new Error("Cannot convert undefined or null to object")
        }
        var to = Object(target);
        for (var i = 1; i < arguments.length; i++) {
          var nextSource = arguments[i];
          for (var nextKey in nextSource) {
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
        return to;
      },
      writable: true,
      configurable: true
    })
  }
}
createAssign2();
@前端进阶之旅: 代码已经复制到剪贴板



空

白

格




function createAssign2() {
    // 1. 判断是否存在assign2
    if (typeof Object.assign2 !== 'function') {
        // 2. 给 Object 中添加属性
        Object.defineProperty(Object, 'assign2', {
            value: function(target) {
                // 4. 开启严格模式
                "use strict";
                // 5. 判断目标对象是否为 null
                if (target == null) {
                    throw new Error("Cannot convert undefined or null to object")
                }
                // 6. 使用 Object() 包裹
                var to = Object(target);
                // 7. 遍历后面传入的所有对象
                for (var i = 1; i < arguments.length; i++) {
                    var nextSource = arguments[i];
                    // 8. 使用 for...in 查找所有的属性(包括原型链中的属性)
                    for (var nextKey in nextSource) {
                        // 9. 只考虑自身的属性
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
                // 10. 返回to
                return to;
            },
            writable: true,
            configurable: true
        })
    }
}
createAssign2();
@前端进阶之旅: 代码已经复制到剪贴板

注:

  1. 为什么要在严格模式下 🤔️?

那是因为使用原生的Object.assign()时, 若是有以下情况:

var newObj = Object.assign('123', '456', { name: 'LinDaiDai' });
// or
var newObj = Object.assign('123', { 0: '4' });
@前端进阶之旅: 代码已经复制到剪贴板

上面👆两种情况都会报错:

Cannot assign to read only property '0' of object '[object String]'
@前端进阶之旅: 代码已经复制到剪贴板

但如果是下面👇的情况就不会:

var newObj = Object.assign(123, '456'); // Number{0:"4", 1:"5", 2:"6" }
// or
var newObj = Object.assign('123', 456) // String{0: "1", 1: "2", 2: "3"}
@前端进阶之旅: 代码已经复制到剪贴板

看出什么了吗? 😊

如果参数中同时存在两个字符串, 比如"123"和"456"就会报错, 或者存在一个字符串和一个带有0属性的对象, 也会报错.

那是因为字符串类型的数据的可写属性writable是false:

var str = 'abc';
console.log(Object.getOwnPropertyDescriptor(str, "0"));
// { 
//   value: 'a',
//   writable: false, // 可写属性
//   enumerable: true,
//   configurable: false 
// }
@前端进阶之旅: 代码已经复制到剪贴板

而平常我们使用下标修改的方式直接修改是不会报错的, 只会修改不成功:

var str = "abc";
str[0] = "d";
console.log(str[0]); // "a" 还是"a", 证明没有修改成功
@前端进阶之旅: 代码已经复制到剪贴板

但是在严格模式下就会报错了:

"use strict";
var str = "abc";
str[0] = "d";
// Cannot assign to read only property '0' of object '[object String]'
@前端进阶之旅: 代码已经复制到剪贴板

换句话说: 在严格模式下, 如果你试图修改一个writable: false的属性的时候就会报错.

所以若是assign()方法的第一个参数的数据类型是非空的字符串, 而后面也有非空的字符串或者带有0属性的对象时, 就会报错, 因此我们在设计assign2的时候, 需要在严格模式下进行.

  1. 为什么需要有var to = Object(target)这一步 🤔️?

因为如果第一个参数target为基本数据类型(string、number、symbol)时, 需要将其包装为对象, 如果没有Object(target)这一步的话, 就会报错了:

Uncaught TypeError: Cannot create property 'obj' on string '123'
@前端进阶之旅: 代码已经复制到剪贴板

而如果第一个参数是这种基本数据类型的时候, 生成的对象会是这样:

var newObj = Object.assign("123", { name: "LinDaiDai" });
// String{ 0: "1", 1: "2", 2: "3", name: "LinDaiDai"}
@前端进阶之旅: 代码已经复制到剪贴板

前面的String表示新生成的对象的__proto__为String对象(而非Object对象), 而且其中有一个名为[[PrimitiveValue]]的属性为"123".

测试代码:

createAssign2();
var obj = {
  name: 'obj',
  colors: ['white', 'black']
}
var obj2 = Object.assign2({}, obj, { sex: 'boy' })
obj2.name = 'obj2'
obj2.colors.push('yellow')
console.log(obj) // { name: 'obj', colors: ['white', 'black', 'yellow'], sex: 'boy' }
console.log(obj2) // { name: 'obj2', colors: ['white', 'black', 'yellow'], sex: 'boy' }
@前端进阶之旅: 代码已经复制到剪贴板

# 2. 手写一个深拷贝方法

思路:

  1. 对传入的参数做校验, 不是对象类型则返回参数本身
  2. 校验的方式, 可以使用Object.prototype.toString.call(obj)的方式来判断
  3. 需要考虑是数组还是对象
  4. 需要考虑循环引用问题(可以用WeakMap或者数组来解决)
  5. 需要判断每一个属性的类型, 若是Object和Array的时候, 要进行递归处理
function isObject (object) {
	let type = Object.prototype.toString.call(object).slice(8, -1);
	return type === 'Object' || type === 'Array';
} 
function cloneDeep (source, hash = new WeakMap()) {
	if (!isObject(source)) return source
	if (hash.has(source)) return hash.get(source);
	
	var target = Array.isArray(source) ? [...source] : {...source};
  hash.set(source, target);
  
  Reflect.ownKeys(target).forEach(key => {
    if (isObject(source[key])) {
      target[key] = cloneDeep(source[key], hash);
    } else {
      target[key] = source[key];
    }
  })
  return target;
}
@前端进阶之旅: 代码已经复制到剪贴板



空

白

格




过程分析:

← 数组扁平化的六种实现方式比继承家业还要简单的JS继承题_封装篇 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合