JavaScript高级四、高阶技巧

news2024/11/23 22:29:52

零、文章目录

JavaScript高级四、高阶技巧

1、深浅拷贝

  • 首先浅拷贝和深拷贝只针对引用类型

(1)浅拷贝

  • 浅拷贝:拷贝对象的属性的值(简单类型存的值就是值本身引用类型存的值是对象的堆地址),所以如果拷贝的对象值中有引用类型属性,拷贝后的新对象属性和源对象属性指向同一个对地址,修改此指向的对象会相互影响

  • 常见方法:

    • 拷贝对象:Object.assgin() 或者 展开运算符 {…obj}
    • 拷贝数组:Array.prototype.concat() 或者 […arr]

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>浅拷贝</title>
</head>

<body>
    <script>
        const user = {
            uname: 'pink',
            age: 18,
            family: {
                baby: '小pink'
            }
        }

        // 浅拷贝
        const o1 = {...user
        }
        o1.age = 20 //对象属性值类型修改不影响
        console.log(o1.age) //20
        console.log(user.age) //18

        // 浅拷贝
        const o2 = {}
        Object.assign(o2, user)
        o2.age = 20
        o2.family.baby = '老pink' //对象属性引用类型修改会相互影响
        console.log(o2.family.baby) //老pink
        console.log(user.family.baby) //老pink
    </script>
</body>

</html>

(2)深拷贝

  • 深拷贝:拷贝对象属性的时候,遇到引用类型就创建一个新对象,然后递归,直至所有字段全部拷贝完成。
  • 常见方法:
    • 递归函数实现深拷贝
      • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
      • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
    • lodash.cloneDeep实现深拷贝
    • JSON.stringify实现深拷贝

递归函数实现深拷贝:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>递归函数实现深拷贝</title>
</head>

<body>
    <script>
        const obj = {
            uname: 'pink',
            age: 18,
            hobby: ['乒乓球', '足球'],
            family: {
                baby: '小pink'
            }
        }
        const o = {}

        // 拷贝函数
        function deepCopy(newObj, oldObj) {
            for (let k in oldObj) {
                // 处理数组
                if (oldObj[k] instanceof Array) {
                    newObj[k] = []
                    deepCopy(newObj[k], oldObj[k])
                        // 处理对象
                } else if (oldObj[k] instanceof Object) {
                    newObj[k] = {}
                    deepCopy(newObj[k], oldObj[k])
                        //值类型
                } else {
                    newObj[k] = oldObj[k]
                }
            }
        }

        // 函数调用  两个参数 o 新对象  obj 旧对象
        deepCopy(o, obj)
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

lodash.cloneDeep实现深拷贝:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>lodash.cloneDeep实现深拷贝</title>
</head>

<body>
    <!-- 先引用 -->
    <script src="./lodash.min.js"></script>
    <script>
        const obj = {
            uname: 'pink',
            age: 18,
            hobby: ['乒乓球', '足球'],
            family: {
                baby: '小pink'
            }
        }
        const o = _.cloneDeep(obj)
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

JSON.stringify实现深拷贝:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON.stringify实现深拷贝</title>
</head>

<body>
    <script>
        const obj = {
                uname: 'pink',
                age: 18,
                hobby: ['乒乓球', '足球'],
                family: {
                    baby: '小pink'
                }
            }
            // 把对象转换为 JSON 字符串    
        const o = JSON.parse(JSON.stringify(obj))
        o.age = 20
        o.hobby[0] = '篮球'
        o.family.baby = '老pink'
        console.log(o.age) //20
        console.log(o.hobby[0]) //篮球
        console.log(o.family.baby) //老pink
        console.log(obj.age) //18
        console.log(obj.hobby[0]) //乒乓球
        console.log(obj.family.baby) //小pink
    </script>
</body>

</html>

2、异常处理

  • 异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

(1)throw 抛异常

  • throw 抛出异常信息,程序也会终止执行
  • throw 后面跟的是错误提示信息
  • Error 对象配合 throw 使用,能够设置更详细的错误信息

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>throw抛出异常</title>
</head>

<body>
    <script>
        function fn(x, y) {
            if (!x || !y) {
                // throw '没有参数传递进来'
                throw new Error('没有参数传递过来')
            }

            return x + y
        }
        console.log(fn())
    </script>
</body>

</html>

image-20230530140409885

(2)try /catch 捕获异常

  • try…catch 用于捕获错误信息
  • 将预估可能发生错误的代码写在 try 代码段中
  • 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
  • finally 不管是否有错误,都会执行

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>try-catch捕获异常</title>
</head>

