爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      最全面的 JavaScript 基础代码手写指南,读完这篇就够了!

      首页 知识中心 软件开发 文章详情页

      最全面的 JavaScript 基础代码手写指南,读完这篇就够了!

      2025-03-18 08:31:45 阅读次数:15

      Promise,函数,参数,对象,手写,拷贝,数组

      一、JavaScript 基础

      1. 手写 Object.create

      思路:将传入的对象作为原型

      function create(obj) {
        function F() {}
        F.prototype = obj
        return new F()
      }

      2. 手写 instanceof 方法

      instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

      实现步骤:

      1. 首先获取类型的原型

      2. 然后获得对象的原型

      3. 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null

      具体实现:

      function myInstanceof(left, right) {
        let proto = Object.getPrototypeOf(left), // 获取对象的原型
            prototype = right.prototype; // 获取构造函数的 prototype 对象
      ​
        // 判断构造函数的 prototype 对象是否在对象的原型链上
        while (true) {
          if (!proto) return false;
          if (proto === prototype) return true;
      ​
          proto = Object.getPrototypeOf(proto);
        }
      }

      3. 手写 new 操作符

      在调用 new 的过程中会发生以上四件事情:

      (1)首先创建了一个新的空对象

      (2)设置原型,将对象的原型设置为函数的 prototype 对象。

      (3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)

      (4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

      function objectFactory() {
        let newObject = null;
        let constructor = Array.prototype.shift.call(arguments);
        let result = null;
        // 判断参数是否是一个函数
        if (typeof constructor !== "function") {
          console.error("type error");
          return;
        }
        // 新建一个空对象,对象的原型为构造函数的 prototype 对象
        newObject = Object.create(constructor.prototype);
        // 将 this 指向新建对象,并执行函数
        result = constructor.apply(newObject, arguments);
        // 判断返回对象
        let flag = result && (typeof result === "object" || typeof result === "function");
        // 判断返回结果
        return flag ? result : newObject;
      }
      // 使用方法
      objectFactory(构造函数, 初始化参数);

      4. 手写 Promise

      const PENDING = "pending";
      const RESOLVED = "resolved";
      const REJECTED = "rejected";
      ​
      function MyPromise(fn) {
        // 保存初始化状态
        var self = this;
      ​
        // 初始化状态
        this.state = PENDING;
      ​
        // 用于保存 resolve 或者 rejected 传入的值
        this.value = null;
      ​
        // 用于保存 resolve 的回调函数
        this.resolvedCallbacks = [];
      ​
        // 用于保存 reject 的回调函数
        this.rejectedCallbacks = [];
      ​
        // 状态转变为 resolved 方法
        function resolve(value) {
          // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
          if (value instanceof MyPromise) {
            return value.then(resolve, reject);
          }
      ​
          // 保证代码的执行顺序为本轮事件循环的末尾
          setTimeout(() => {
            // 只有状态为 pending 时才能转变,
            if (self.state === PENDING) {
              // 修改状态
              self.state = RESOLVED;
      ​
              // 设置传入的值
              self.value = value;
      ​
              // 执行回调函数
              self.resolvedCallbacks.forEach(callback => {
                callback(value);
              });
            }
          }, 0);
        }
      ​
        // 状态转变为 rejected 方法
        function reject(value) {
          // 保证代码的执行顺序为本轮事件循环的末尾
          setTimeout(() => {
            // 只有状态为 pending 时才能转变
            if (self.state === PENDING) {
              // 修改状态
              self.state = REJECTED;
      ​
              // 设置传入的值
              self.value = value;
      ​
              // 执行回调函数
              self.rejectedCallbacks.forEach(callback => {
                callback(value);
              });
            }
          }, 0);
        }
      ​
        // 将两个方法传入函数执行
        try {
          fn(resolve, reject);
        } catch (e) {
          // 遇到错误时,捕获错误,执行 reject 函数
          reject(e);
        }
      }
      ​
      MyPromise.prototype.then = function(onResolved, onRejected) {
        // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
        onResolved =
          typeof onResolved === "function"
            ? onResolved
            : function(value) {
                return value;
              };
      ​
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : function(error) {
                throw error;
              };
      ​
        // 如果是等待状态,则将函数加入对应列表中
        if (this.state === PENDING) {
          this.resolvedCallbacks.push(onResolved);
          this.rejectedCallbacks.push(onRejected);
        }
      ​
        // 如果状态已经凝固,则直接执行对应状态的函数
      ​
        if (this.state === RESOLVED) {
          onResolved(this.value);
        }
      ​
        if (this.state === REJECTED) {
          onRejected(this.value);
        }
      };

      5. 手写 Promise.then

      then 方法返回一个新的 promise 实例,为了在 promise 状态发生变化时(resolve / reject 被调用时)再执行 then 里的函数,我们使用一个 callbacks 数组先把传给then的函数暂存起来,等状态改变时再调用。

      那么,怎么保证后一个 *\*\*then\*\** 里的方法在前一个 *\*\*then\*\**(可能是异步)结束之后再执行呢? 我们可以将传给 then 的函数和新 promise 的 resolve 一起 push 到前一个 promise 的 callbacks 数组中,达到承前启后的效果:

      • 承前:当前一个 promise 完成后,调用其 resolve 变更状态,在这个 resolve 里会依次调用 callbacks 里的回调,这样就执行了 then 里的方法了

      • 启后:上一步中,当 then 里的方法执行完成后,返回一个结果,如果这个结果是个简单的值,就直接调用新 promise 的 resolve,让其状态变更,这又会依次调用新 promise 的 callbacks 数组里的方法,循环往复。。如果返回的结果是个 promise,则需要等它完成之后再触发新 promise 的 resolve,所以可以在其结果的 then 里调用新 promise 的 resolve

      then(onFulfilled, onReject){
          // 保存前一个promise的this
          const self = this; 
          return new MyPromise((resolve, reject) => {
            // 封装前一个promise成功时执行的函数
            let fulfilled = () => {
              try{
                const result = onFulfilled(self.value); // 承前
                return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后
              }catch(err){
                reject(err)
              }
            }
            // 封装前一个promise失败时执行的函数
            let rejected = () => {
              try{
                const result = onReject(self.reason);
                return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
              }catch(err){
                reject(err)
              }
            }
            switch(self.status){
              case PENDING: 
                self.onFulfilledCallbacks.push(fulfilled);
                self.onRejectedCallbacks.push(rejected);
                break;
              case FULFILLED:
                fulfilled();
                break;
              case REJECT:
                rejected();
                break;
            }
          })
         }

      注意:

      • 连续多个 then 里的回调方法是同步注册的,但注册到了不同的 callbacks 数组中,因为每次 then 都返回新的 promise 实例(参考上面的例子和图)

      • 注册完成后开始执行构造函数中的异步事件,异步完成之后依次调用 callbacks 数组中提前注册的回调

      6. 手写 Promise.all

      1) 核心思路

      1. 接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数

      2. 这个方法返回一个新的 promise 对象,

      3. 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象

      4. 参数所有回调成功才是成功,返回值数组与参数顺序一致

      5. 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。

      2)实现代码

      一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,如果其中一个接口失败了,多个请求也就失败了,页面可能啥也出不来,这就看当前页面的耦合程度了

      function promiseAll(promises) {
        return new Promise(function(resolve, reject) {
          if(!Array.isArray(promises)){
              throw new TypeError(`argument must be a array`)
          }
          var resolvedCounter = 0;
          var promiseNum = promises.length;
          var resolvedResult = [];
          for (let i = 0; i < promiseNum; i++) {
            Promise.resolve(promises[i]).then(value=>{
              resolvedCounter++;
              resolvedResult[i] = value;
              if (resolvedCounter == promiseNum) {
                  return resolve(resolvedResult)
                }
            },error=>{
              return reject(error)
            })
          }
        })
      }
      // test
      let p1 = new Promise(function (resolve, reject) {
          setTimeout(function () {
              resolve(1)
          }, 1000)
      })
      let p2 = new Promise(function (resolve, reject) {
          setTimeout(function () {
              resolve(2)
          }, 2000)
      })
      let p3 = new Promise(function (resolve, reject) {
          setTimeout(function () {
              resolve(3)
          }, 3000)
      })
      promiseAll([p3, p1, p2]).then(res => {
          console.log(res) // [3, 1, 2]
      })

      7. 手写 Promise.race

      该方法的参数是 Promise 实例数组, 然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能改变一次, 那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法, 注入到数组中的每一个 Promise 实例中的回调函数中即可.

      Promise.race = function (args) {
        return new Promise((resolve, reject) => {
          for (let i = 0, len = args.length; i < len; i++) {
            args[i].then(resolve, reject)
          }
        })
      }

      8. 手写防抖函数

      函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

      // 函数防抖的实现,fn是高频触发的函数,delay 
      function debounce(fn, wait) {
        let timer = null;
      ​
        return function() {
          let context = this,
              args = arguments;
      ​
          // 如果此时存在定时器的话,则取消之前的定时器重新记时
          if (timer) {
            clearTimeout(timer);
            timer = null;
          }
      ​
          // 设置定时器,使事件间隔指定事件后执行
          timer = setTimeout(() => {
            fn.apply(context, args);
          }, wait);
        };
      }

      9. 手写节流函数

      函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

      // 函数节流的实现;
      function throttle(fn, delay) {
        let curTime = Date.now();
      ​
        return function() {
          let context = this,
              args = arguments,
              nowTime = Date.now();
      ​
          // 如果两次时间间隔超过了指定时间,则执行函数。
          if (nowTime - curTime >= delay) {
            curTime = Date.now();
            return fn.apply(context, args);
          }
        };
      }

      10. 手写类型判断函数

      function getType(value) {
        // 判断数据是 null 的情况
        if (value === null) {
          return value + "";
        }
        // 判断数据是引用类型的情况
        if (typeof value === "object") {
          let valueClass = Object.prototype.toString.call(value),
            type = valueClass.split(" ")[1].split("");
          type.pop();
          return type.join("").toLowerCase();
        } else {
          // 判断数据是基本数据类型的情况和函数的情况
          return typeof value;
        }
      }

      11. 手写 call 函数

      call 函数的实现步骤:

      1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。

      2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。

      3. 处理传入的参数,截取第一个参数后的所有参数。

      4. 将函数作为上下文对象的一个属性。

      5. 使用上下文对象来调用这个方法,并保存返回结果。

      6. 删除刚才新增的属性。

      7. 返回结果。

      // call函数实现
      Function.prototype.myCall = function(context) {
        // 判断调用对象
        if (typeof this !== "function") {
          console.error("type error");
        }
        // 获取参数
        let args = [...arguments].slice(1),
            result = null;
        // 判断 context 是否传入,如果未传入则设置为 window
        context = context || window;
        // 将调用函数设为对象的方法
        context.fn = this;
        // 调用函数
        result = context.fn(...args);
        // 将属性删除
        delete context.fn;
        return result;
      };

      12. 手写 apply 函数

      apply 函数的实现步骤:

      1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。

      2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。

      3. 将函数作为上下文对象的一个属性。

      4. 判断参数值是否传入

      5. 使用上下文对象来调用这个方法,并保存返回结果。

      6. 删除刚才新增的属性

      7. 返回结果

      // apply 函数实现
      Function.prototype.myApply = function(context) {
        // 判断调用对象是否为函数
        if (typeof this !== "function") {
          throw new TypeError("Error");
        }
        let result = null;
        // 判断 context 是否存在,如果未传入则为 window
        context = context || window;
        // 将函数设为对象的方法
        context.fn = this;
        // 调用方法
        if (arguments[1]) {
          result = context.fn(...arguments[1]);
        } else {
          result = context.fn();
        }
        // 将属性删除
        delete context.fn;
        return result;
      };

      13. 手写 bind 函数

      bind 函数的实现步骤:

      1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。

      2. 保存当前函数的引用,获取其余传入参数值。

      3. 创建一个函数返回

      4. 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象。

      // bind 函数实现
      Function.prototype.myBind = function(context) {
        // 判断调用对象是否为函数
        if (typeof this !== "function") {
          throw new TypeError("Error");
        }
        // 获取参数
        var args = [...arguments].slice(1),
            fn = this;
        return function Fn() {
          // 根据调用方式,传入不同绑定值
          return fn.apply(
            this instanceof Fn ? this : context,
            args.concat(...arguments)
          );
        };
      };

      14. 函数柯里化的实现

      函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

      function curry(fn, args) {
        // 获取函数需要的参数长度
        let length = fn.length;
      ​
        args = args || [];
      ​
        return function() {
          let subArgs = args.slice(0);
      ​
          // 拼接得到现有的所有参数
          for (let i = 0; i < arguments.length; i++) {
            subArgs.push(arguments[i]);
          }
      ​
          // 判断参数的长度是否已经满足函数所需参数的长度
          if (subArgs.length >= length) {
            // 如果满足,执行函数
            return fn.apply(this, subArgs);
          } else {
            // 如果不满足,递归返回科里化的函数,等待参数的传入
            return curry.call(this, fn, subArgs);
          }
        };
      }
      ​
      // es6 实现
      function curry(fn, ...args) {
        return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
      }

      15. 实现AJAX请求

      AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。

      创建AJAX请求的步骤:

      • 创建一个 XMLHttpRequest 对象。

      • 在这个对象上使用 open 方法创建一个 HTTP 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。

      • 在发起请求前,可以为这个对象添加一些信息和监听函数。比如说可以通过 setRequestHeader 方法来为请求添加头信息。还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候就可以通过 response 中的数据来对页面进行更新了。

      • 当对象的属性和监听函数设置完成后,最后调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。

      const SERVER_URL = "/server";
      let xhr = new XMLHttpRequest();
      // 创建 Http 请求
      xhr.open("GET", SERVER_URL, true);
      // 设置状态监听函数
      xhr.onreadystatechange = function() {
        if (this.readyState !== 4) return;
        // 当请求成功时
        if (this.status === 200) {
          handle(this.response);
        } else {
          console.error(this.statusText);
        }
      };
      // 设置请求失败时的监听函数
      xhr.onerror = function() {
        console.error(this.statusText);
      };
      // 设置请求头信息
      xhr.responseType = "json";
      xhr.setRequestHeader("Accept", "application/json");
      // 发送 Http 请求
      xhr.send(null);

      16. 使用Promise封装AJAX请求

      // promise 封装实现:
      function getJSON(url) {
        // 创建一个 promise 对象
        let promise = new Promise(function(resolve, reject) {
          let xhr = new XMLHttpRequest();
          // 新建一个 http 请求
          xhr.open("GET", url, true);
          // 设置状态的监听函数
          xhr.onreadystatechange = function() {
            if (this.readyState !== 4) return;
            // 当请求成功或失败时,改变 promise 的状态
            if (this.status === 200) {
              resolve(this.response);
            } else {
              reject(new Error(this.statusText));
            }
          };
          // 设置错误监听函数
          xhr.onerror = function() {
            reject(new Error(this.statusText));
          };
          // 设置响应的数据类型
          xhr.responseType = "json";
          // 设置请求头信息
          xhr.setRequestHeader("Accept", "application/json");
          // 发送 http 请求
          xhr.send(null);
        });
        return promise;
      }

      17. 实现浅拷贝

      浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

      (1)Object.assign()

      Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

      注意:

      • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。

      • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。

      • 因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,会报错。

      let target = {a: 1};
      let object2 = {b: 2};
      let object3 = {c: 3};
      Object.assign(target,object2,object3);  
      console.log(target);  // {a: 1, b: 2, c: 3}
      (2)扩展运算符

      使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。语法:let cloneObj = { ...obj };

      let obj1 = {a:1,b:{c:1}}
      let obj2 = {...obj1};
      obj1.a = 2;
      console.log(obj1); //{a:2,b:{c:1}}
      console.log(obj2); //{a:1,b:{c:1}}
      obj1.b.c = 2;
      console.log(obj1); //{a:2,b:{c:2}}
      console.log(obj2); //{a:1,b:{c:2}}
      (3)数组方法实现数组浅拷贝
      1)Array.prototype.slice
      • slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。

      • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

      let arr = [1,2,3,4];
      console.log(arr.slice()); // [1,2,3,4]
      console.log(arr.slice() === arr); //false
      2)Array.prototype.concat
      • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

      • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

      let arr = [1,2,3,4];
      console.log(arr.concat()); // [1,2,3,4]
      console.log(arr.concat() === arr); //false
      (4)手写实现浅拷贝
      // 浅拷贝的实现;
      ​
      function shallowCopy(object) {
        // 只拷贝对象
        if (!object || typeof object !== "object") return;
      ​
        // 根据 object 的类型判断是新建一个数组还是对象
        let newObject = Array.isArray(object) ? [] : {};
      ​
        // 遍历 object,并且判断是 object 的属性才拷贝
        for (let key in object) {
          if (object.hasOwnProperty(key)) {
            newObject[key] = object[key];
          }
        }
      ​
        return newObject;
      }// 浅拷贝的实现;
      ​
      function shallowCopy(object) {
        // 只拷贝对象
        if (!object || typeof object !== "object") return;
      ​
        // 根据 object 的类型判断是新建一个数组还是对象
        let newObject = Array.isArray(object) ? [] : {};
      ​
        // 遍历 object,并且判断是 object 的属性才拷贝
        for (let key in object) {
          if (object.hasOwnProperty(key)) {
            newObject[key] = object[key];
          }
        }
      ​
        return newObject;
      }// 浅拷贝的实现;
      function shallowCopy(object) {
        // 只拷贝对象
        if (!object || typeof object !== "object") return;
        // 根据 object 的类型判断是新建一个数组还是对象
        let newObject = Array.isArray(object) ? [] : {};
        // 遍历 object,并且判断是 object 的属性才拷贝
        for (let key in object) {
          if (object.hasOwnProperty(key)) {
            newObject[key] = object[key];
          }
        }
        return newObject;
      }

      18. 实现深拷贝

      • 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用 Object.assign 和展开运算符来实现。

      • 深拷贝: 深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败

      (1)JSON.stringify()
      • JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。

      • 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

      let obj1 = {  a: 0,
                    b: {
                       c: 0
                       }
                  };
      let obj2 = JSON.parse(JSON.stringify(obj1));
      obj1.a = 1;
      obj1.b.c = 1;
      console.log(obj1); // {a: 1, b: {c: 1}}
      console.log(obj2); // {a: 0, b: {c: 0}}
      (2)函数库lodash的_.cloneDeep方法

      该函数库也有提供_.cloneDeep用来做 Deep Copy

      var _ = require('lodash');
      var obj1 = {
          a: 1,
          b: { f: { g: 1 } },
          c: [1, 2, 3]
      };
      var obj2 = _.cloneDeep(obj1);
      console.log(obj1.b.f === obj2.b.f);// false
      (3)手写实现深拷贝函数
      // 深拷贝的实现
      function deepCopy(object) {
        if (!object || typeof object !== "object") return;
      ​
        let newObject = Array.isArray(object) ? [] : {};
      ​
        for (let key in object) {
          if (object.hasOwnProperty(key)) {
            newObject[key] =
              typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
          }
        }
      ​
        return newObject;
      }
      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://qingshan09.blog.csdn.net/article/details/134916749,作者:前端青山,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:JavaScript 成绩管理系统与值传递、引用传递详解

      下一篇:JavaScript 运算符与数据类型转换详解

      相关文章

      2025-05-19 09:04:44

      js原生手写一个拖拽小功能

      js原生手写一个拖拽小功能

      2025-05-19 09:04:44
      js , 手写
      2025-05-19 09:04:44

      js小题2:构造函数介绍与普通函数对比

      js小题2:构造函数介绍与普通函数对比

      2025-05-19 09:04:44
      new , 关键字 , 函数 , 对象 , 构造函数
      2025-05-19 09:04:30

      【Canvas技法】辐射式多道光影的实现

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30
      代码 , 函数 , 实现
      2025-05-19 09:04:22

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22
      使用 , 函数 , 初始化 , 定义 , 对象
      2025-05-19 09:04:22

      外设驱动库开发笔记46:MAX31855热偶变送器驱动

      外设驱动库开发笔记46:MAX31855热偶变送器驱动

      2025-05-19 09:04:22
      对象 , 温度
      2025-05-19 09:04:14

      C语言字符函数和字符串函数--(超全超详细)

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14
      函数 , 字符 , 字符串
      2025-05-19 09:04:14

      复杂度的OJ练习

      复杂度的OJ练习

      2025-05-19 09:04:14
      代码 , 复杂度 , 思路 , 数组 , 算法
      2025-05-16 09:15:24

      如何将一串数字用函数的方法倒过来(C语言)

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24
      函数 , 数字 , 数组
      2025-05-16 09:15:24

      jQuery遍历对象、数组、集合

      jQuery遍历对象、数组、集合

      2025-05-16 09:15:24
      jQuery , 对象 , 数组 , 遍历 , 集合
      2025-05-16 09:15:17

      递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝

      递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝

      2025-05-16 09:15:17
      回溯 , 子集 , 数组 , 算法 , 递归
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5260984

      查看更多

      最新文章

      【Canvas技法】辐射式多道光影的实现

      2025-05-19 09:04:30

      外设驱动库开发笔记54:外设库驱动设计改进的思考

      2025-05-19 09:04:22

      外设驱动库开发笔记46:MAX31855热偶变送器驱动

      2025-05-19 09:04:22

      C语言字符函数和字符串函数--(超全超详细)

      2025-05-19 09:04:14

      复杂度的OJ练习

      2025-05-19 09:04:14

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24

      查看更多

      热门文章

      Arrays类的使用

      2023-06-08 06:23:00

      游戏编程之十一 图像页CPICPAGE介绍

      2022-11-28 01:25:04

      Python 函数调用父类详解

      2023-04-23 09:44:31

      C++拷贝构造函数(深拷贝,浅拷贝)详解

      2023-03-30 09:59:46

      游戏编程之六 游戏编程的特点

      2024-09-25 10:13:46

      C#8.0新语法

      2023-02-07 10:34:04

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      超级好用的C++实用库之国密sm3算法

      函数与二元关系在编程中的应用

      Python之⾯向对象基础

      用go语言,集团里有 n 名员工,他们可以完成各种各样的工作创造利润, 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与

      JavaScript-函数和方法的区别

      统计js数组中奇数元素的个数

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号