首页 文章 精选 留言 我的

精选列表

搜索[Web 应用托管],共10000篇文章
优秀的个人博客,低调大师

Web瞎捣鼓——浅拷贝与深拷贝

今天要研究一下Object.assign()这个方法,有人说它是浅拷贝,它真的只能做浅拷贝吗? 浅拷贝?深拷贝? 浅拷贝:将源对象(或数组)的引用直接赋值给新对象(或数组),也就是它操作的是同一个内存地址。 深拷贝:将源对象(或数组)的属性值(或数组元素)复制到新对象(或数组),它操作的是一个新的内存区域。 刚刚查浅拷贝和深拷贝的概念,翻到一篇文章说:浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。 阅读量3.2k,这得多少人“受益”,我的表情是这样的: 关于深拷贝和浅拷贝,拿数组举个例子: // 浅拷贝 let sourceArr = [1, 2, 3] let shallowCopyArr = sourceArr sourceArr[0] = 4 shallowCopyArr[1] = 5 console.log(JSON.stringify(sourceArr)) // [4,5,3] console.log(JSON.stringify(shallowCopyArr)) // [4,5,3] // 深拷贝 let deepCopyArr = [...sourceArr] sourceArr[0] = 6 deepCopyArr[1] = 7 console.log(JSON.stringify(sourceArr)) // [6,5,3] console.log(JSON.stringify(deepCopyArr)) // [4,7,3] 浅拷贝和深拷贝的目的很明显是截然相反的。有时我们只是需要另一个对象的引用,比如Vue的mixins混入(抠着脚丫子想了半个小时,还是没想到合适的例子)。有时我们希望在创建一个副本来进行修改而不影响源数据,比如有一组参数要传给后台,参数里有个时间需要毫秒,而时间选择器组件则依靠日期对象回显,这时传给后台的数据就需要深拷贝单独处理。 我们在开发中用到的看似都是深拷贝,于是就有人问,为什么还会有浅拷贝这个存在?当然,在数据处理方面用深拷贝的时间多一些,但在其它方面或许用到了浅拷贝却不知道。比如封装的方法对象,它是深拷贝吗? 浅拷贝就不用多说,直接赋值就是浅拷贝,需要注意的是有些地方看似深拷贝实际是浅拷贝,这个在之前的篇章中提到过。下面就来看下深拷贝。 数组的深拷贝 遍历(for循环、数组迭代方法内部、其他遍历方式) let sourceArr = [1, 2, 3] let copyArr = [] for (let i = 0; i < sourceArr.length; i++) { copyArr.push(sourceArr[i]) } sourceArr[0] = sourceArr.length * 10 copyArr[1] = copyArr.length * 10 console.log(JSON.stringify(sourceArr)) // [30,2,3] console.log(JSON.stringify(copyArr)) // [1,30,3] Array原型方法 // Array.prototype.slice() - 抽取当前数组中的一段元素组合成一个新数组。 copyArr = sourceArr.slice() // Array.prototype.concat() - 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。 copyArr = sourceArr.concat() // Array.prototype.filter() - 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。 copyArr = sourceArr.filter(v => true) // Array.prototype.map() - 返回一个由回调函数的返回值组成的新数组。 copyArr = sourceArr.map(v => v) JSON转换 copyArr = JSON.parse(JSON.stringify(sourceArr)) 扩展运算符 copyArr = [...sourceArr] 以上方法部分只能拷贝单个数组,除此外,如果把数据换成复杂类型结果会怎样? let sourceArr = [{a: 1}, {b: 2}, 3] let copyArr = [] // 数组的深拷贝方法 sourceArr[0].a = sourceArr.length * 10 copyArr[1].b = copyArr.length * 10 最终结果是所有方法几乎没有完成任务,只有JSON转换完美搞定。接下来看看另一个数据类型的深拷贝。 对象的深拷贝 数组的深拷贝拷贝方式中有三种也适用于对象: 遍历(for循环、其他遍历方式) let sourceObj = {a: 1, b: 2} let copyObj = {} for (let k in sourceObj) { copyObj[k] = sourceObj[k] } console.log(JSON.stringify(sourceObj)) console.log(JSON.stringify(copyObj)) sourceObj.a = 3 copyObj.b = 4 console.log(JSON.stringify(sourceObj)) // {"a":3,"b":2} console.log(JSON.stringify(copyObj)) // {"a":1,"b":4} JSON转换 copyObj = JSON.parse(JSON.stringify(sourceObj)) 扩展运算符 copyObj = {...sourceObj} 除以上方法外,还有Object的方法,比如assign(),它是分情况的,后面来讨论。 copyObj = Object.assign({}, sourceObj) 接下来将数据换为复杂的数据结构: let sourceObj = {a: [1], b: [2]} let copyObj = {} // 对象的深拷贝方法 sourceObj.a[0] = 3 copyObj.b[0] = 4 结果和数组一样,只有JSON转换成功的完成了深拷贝,而且对数组和对象都有效。当然通用不一定就好,合适的时候用合适的方法解决问题才是正确的选择。然而,我们通常遇到的会是这种复杂的数据结构,难道这是唯一的解决办法?从两种类型的所用方法来看,它们有三种方法是相近的,其中就有JSON转换。并且复杂的数据结构也是凭借这两种数据类型组成,那么,另外两种方式有没有办法改造成有效的深拷贝方法呢?当然,我们试一下: // 声明:方法修改自jQuery // 因为jQuery考虑有很多情况,所以有很多方法封装 // 本着实验的态度,直接copy了源码,我的封装没考虑变数 // 就是懒,原谅我 let class2type = {} let toString = class2type.toString let hasOwn = class2type.hasOwnProperty let fnToString = hasOwn.toString let ObjectFunctionString = fnToString.call(Object) let getProto = Object.getPrototypeOf // 检查是否为普通对象 function isPlainObject(obj) { let proto, Ctor if (!obj || toString.call(obj) !== '[object Object]') return false proto = getProto(obj) if (!proto) return true Ctor = hasOwn.call(proto, 'constructor') && proto.constructor return typeof Ctor === 'function' && fnToString.call(Ctor) === ObjectFunctionString } // 转换类型 function toType(obj) { if (obj == null) return `${obj}` return typeof obj === 'object' ? class2type[toString.call(obj)] || 'object' : typeof obj } // window对象判断 function isWindow(obj) { return obj != null && obj === obj.window } // 检查类数组 function isArrayLike(obj) { let length = !!obj && obj.length, type = toType(obj); if (typeof obj === "function" || isWindow(obj)) return false return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj } // 深拷贝封装 // 第一个参数是目标对象(数组),第二个参数为拷贝数据 // 这个方法和Object.assign()有点像,它实际是浅拷贝 // 就不去修改调整了,为后文说Object.assign()做个铺垫 function deepCopy(target, source) { let iterator if (Array.isArray(target)) { if (!Array.isArray(source)) { source = isArrayLike(source) ? Array.from(source) : [source] } iterator = source.entries() } else { if (!isPlainObject(target)) { target = {} } iterator = Object.entries(source) } for (let [k, v] of iterator) { let copy = isArrayLike(v) ? Array.from(v) : v let copyIsArray = Array.isArray(copy) let clone if (copy && (isPlainObject(copy) || copyIsArray)) { src = isArrayLike(target[k]) ? Array(target[k]) : target[k] if (copyIsArray && !Array.isArray(src)) { clone = [] } else if (!copyIsArray && !isPlainObject(src)) { clone = {} } else { clone = src } copyIsArray = false target[k] = deepCopy(clone, copy) } else if (copy !== undefined) { target[k] = copy } } return target } Object.assign()深拷贝?浅拷贝? 前面封装了一个deepCopy()的拷贝函数,来执行一下: let a = [1] let b = deepCopy(a) b[0] = 4 console.log(a, b) // 都是[4] let c = deepCopy([], a) c[0] = 5 console.log(JSON.stringify(a)) // [1] console.log(JSON.stringify(b)) // [5] 两种不同的方式,结果完全不一样,因为返回值和参数是同一个地址。Object.assign()也一样,第一个参数是目标对象,返回也是目标对象。但是第一个参数给一个新对象,就是深拷贝,这就有意思了,下面就来探索一下。前面做了许多铺垫,下面直接上代码看下Object.assign(),多给它创建几个数据,这样能更好的看出其中的变化。先来一组简单的数据类型: let t = {} let s1 = {i: 1} let s2 = {i: 2} let s3 = {i: 3} Object.assign(t, s1, s2, s3) t.i = 4 console.log(t, s1, s2, s3) // 依次输出{i: 4} {i: 1} {i: 2} {i: 3} t = Object.assign(s1, s2, s3) t.i = 5 console.log(t, s1, s2, s3) // 依次输出{i: 5} {i: 5} {i: 2} {i: 3} 前一种写法t对象没有影响其他对象,后一种写法s1对象被改变了。稍安勿躁,语法就是这样,其他参数对象的数据拷贝给第一个参数s1对象,返回s1对象给t。就像deepCopy()第二种写法,就和前一种写法结果一样了: t = Object.assign({}, s1, s2, s3) 看来简单数据一切正常,这个时候它是深拷贝的,换成复杂的数据类型再看看: let t = {} let s4 = { i: 4, d: { n: 4 } } let s5 = { i: 5, d: { n: 5 } } let s6 = { i: 6, d: { n: 6 } } Object.assign(t, s4, s5, s6) t.i = 7 t.d.n = 7 console.log(t, s4, s5, s6) // 依次输出: // {i: 7, d: { n: 7 }} // {i: 4, d: { n: 4 }} // {i: 5, d: { n: 5 }} // {i: 6, d: { n: 7 }} 这下问题来了,所有对象的简单数据类型都没受影响,object类型受到影响,对象s6的d属性被改变。这充分说明了这个方法是浅拷贝,深拷贝也就是对于这样的复杂数据结构。换个说法就是只拷贝了指针,第一次拷贝了s4的,第二次拷贝了s5,第三次拷贝了s6。所以,对象t对d属性进行修改,s6也被改变。 换成另一种写法,对象t作为返回值也一样,对象t和第一个参数对象一样,最后一个参数对象的属性d被改变。 总结 事实证明Object.assign()确实是浅拷贝,只能用它来合并对象(哈哈,我终于找到浅拷贝的用处了)。它和我的deepCopy()还是有差别的,那么我的deepCopy()问题出在哪儿呢?当只传一个参数的时候,就是将第一个参数直接返回给了新变量,是浅拷贝。(顺便说一下,这个函数封装用的是循环,扩展运算符是不是也可以封装深拷贝呢?我认为是可以的。) 在日常开发中一定要注意许多原生方法它执行的是深拷贝还是浅拷贝,不然神不知鬼不觉地做了错误的改变。我就犯过这样的低级错误:数据显示的是元,存数据库的是分,我用数组的map()方法进行遍历,回调函数带出的每个元素值用一个变量接收,将修改之后的值返回产生新数组。不幸的是数组存储的是对象,回调函数带出的参数只是一个引用,一不小心就将源数据给改了。改变数据的同时还造了一些废代码出来,得不偿失呀!