<body>
    <p>123</p>
    <script>
        function fn() {
            try {
                // 可能发送错误的代码 要写到 try
                const p = document.querySelector('.p')
                p.style.color = 'red'
            } catch (err) {
                // 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
                console.log(err.message)
            } finally {
                // 不管你程序对不对,一定会执行的代码
                console.log('不管你程序对不对,一定会执行的代码')
            }
            console.log(11)
        }
        fn()
    </script>
</body>

</html>

image-20230530140903442

(3)debugger

  • 可以在代码里面添加debugger,用来断点调试代码

image-20230530141922325

3、处理this

(1)this指向

  • 普通函数this指向

    • 谁调用 this 的值指向谁
    • 普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined
  • 箭头函数this指向

  • 箭头函数中的并不存在 this,箭头函数中的this是绑定的最近作用域中的this,向外层作用域中一层一层查找this,直到有this的定义

  • 不适用情况:构造函数,原型函数,字面量对象中函数,dom事件函数等

  • 适用情况:需要使用上层this的地方

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>普通函数的this指向</title>
</head>

<body>
    <button>点击</button>
    <script>
        // 普通函数:谁调用我,this就指向谁
        console.log(this) // window
        function fn() {
            console.log(this) // window    
        }
        window.fn()
        window.setTimeout(function() {
            console.log(this) // window 
        }, 1000)
        document.querySelector('button').addEventListener('click', function() {
            console.log(this) // 指向 button
        })
        const obj = {
            sayHi: function() {
                console.log(this) // 指向 obj
            }
        }
        obj.sayHi()
    </script>
</body>

</html>

(2)改变this

  • JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向
  • call()
    • 使用 call 方法调用函数,同时指定被调用函数中 this 的值
    • 语法:fun.call(thisArg, arg1, arg2, ...)
      • thisArg:在 fun 函数运行时指定的 this 值
      • arg1,arg2:传递的其他参数
      • 返回值就是函数的返回值,因为它就是调用函数
  • apply()
    • 使用 apply 方法调用函数,同时指定被调用函数中 this 的值
    • 语法:fun.apply(thisArg, [argsArray])
      • thisArg:在fun函数运行时指定的 this 值
      • argsArray:传递的值,必须包含在数组里面
      • 返回值就是函数的返回值,因为它就是调用函数
      • 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
  • bind()
    • bind() 方法不会调用函数指定被调用函数中 this 的值,返回新函数
    • 语法:fun.bind(thisArg, arg1, arg2, ...)
      • thisArg:在 fun 函数运行时指定的 this 值
      • arg1,arg2:传递的其他参数
      • 返回由指定的 this 值和初始化参数改造的 原函数拷贝 (新函数)
      • 因此只改变 this 指向,不调用函数时,使用 bind,比如改变定时器内部的this指向.

call案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>call</title>
</head>

<body>
    <script>
        const obj = {
            uname: 'pink'
        }

        function fn(x, y) {
            console.log(this) // window
            console.log(x + y)
        }

        // 1. 调用函数  
        // 2. 改变this指向obj,原来是window调用指向window
        // 3. 返回值就是函数的返回值
        fn.call(obj, 1, 2)
    </script>
</body>

</html>

apply案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>apply</title>
</head>

<body>
    <script>
        const obj = {
            age: 18
        }

        function fn(x, y) {
            console.log(this)
            console.log(x + y)
        }
        // 1. 调用函数
        // 2. 改变this指向
        // 3. 返回值就是函数的返回值
        fn.apply(obj, [1, 2])

        // 使用场景:求数组最大值最小值
        const arr = [100, 44, 77]
        const max = Math.max.apply(Math, arr)
        const min = Math.min.apply(null, arr)
        console.log(max, min)
    </script>
</body>

</html>

bind案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bind</title>
</head>

<body>
    <button>发送短信</button>
    <script>
        const obj = {
            age: 18
        }

        function fn() {
            console.log(this)
        }

        // 1. bind不会调用函数 
        // 2. 能改变this指向
        // 3. 返回值是个函数,但是这个函数里面的this是更改过的obj
        const fun = fn.bind(obj)
        fun()

        // 需求:有一个按钮,点击里面就禁用,2秒钟之后开启
        document.querySelector('button').addEventListener('click', function() {
            // 禁用按钮
            this.disabled = true
            window.setTimeout(function() {
                    // 在这个普通函数里面,我们要this由原来的window 改为 btn
                    this.disabled = false
                }.bind(this), 2000) // 这里的this 和 btn 一样
        })
    </script>
</body>

</html>

4、性能优化

