最近参与了公司的 Vue + ElementUI 后台管理系统开发,目前项目告一段落,正好做一个总结。
本文件分三部分进行,其一是 JavaScript 部分,其二是 Vue 部分,最后是 ElementUI 部分。
一、对JavaScript的理解
1. 如何复制一个Js对象?
在 C#,Java 等面向对象的高级语言中,可以直接通过 '='
赋值操作符复制一个对象,如果想定义一个指向对象的代理要用到指针定义,然而 Js 早期出于性能的考虑,默认将 '='
赋值操作符用于定义一个指向对象的指针。
var a = { prop: 'val' }var b = aa.prop = 'nothing'console.log(b.prop) // 'nothing'复制代码
无论怎样 “复制”,新对象总是指向原始对象,原始对象发生改变,新对象也自然改变。如何复制对象呢?我们知道,对象可以互相嵌套,即对象属性可以是另一个对象,那么通过普通的循环遍历来复制原对象的属性到一个新对象中是无法确保两者保持一致的,因此这种复制也被称作 “浅拷贝”。相对的通过递归遍历来复制对象的方式也被称作 “深拷贝”。
// 浅拷贝function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } c.uber = p; return c;}复制代码
// 深拷贝function deepCopy(p, c) { var c = c || {}; for (var i in p) { if (typeof p[i] === 'object') { c[i] = (p[i].constructor === Array) ? [] : {}; deepCopy(p[i], c[i]); // 递归调用 } else { c[i] = p[i]; } } return c;}复制代码
当然,可以在项目中引入函数库(如 utils.js
)来方便的引用这些扩展函数,不过我们不得不想有没有更简洁的方式来实现呢?答案是肯定的。
// utils.jsexport const extendCopy(p) {...}export const deepCopy(p, c) {...}复制代码
// page.jsimport {extendCopy, deepCopy} from utils.jsa = {...}b = extendCopy(a)复制代码
一般还可以通过如下方式解决:
- 通过 JSON 对象提供的
stringify
和parse
方法组合使用的方式实现。
// 勉强也算一句话解决了,问题在于性能不理想。var a = {}var b = JSON.parse(JSON.stringify(a))复制代码
- 通过在函数中以返回值的形式生成新对象。
// 这种方式的缺点在于要以字面量的形式定义一个原始对象作为模板。a = function() { return { prop:"123" } }b = a()d = a()b === d // false复制代码
- 通过 ES2015 中提供的
Object.assign()
方法来复制。
// 简洁明了,性能良好的解决方案var a = {}var b = Object.assign({},a)复制代码
通过上面的例子,我们不难得出一个结论:使用 Object.assign()
方法复制一个对象是比较理想的解决方案。
2. 如何判断一个元素是否在集合中
早期的 Js 标准中并没有实现类似 array.contains(item)
的方法,因此只能通过元素在集合中的索引判断。(感谢 XiaozhongLiu 的提醒,ES7中已加入 Array.includes()
方法。)
// 传统写法var isSelected = function(row) { return (selection || []).indexOf(row) > -1;}// ES7写法,简洁了许多isSelected = selection.includes(row)复制代码
3. 判断一个变量是否为非空对象
首先 null 的类型是对象,如果在控制台输入 typeof null
会返回 "object"
,事实上我们要检测一个非空数组,就要先排除 null
值的可能。
var isObject = function(obj) { return obj !== null && typeof obj === 'object'}复制代码
4. 如何查询一个数组并返回元素的子集
这里用到了数组的 filter
方法,此方法具有以下特点:
- 需要传入一个回调函数作为参数,此函数应返回一个布尔值。
filter
执行时会对数组中的每一个元素执行一遍回调函数。filter
的返回值为一个新数组,其成员为原数组中返回值为true
的元素。
var users = allUsers.filter((user) => user.loged === true)复制代码
5. 如何判断一个对象没有任何属性
在 Js 中两个空对象进行比较会有什么结果呢?
var a = {}a === {} // false复制代码
在 Js 中对 object
类型的数据应用 “等于” 操作符时,比较的是两个变量是否指向同一个引用,也就是说在内存中是否为同一个地址。
而我们期待的结果是比较两个对象在逻辑上是否具有相同的属性,空对象是一个特例,它们不具有任何属性。
if (Object.keys(search).length !== 0) { args = Object.assign({}, args, search)}复制代码
这里用到 Object.keys()
方法,获取对象键的集合并判断其长度是否为 0
即可。
6. 如何使用循环删除数组中的多个元素
for (let i = 0, let j = delIds.length - 1; i < j; i++) { allItems.splice(delIds[i], 0)}复制代码
如上,根据索引来循环删除原始数组中的元素是否可行呢?如果我们执行这段代码是无法得到预期结果的。原因在于,当前面的元素被删除时,后面元素的索引发生了变化,继续用删除前的索引就无法定位正确的元素,解决的方法有两种:
- 通过倒序循环,获取被删除元素的索引,从后向前遍历删除。
for (let i = delIds.length - 1; i > 0; i--) { allItems.splice(delIds[i], 0)}复制代码
- 通过数组的两个方法即 forEach 配合 indexOf 删除,这种方法需要获取被删除元素本身,动态获取它的索引。
delItems.forEach ((item) => { allItems.splice(allItems.indexOf(item), 0)})复制代码
(未完待续...)