午夜,办公室里透着电脑屏幕散发出微弱的光,安静得只能听到从某个角落里发出的敲击键盘的声音,或许是还有其他人在陪着我一块加班吧。
因加班劳累的我正趴在办公桌上呼呼大睡,“叮咚叮咚”……忽然一阵急促的门铃声把我吵醒,我迷迷糊糊睁开眼,看了一眼挂在墙壁上的钟——竟已凌晨两点了,透过玻璃门,在伸手不见五指的走廊尽头,只见一个黄衣加身、手提塑料袋的小哥,原来是点的外卖到了。
我按下门锁,小哥向我走来,我准备接过小哥手中的外卖,突然感觉到一股力量在与我对抗,小哥紧紧攥住手中的塑料袋,楼道的光打在他的头顶,我看见他那比我人生规划还要清晰的下颌线,小哥露出一丝狡黠的笑。
👷 “就凭你,也配坐在这个办公室里?”
👧 “我可是通过层层考验才有资格坐在这里的!” 我的睡意褪去了半分。
👷 “连数组和对象的操作方法都说不出十个,你好意思坐在这里吗?”
👧 “瞧不起谁呢你?我可是有着三年工作经验的前端工程师!”
操作数组常用的几种方法
👷 “你先随便说几种能遍历数组的方法。”
1. easy,说到遍历,当然我们立马会想到的就是 forEach:循环遍历
- forEach 用于循环遍历数组,是最常用的方法之一,它提供一个回调函数,可以用来处理数组的每一个元素,默认没有返回值。回调函数的第一个参数是数组的每一项,第二个参数是数组中每一项的下标。
var arr = [1,2,3,4,5,6]
arr.forEach((item,index) => {
console.log(item,index)
})
2. 其实 map 也可以做到
- map 方法返回一个新数组,不会对空数组进行检测、不会改变原数组,按照原始数组元素顺序依次处理元素
let person = [{
name:'张三',
age:18,
sex:1
},{
name:'李四',
age:20,
sex:1
},{
name:'王菲',
age:25,
sex:0
}]
let newPerson = person.map(item => {
return {
name: item.name,
sex: item.sex
}
})
console.log(newPerson)
// [{
// name:'张三',
// sex:1
// },{
// name:'李四',
// sex:1
// },{
// name:'王菲',
// sex:0
// }]
3. filter 的同时当然也是一个在遍历的过程 filter:过滤数组
- filter 返回值是一个新的数组
- filter 可以直接 return 筛选条件
var arr = [10,20,30]
let newArr = arr.filter((item,index,array) => {
return item >= 20
})
console.log(newArr)
4. concat:拼接数组
- 用来拼接数组并且会返回一个新数组,该方法不会改变原数组。
var arr1 = [1,2,3]
var arr2 = [4,5,6]
var arr3 = arr1.concat(arr2)
console.log(arr3) // 1,2,3,4,5,6
5. every,一个容易和 some 搞混的方法
- every 遍历数组,一般用于判断数组中是否有满足条件的数组元素,所有元素遍历后都满足条件则返回 true ,只要有一项不满足则返回 false
let arr = [1,2,3,4,5,6]
let result1 = arr.every(item => item > 0)
console.log(result1) // true
let result2 = arr.every(item => item > 1)
console.log(result2) // false
6. some,一个容易和 every 搞混的方法
- some 和 every 方法非常类似,在遍历所有元素后,只要有一项元素满足条件则返回 true,全部不满足则返回 false
let arr = [1,2,3,4,5,6]
let result1 = arr.some(item => item > 2)
let result2 = arr.some(item => item < 0)
console.log(result1,result2) // true false
7. 再来个高级的用法 reduce:数组求和
- reduce 方法可以做的事情特别多,遍历循环能做的它都可以做,比如:数组求和、数组求积、数组中元素出现的次数、数组去重等等
语法:
arr.reduce(function(prev,cur,index,arr){
...
},init)
参数:
- prev 必需:累计器累计回调的返回值,表示上一次调用回调时的返回值,或者初始值init
- cur 必需:表示当前正在处理的数组元素
- index 可选:表示当前正在处理的数组元素的索引,若提供了init值,则其实索引为0,否则为1
- arr 可选:表示原数组
- init 可选:表示初始值
// 举一个简单常用的求和栗子
var arr = [1,2,3,4,5]
let sum = arr.reduce((sum,val)=> sum + val,0)
console.log(sum) // 15
8. 容易混淆的方法 find 和 findIndex:查找数组中第一个满足条件的属性并
- find 找到第一个满足条件的值
- findIndex 方法返回的是第一个满足条件元素的下标
let arr = [25,35,12,8,40,12,72,86]
let value1 = arr.find(item => {
return item > 50
})
let value2 = arr.findIndex(item => {
return item > 50
})
console.log(value1) // 72 找到值
console.log(value2) // 6 找到下标
9. 很多人都不知道区别的 substring 和 substr:截取字符串
- 相同点:只传一个参数两者作用相同
let arr = '123456789'
console.log(arr.substr(5)) // '6789'
console.log(arr.substring(5)) // '6789'
- 不同点:传两个参数
- substr(startIndex,length) 第二个参数是从起始点开始计算所截取的字符串的长度
- substring(starIndex,endIndex) 第二个参数是截取的字符串最终下标
let arr = '123456789'
console.log(arr.substr(2,5)) // '34567'
console.log(arr.substring(2,5)) // '345'
10. 一个常用方法 splice:删除数组中的属性
- splice 方法可以删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素,该方法
会直接对数组进行修改
let arr = ['a','b','c','d','e','f','g']
arr.splice(0,1,'aaa')
console.log(arr) // ['aaa','b','c','d','e','f','g']
11. 数组嘎巴机 slice:截取元素
- slice 会返回一个新的数组,返回选定的元素,不会修改原数组
let arr = ['a','b','c','d','e','f','g']
let newArr = arr.slice(0,2)
console.log(arr) // ['a','b','c','d','e','f','g']
console.log(newArr) // 返回的是被截取的元素 ['a','b']
12. join:把数组中所有元素放入一个字符串
- 把数组中所有元素放入一个字符串,元素之间通过指定的分隔符进行分割
let arr = ['H','E','L','L','O']
let str = arr.join('')
console.log(str) // HELLO
13. push:从数组末尾追加
- push 从数组末尾添加项,返回值是数组的长度,
会改变原数组
let arr = [1,2,3,4,5,6]
console.log(arr.push(7,8)) // 8 arr 数组的长度
console.log(arr) // [1,2,3,4,5,6,7,8]
14. unshift:从数组前面添加
- unshift 从数组前面添加,返回值是数组的长度,
会改变原数组
let arr = [1,2,3,4,5,6]
console.log(arr.unshift(7,8)) // 8 arr 数组的长度
console.log(arr) // [7,8,1,2,3,4,5,6]
15. pop:从数组末尾删除
- pop 从数组末尾删除,返回值是删除的元素,
会改变原数组
let arr = [1,2,3,4,5,6]
console.log(arr.pop()) // 6 删除的值
console.log(arr) // [1,2,3,4,5]
16. reverse:反转数组
- reverse 作用是将数组翻转
let arr = [1,2,3,4,5,6]
console.log(arr.reverse()) // [5,4,3,2,1]
17. sort:数组排序
- sort 方法按照 Unicode code 位置排序,默认升序
let str = ['e','z','o','k','v']
let num = [5,8,1,3,0,9]
console.log(str.sort()) // ["e", "k", "o", "v", "z"]
console.log(num.sort()) // [0, 1, 3, 5, 8, 9]
18. indexOf 和 lastIndexOf:查找
- indexOf 和 lastIndexOf 都接受两个参数L查找的值、查找的起始位置,不存在时返回 -1;存在返回下标位置
- indexOf是从前往后查找,lastIndexOf是从后往前查找
let arr = [2,7,6,8,5,7,1]
// 传入一个参数
console.log(arr.indexOf(4)) // -1 不存在
console.log(arr.indexOf(7)) // 1 找到的第一个元素的下标位置
// 传入两个参数
console.log(arr.indexOf(7,3)) // 5
👷 “可以了可以了,再说就不礼貌了。”
👧 “数组的操作就讲到这里,等我想起来再告诉你,接下来讲讲对象的操作。”
对象的操作
1. 每一位 VUE user 刻在DNA里的方法 Object.defineProperty(obj,porperty,detail)
- 作用是在一个对象上定义一个新属性或者修改一个对象的现有属性并返回这个对象,vue做数据拦截使用
let student = {}
let studentName = '张三'
Object.defineProperty(student,'name',{
get: function() {
console.log('触发get方法')
return studentName
},
set: function(val) {
console.log('触发set方法')
studentName = val
}
})
// 读取person对象的name属性时触发get方法
console.log(student.name)
// 对student.name进行修改,触发set方法
student.name = '李四'
console.log(student.name)
👷 “你能说出如何判断两个对象是否相同么?”
👧 “用 === 不就行了”
👷 “不够精确,如果你能说出另一种更严谨的方法我就承认你是有着三年开发经验的前端!”
2. 那就得搬出一个我不常用的方法 Object.is()
- Object.is()是一种判断两个值是否相同的方法,与严格相等运算符(===)几乎相同
- Object.is()在===的基础上修复了一些特殊情况下的失误:+0和-0、NaN和NaN
Object.is(1,1) // true
Object.is(1,'1') // false
Object.is(1,true) // false
👧 “……嘿,我为什么要得到你的肯定 - -”
3. Object.create()
- 使用指定对象作为原型去创建一个新的对象,并可以为新对象直接添加属性
var obj = {
a:1,b:2
}
var obj1 = Object.create(obj)
console.log(obj1) // {} 空对象
console.log(obj2.a) // 1 可以访问到原对象中的属性 说明原对象是新对象的原型
4. 再说一个很常用的方法 Object.assign()
- 复制与合并 Object.assign 用于将所有可枚举的自身属性从一个或多个源对象复制到目标对象,语法:Object.assign(target,...sources)
let obj1 = { a:1 }
let obj2 = Object.assign({},obj1)
console.log(obj2) // { a:1 }
// 合并多个多选
const obj = {
firstname:"John",
lastname:"Doe",
age:50
};
const obj2 = { other: "cat" };
const obj3 = { car: "Benz" };
const obj4 = Object.assign(obj1,obj2,obj3)
console.log(obj4) // ...有点复杂 不打印了
5. Object.values()
- Object.values() 会返回一个由一个给定对象的自身可枚举属性值组成的数组,和Object.keys()相似,这个在下面会提到!
let obj = {a:1,b:2}
Object.values(obj) // [1,2]
6. Object.entries()
- Object.entries()方法返回一个对象:key 和 value 键值对组成的数组
const obj = { a:1,b:2 }
console.log(Object.entries(obj)) // [['a',1],['b',2]]
7. 使用频率相对高的一个方法 obj.hasOwnProperty()
- 判断对象中某个属性是否存在
const obj = {
name: '张三',
age: 20,
sex: 'male'
}
console.log(obj,hasOwnProperty('name')) // true
👷 “你能说出三种枚举对象属性的方法么?”
8. for in 在我的脑海中一闪而过,它会遍历对象中所有可枚举的属性,包括自有属性和继承属性
const obj = {
itemA: 'itemA',
itemB: 'itemB'
}
var newObj = Object.create(obj)
newObj.newItemA = 'newItemA'
newObj.newItemB = 'newItemB'
for(i in newObj) {
console.log(i) // newItemA newItemB itemA itemB
}
// 现在将其中一个属性变为不可枚举
Object.defineProperty(newObj,'newItemA',{ enumerable: false })
for(i in newObj) {
console.log(i) // newItemB itemA itemB
}
9. 第二个 Object.keys()
- Object.keys() 会返回一个由一个给定对象的自身可枚举属性组成的数组
let obj = {a:1,b:2}
Object.keys(obj) // [a,b]
10. 小样,三个不是轻轻松松,最后一个 Object.getOwnPropertyNames():不论是否可枚举都会返回自有的属性名称
const obj = {a:1,b:2}
const result = Object.getOwnPropertyNames(obj) // [a,b]
👷 “如果我想要一个对象再也不能被修改该如何处理?”
10. 那就不得不说说冻结了 Object.freeze()
- Object.freeze() 可以冻结一个对象,一个被冻结的对象再也不能被修改,冻结一个对象不能向对象添加、删除、修改……
const obj = {a:1,b:2}
Object.freeze(obj)
obj.a = 10086
console.log(obj.a) // 1
再补充一下,非常重要的一个东东,有点类似于 深浅拷贝 的概念:
- 被冻结的对象是不可变的,但也不总是这样,下面介绍一下浅冻结
const obj = {
objChild:{}
}
Object.freeze(obj)
obj.objChild.name = '张三'
console.log(obj.objChild.name) // 张三
- 要使对象不可变,需要递归冻结每个类型为对象的属性,这叫深冻结
// 写一个深冻结方法
function deepFreeze(obj) {
const propNames = Object.getOwnPropertyNames(obj)
// 冻结自身之前冻结属性
propNames.forEach(function(name){
const prop = obj[name]
if(type prop == 'object[ && prop != null)
deepFreeze(prop)
})
return Object.freeze(obj)
}
const obj = {
objChild:{}
}
deepFreeze(obj)
obj.objChild.name = '张三'
console.log(obj.objChild.name) // undefined
👷 “那如何……”
👧 “额,很遗憾,在浏览器规范中这个是不可逆的^ ^”
👷 “行,算你小子厉害!我看你一副大聪明模样,一看就不知道怎么判断一个对象是否为空”
👧 “嗨嗨,你未免太瞧不起人了,试问哪个三四年的前端回答不上来!上面提到的枚举属性的方法不就可以判断了么!再不济您就用 JSON.stringify(),总有一款适合您。”
👷 “可以,伶牙俐齿,来美团上班吧,做我小弟,我带你。”
👧 “是美团!我可以吗?🤩”
👷 “当然啊!”
说罢,小哥递给我一个头盔和一件大衣,还给我配了一辆电动车 🛵。