优秀的个人博客,低调大师

python web ide/jailkit python环境安装 搭建

PythonIDE基础软件安装 PythonIDE 基于开源修改,地址为: https://github.com/vimior/PythonWebIDE.git 对其增加了 关键词验证、输出过滤、同步登录等功能。 为了安全性考虑,将用户从系统上隔离(没有采用docker)。隔离软件为 jailkit,本文只介绍jailkit的软件安装以及基于这个软件的pythonide 环境安装。 以下操作均需要 root用户操作,需要切换用户的都作说明了,注意看 Jailkit 安装 版本为:2.21 wget http://olivier.sessink.nl/jailkit/jailkit-2.21.tar.bz2 tar xf jailkit-2.19.tar.bz2 ./configure --prefix=/usr/local/src/jailkit-2.21 make make install 配置受限环境 注:受限不目录能与安装目录相同 mkdir -p /app/ideuser 设置在受限环境中可以运行的命令 jk_init -v /app/ideuser netutils basicshell jk_lsh id whoami who logger 解释: 将基础命令 jk_lsh id whoami 等命令安装或初始化到受限目录(环境)中, 可参考安装目录下 /usr/local/src/jailkit-2.21/etc/jailkit/jk_init.ini 文件,此文件包含定义了基础命令有哪些.如果需要限是请在其中删除。 netutils 为网络相关包,为后期安装python环境使用 创建受限制的用户 useradd ide_guest passwd ide_guest #回车后输入二次密码 将用户放入限制环境中 jk_jailuser -j /app/ideuser/ -s /bin/bash ide_guest 至此用户限制完成,可以使用su ide_guest 来切换用户体验. jailkit python 环境安装 由于项目要求,必须要使用python,这个过程费了些劲 切换为root用户 不管你系统中是否有python环境,此处建议从新安装,如果完全知道python的安装目可跳过此步骤 下载python 找一个空白目录 wget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tgz tar zxvf Python-3.6.8.tgz cd Python-3.6.8 ./configure --prefix=/usr/local make make altinstall 不出意外 python安装完成 修改 jialkit_init 配置文件 文件位置 /usr/local/src/jailkit-2.21/etc/jailkit 增加如下代码,路径请修改成自己的 [python3.6] comment = python3.6.8 paths = /usr/local/python3,/etc/ld.so.conf devices = /dev/null, /dev/zero, /dev/random, /dev/urandom 执行 jk_init -v -j /app/ideuser python3.6 等待完成 切换 ide_guest 用户,此时会发现在/usr/local中多了 python3 配置环境变量 vi .bashrc export PYTHON_HOME=/usr/local/python3/ export PATH=$PATH:$PYTHON_HOME/bin 此时 python 就可以使用了.如果还是不行.请将 python3 做一个软件连接,进入sr/local/python3/ 目录,执行 ln -s python3 python 即可 安装python包 注意:此时可在ide_guest用户安装python相关包,请留意安装的提示信息,如发现权限不足,可通过root 用户不足的用户修改成可以权限,本人在安装过程中提示 没有/usr/xx/xxxx 等权限,此时不要退出ide_guest 用户,在重新clone 一个窗口,通过root 用户将受限环境中 usr赋权 777 ,待ide_guest 安装完之后在变更回来,不改回来 ide_guest 将不能登录(这就是不要退出用户的原因) 赋权限命令如下: chmod 777 -R usr 改回命令如下 chmod 755 -R usr 中文支持 jailkit 安装后,默认只支持英文.不支持中文,centos 操作如下: 修改ide_guest 环境变量 vi /app/ideuser/home/.bash_profile LANG=zh_CN.UTF-8 LANGUAGE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 export LANG LANGUAGE LC_ALL 复制系统字符语言到受限环境 cp /usr/lib/locale/locale-archive /app/ideuser/usr/lib/locale 目标目录如果不存在,请创建 mkdir -p /app/ideuser/usr/lib/locale 在次进行 su ide_guest 用户,就可以输入中文了.python 不能编写中文的问题也解决了 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128) 注意事项 如受限环境还需要增加命令,可通过 jk_cp -v -f /app/ideuser/ 命令 来实现. 好了 到这就安装完了.这软件确实是很方便.相当于一个独立的小系统了.如果要是其他的需求..比如:共享目录啥的,可通过mount 挂载命令来实现 二个环境中的文件共享

优秀的个人博客,低调大师

web-flash 1.3 发布,后台管理系统

本次有重大更新: 页面导航使用多标签页形式 左侧菜单完全由后台配置生成,无需配置路由 本次全部更新内容如下: Add 页面导航增加多标签页的方式 Add 左侧菜单完全通过后台配置动态生成 Add 基于vue-cli3构建脚手架,更精简合理的配置方式 Change 优化页面样式 Change 完善密码修改功能 Change 完善退出登录功能 Change 查询in条件中无法使用数组作为参数的问题 Fix 保存菜单出现异常:Converting circular structure to JSON Fix 权限编码配置错误的问题

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册