(1)防抖

  • 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
  • 使用场景:搜索框输入,设定每次输入完毕n秒后发送请求,如果期间还有输入,则重新计算时间

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>防抖</title>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // 防抖函数
        function debounce(fn, t) {
            let timeId
            return function() {
                // 如果有定时器就清除
                if (timeId) clearTimeout(timeId)

                // 开启定时器
                timeId = setTimeout(function() {
                    fn()
                }, t)
            }
        }
        //鼠标移动触发函数
        box.addEventListener('mousemove', debounce(mouseMove, 1000))
    </script>
</body>

</html>

(2)节流

  • 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
  • 使用场景: 鼠标移动,页面尺寸发生变化,滚动条滚动等开销比较大的情况下

案例如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>节流</title>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // 节流函数
        function throttle(fn, t) {
            // 起始时间
            let startTime = 0
            return function() {
                // 得到当前的时间
                let now = Date.now()

                // 判断如果大于等于 500 调用函数
                if (now - startTime >= t) {
                    // 调用函数
                    fn()

                    // 起始的时间 = 现在的时间
                    startTime = now
                }
            }
        }
        box.addEventListener('mousemove', throttle(mouseMove, 1000))
    </script>
</body>

</html>

(3)lodash节流和防抖

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>lodash节流和防抖</title>
    <style>
        .box {
            width: 500px;
            height: 500px;
            background-color: #ccc;
            color: #fff;
            text-align: center;
            font-size: 100px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script src="./lodash.min.js"></script>
    <script>
        const box = document.querySelector('.box')
        let i = 1

        //数值加1
        function mouseMove() {
            box.innerHTML = ++i
        }

        // lodash 节流
        // box.addEventListener('mousemove', _.throttle(mouseMove, 500))
        // lodash 防抖
        box.addEventListener('mousemove', _.debounce(mouseMove, 500))
    </script>
</body>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/589360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows里怎么杀死一个进程?

我们可以使用 taskkill 命令&#xff0c;可以使用该工具按照进程 ID (PID) 或映像名称终止任务。 显示帮助消息&#xff1a; taskkill /?参数列表&#xff1a; /S&#xff1a;system&#xff1a;指定要连接的远程系统。/U&#xff1a;[domain\]user&#xff1a;指定应该在哪…

【ESP-01S / ESP8266 AT指令连接阿里云物联网平台】

ESP-01S / ESP8266 AT指令连接阿里云物联网平台 阿里云物联网平台新建设备获取AT参数 AT指令介绍连接阿里云AT指令介绍MQTT固件固件下载硬件连接固件烧录 串口助手调试硬件连接测试指令 AT_Command移植总结问题排查 源码获取 关注星标公众号&#xff0c;不错过精彩内容 作者 | …

【简单实用框架】【十大排序算法直接调用】【可移植】

☀️博客主页&#xff1a;CSDN博客主页&#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2;&#x1f525;学习专栏推荐&#xff1a;面试汇总❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&#…

JeecgBoot企业级开发中实现自定义导出EXCEL的前端表格字段功能

文章目录 如何在后端实现导出前端列表字段到Excel功能需求前端的实现1. 提供一个导出的点击函数2.引入组件中的userMethod3.tableProps中导出中添加对应的查询参数4. 编写导出函数 后端逻辑的实现1.Controller层2.创建Modal类3.Sevice层 检验成果总结 如何在后端实现导出前端列…

【Android Gradle 插件】更新依赖方式,同时解决github三方库引用无法使用问题

首先看一下完整的 settings.gradle 依赖介绍 /* pluginManagement 脚本块,用于配置Gradle插件的Maven仓库,配置的是构建过程中,使用的仓库 ; pluginManagement 脚本块中的 repositories 配置 , 对应之前的 buildscript 中的 repositories 配置 ; */ pluginManagement {reposit…

技术分享 | 一文了解 MySQL Optimizer Trace 的神奇功效

作者&#xff1a;Mutlis CSDN & 阿里云 & 知乎 等平台优质作者&#xff0c;擅长Oracle & MySQL等主流数据库系统的维护和管理等 本文来源&#xff1a;原创投稿 前言 对于 MySQL 5.6 以及之前的版本来说&#xff0c;查询优化器就像是一个黑盒子一样&#xff0c;…

迪赛智慧数——柱状图(基本柱状图):购买雪糕考虑的因素

效果图 冰淇淋季节来袭&#xff0c;因其细腻凉爽的口感和浓郁的口味被广大消费者所钟爱&#xff0c;近年来已经从一款传统的解暑冷冻饮品转变为一种原料丰富、口味多元、追求健康、愉悦和高品质生活方式的休闲食品。据数据显示&#xff0c;82.2&#xff05;女性、82.3%男性消费…

chatgpt赋能python:Python中乘方的介绍

Python中乘方的介绍 在Python中&#xff0c;乘方运算指数运算&#xff0c;常用符号为“”&#xff08;例如2的3次方为23&#xff09;。使用乘方运算可以快速地进行数值计算&#xff0c;尤其是在科学和工程领域中。 为什么要使用乘方运算&#xff1f; 乘方运算主要用于处理大…

NeRF算法

Instant-ngp Instant-ngp简单介绍 Instant-ngp论文链接 英伟达实现的github链接 taichi实现Instant-ngp taichi实现的github链接 渲染 采用体素渲染方法&#xff0c;从相机光线出发&#xff0c;逐步采样3D场景中的三维坐标点的颜色&#xff0c;即可渲染出3D画面。如果直接将3…

进阶神册,Redis+Nginx+设计模式+Spring全家桶+Dubbo核心技术笔记

最近花了很长的时间去搜罗Java核心技术好文&#xff0c;我把每个Java核心技术的优选文章都整理成了一个又一个的文档。昨天也是终于全部整理好了&#xff0c;今天就把这些东西分享给老铁们&#xff0c;也能为老铁们省去不少麻烦&#xff0c;想学什么技能了&#xff0c;遇到哪方…

vue3组件通信详解

vue3组件通信方式有以下几种&#xff1a;porps&#xff0c;$emit&#xff0c; bus&#xff0c;v-model&#xff0c;useAttrs&#xff0c;$ref/$parent&#xff0c;provide/inject&#xff0c;pinia&#xff0c;slot。下面将逐一讲解。 目录 1.porps&#xff1a;实现父子组件通…

V7.0_增加消息队列功能

一&#xff0c;功能描述 增加消息队列&#xff1b;使用自定义copy功能时&#xff0c;子进程copy结束后向父进程发送消息&#xff08;通过消息队列&#xff09;然后exit&#xff1b;此时因wait&#xff08;&#xff09;而处于阻塞态的父进程终于解除了阻塞并且从队列中读取到消…

【云原生-K8s-1】kubeadm搭建k8s集群(一主两从)完整教程及kubernetes简介

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录 Kubernetes简介1 kubernetes架构1.1m…

清凉一夏小风扇-React版

这里写目录标题 前言 一、效果二、代码分享三、总结 前言 本片文章主要是做一个小练习&#xff0c;通过react来制作一个风扇练习css动画。 vue3实现部分看这里–> 一、效果 二、代码分享 1、主体框架 “react”: “^18.2.0” “sass”: “^1.62.1” 2、主要技术点 使用事…

企业上云容灾如何实现碳中和?

随着能源成本的增加和数据消费的激增&#xff0c;“电耗”和“碳排放”成为今年世界移动通信大会热议的话题。目前&#xff0c;ICT行业耗电量约占全球用电量的7%。预计到2040年&#xff0c;ICT行业碳排放量占全球排放量的比例将上升至14%。 容灾是企业为了在灾难时保证业务继续…

基于html+css的图展示99

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Log4j2 - JNDI 注入漏洞复现(CVE-2021-44228)

文章目录 Apache Log4j简介漏洞介绍影响版本漏洞编号影响组件应用 环境准备靶场搭建漏洞利用利用工具使用方式 反弹shell操作 漏洞修复建议 Apache Log4j简介 Apache log4j 是 Apache 的一个开源项目&#xff0c; Apache log4j2 是一个 Java 的日志记录工具。该工具重写了 log4…

03.hadoop上课笔记之hdfs环境的搭建和使用

1.启动网络 在windows任务管理器启动服务vm Dhcp #由动态ip变为静态 #启动网卡ifup ens33#修改网卡配置文件vi /etc/sysconfig/network-scripts/ifcfg-ens33BOOTSTRAPstaticIPADDR192.168.202.101NETMASK255.255.255.0GATEWAY192.168.202.2DNS1192.168.202.2#重启网络 servic…

Vue动态路由在实际项目中的应用(包含前后台细节)

背景 近期做一个公司的门户网站&#xff0c;在产品和新闻这一块需要用到动态路由。本节博客以产品板块为例说一下动态路由的应用。 另外如果路由相关的基础知识有问题&#xff0c;可以查看我的这篇博客&#xff1a; Vue2路由的详细讲解 另外&#xff0c;这篇博客也会涉及到一…

macOS visual studio code 没有读写权限 检查更新报错

问题描述 visual studio code 检查更新&#xff0c;报错&#xff0c;visual studio code没有磁盘读写权限。&#xff08;可能会导致插件安装报错&#xff1f;&#xff09; 报错&#xff1a;The application is on a read-only volume. Please move the application and try a…