最近在熟悉新公司的一些基础服务和核心库的代码,说真的想吐槽一下JavaScript了,风格太灵活多变了,一样的操作可以用n多种方式或者语法实现,读代码太痛苦了。。

这不是上次面腾讯被问到defineProperty这种定义属性的方式和普通的. 操作有什么不同,刚好想起来我就搜了一下,给我惊到了。。


JavaScript对象属性定义

JavaScript文档

如何理解Object.defineProperty()?

基本的语法和操作没什么好说的。

除了defineProperty之外,还有definePropertiesgetOwnPropertyDescriptorgetOwnPropertyDescriptors等配套的方法。


defineProperty.操作的区别

原来区别还真的挺大的。。

  • 通过点操作符定义的属性,writable,configurable,enumerable值都为truevalue为赋入的值

  • 通过Object.defineProperty只指定value的属性,writable,configurable,enumerable值都为false


console.log(a+a+a) //“abc”问题

说实话这个题我是第一次遇到。。只能说一句JavaScript博大精深,太秀了,一般这么写业务代码会被打死吧。。

下面的代码,在浏览器控制台中可以直接使用,但在node.js环境中,要使用console.log(this.a + this.a + this.a),或者吧this全部换为global

代码出处


## JavaScript的遍历方式

for…in

for...in 其实是对象的遍历方式,不建议用来遍历数组,因为无法保证顺序

遍历对象

  • 环遍历对象本身的所有可枚举属性
  • 遍历对象从其构造函数原型中继承的属性
  • 遍历顺序与Object.keys()函数取到的列表一致

遍历数组

  • 不保证顺序
  • 会忽略空元素
  • 会遍历出来自定义属性
1
2
3
4
5
6
7
8
9
// for...in遍历数组的缺陷
var arr = [11, 22]
arr.name = 12313
for (const key in arr) {
console.log(key) // 0 1 name
}
for (const iterator of Object.keys(arr)) {
console.log(iterator) // 0 1 name
}

for…of

for...ofes6提供的一种通用的遍历方式,它可以遍历所有可迭代对象

遍历数组

  • 有序的
  • 不会忽略空值

和for…in的区别

  • 推荐在遍历对象时使用for...in,遍历数组使用for...of

  • for...in遍历出来的是keyfor...of遍历出来的是value

  • for...of不能遍历普通对象

1
2
3
4
5
6
// for...of修复了这个缺陷
var arr = [11, 22]
arr.name = 12313
for (const iterator of arr) {
console.log(iterator) // 11 22
}

forEach

forEach是数组的一个高阶函数

forEach和for…of遍历数组的区别

  • forEach中不能使用breakreturncontinue等语句。

map

map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

lodashmap功能一样呐好像。

map() 方法参数与forEach完全相同,二者区别仅仅在于map会将回调函数的返回值收集起来产生一个新数组。


filter

filter() 参数与forEach完全一致,不过它的callback函数应该返回一个真值或假值。


find/findIndex

find() 方法返回数组中使callback返回值为Truthy的第一个元素的值,没有则返回undefined

findIndex()方法与find方法很类似,只不过findIndex返回使callback返回值为Truthy的第一个元素的索引,没有符合元素则返回-1


every/some/

两个函数接收参数都与以上函数相同,返回都是布尔值。

every用于判断是否数组中每一项都使得callback返回值为Truthy

some用于判断是否至少存在一项使得callback元素返回值为Truthy


reduce/reduceRight 累加器

reducereduceRight的区别只是执行顺序相反。

reduce功能很强大,用法也可以很灵活多变。


基本语法

MDN web doc


扁平化数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function flatten(arr) {
return arr.reduce((prev, item) => {
return prev.concat(Array.isArray(item) ? flatten(item) : item)
}, [])
}

const arr = [1, 2, [3, 4, 5], [2, [1, 98, 21]]]
console.log(flatten(arr)) // [ 1, 2, 3, 4, 5, 2, 1, 98, 21 ]

// reduceRight
function flatten(arr) {
return arr.reduceRight((prev, item) => {
return prev.concat(Array.isArray(item) ? flatten(item) : item)
}, [])
}

const arr = [1, 2, [3, 4, 5], [2, [1, 98, 21]]]
console.log(flatten(arr)) // [ 21, 98, 1, 2, 5, 4, 3, 2, 1 ]

list to tree

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

let list = [
{ id: 1, parentId: '' },
{ id: 2, parentId: '' },
{ id: 3, parentId: '1' },
{ id: 4, parentId: '2' },
{ id: 5, parentId: '3' }
]

function listToTree (list) {
let nodeInfo = list.reduce((data, node) => {
node.children = []
data[node.id] = node
return data
}, {})
let result = []
for (const v of list) {
if (!v.parentId) {
result.push(nodeInfo[v.id])
continue
}
nodeInfo[v.parentId].children.push(nodeInfo[v.id])
}

return result
}
console.log(JSON.stringify(listToTree(list)))

//[{"id":1,"parentId":"","children":[{"id":3,"parentId":"1","children":[{"id":5,"parentId":"3","children":[]}]}]},{"id":2,"parentId":"","children":[{"id":4,"parentId":"2","children":[]}]}]

ProxyReflect

它们都是es6的新语法。

JS 中的 Reflect 和 Proxy


空值合并运算符可选链式操作符

因为我在看代码的时候遇到了,查了一下,居然是node14.0.0才支持的语法。。

可选链式操作符(?.)

其实看起来就是个快捷操作的语法,帮你判断了一下空值:

1
2
3
4
5
6
7
8
9
var obj = {}
console.log(obj.a.a) // 报错
console.log(obj.a?.a) // undefined

// 等同于

var obj = {}
console.log(obj.a && obj.a.a) // undefined
console.log(obj.a ? obj.a.a : 0) // 0

空值合并运算符

空值合并操作符(**??**)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

逻辑或操作符(||不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''0)时。

说白了:

  • ?? 只会当左侧的值为null或是undefined时,才会返回其右侧结果
  • || 当左侧为假值('',0)等时,返回右侧结果

彩蛋:在浏览器输出(![]+{})[-~!+[]^-~[]]+([]+{})[-~!![]] (~手动狗头)

end~

ps: 真的是不得不吐槽一下JavaScript