说明:写一个转换函数,把json对象的key值从下划线(Pascal)形式转为小驼峰(Camel)形式
示例:convert({“a_bc_def”: 1}); // 返回 {“aBcDef”: 1}

面试是一种很好的学习方式

分析:此题的考点是replace方法,和正则表达式

先看看replace方法,都知道replace方法有两个参数,第二个参数表示要替换为的字符串,但是没怎么详细用过,
此题的考点即考察第二个参数为方法的用法

先举个栗子,看看这个方法怎么用:

1
2
3
4
5
6
7
var str = "a_bc_def"
function toCamel (str) {
return str.replace(/_\w/g, function (a, b, c, d) {
console.log(a, b, c, d)
})
}
toCamel(str)
1
2
3
结果:
_b 1 a_bc_def undefined
_d 4 a_bc_def undefined

分析:

参数 a 代表 匹配到的字符串
参数 b 代表 匹配到的字符串的下标
参数 c 代表 整个字符串
至此,此题已经可解
但是,匹配到字符串中还有下划线,此时还不是最优解,能不能直接匹配到首字母呢

答案是肯定的,先看效果,换一种写法

1
2
3
4
5
6
7
var str = "a_bc_def"
function toCamel (str) {
return str.replace(/_(\w)/g, function (a, b, c, d) {
console.log(a, b, c, d)
})
}
toCamel(str)
1
2
3
结果:
_b b 1 a_bc_def
_d d 4 a_bc_def

分析:

参数 a 代表 匹配到的字符串
参数 b 代表 匹配到的没有下划线的字符串
参数 c 代表 匹配到的字符串的下标
参数 d 代表 整个字符串
不同的地方,就是多写了个括号,下面来看看这个括号是个什么东西

正则表达式的子项(子表达式)

没错了,每个()中包含的部分叫做正则的子项(子表达式)

为了看明白,改一下上面的栗子,给下划线也加上括号,看看效果

1
2
3
4
5
6
7
var str = "a_bc_def"
function toCamel (str) {
return str.replace(/(_)(\w)/g, function (a, b, c, d, e) {
console.log(a, b, c, d, e)
})
}
toCamel(str)
1
2
3
结果:
_b _ b 1 a_bc_def
_d _ d 4 a_bc_def

分析:

现在参数变成5个了
参数 a 代表 匹配到的字符串
参数 b 代表 匹配到的下划线
参数 c 代表 匹配到的没有下划线的字符串
参数 d 代表 匹配到的字符串的下标
参数 e 代表 整个字符串

由上,可以发现规律:

第1个参数表示: 匹配到的字符串
第2个参数表示: 匹配到的字符串的第1个子项
第3个参数表示: 匹配到的字符串的第2个子项
……如果有多个子项,则依次类推
然后倒数第2个参数是:匹配到的字符串的下标
最后1个参数是:整个字符串

至此,分析完毕

此题解法是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var obj = {"a_bc_def": 1}

function toCamel (a) {
return a.replace(/_(\w)/g, function(a, b){
return b.toUpperCase()
})
}

function convert (obj) {
if (Object.prototype.toString.call(obj) !== '[object Object]') return obj
let res = {}
for (key in obj) {
let newKey = toCamel(key)
res[newKey] = obj[key]
}
return res
}

console.log(convert(obj)) // {aBcDef: 1}

拓展

replace的第二个参数可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。
但是 replacement 中的 $ 字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换。

字符 替换文本
$1、$2、…、$99 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。
$& 与 regexp 相匹配的子串。
$` 位于匹配子串左侧的文本。
$’ 位于匹配子串右侧的文本。
$$ 直接量符号

栗子

1
2
3
4
5
6
7
8
9
let name = "Doe, John";
name = name.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1");
console.log(name) // John Doe
```js

```js
let name = '"a", "b"';
name = name.replace(/"([^"]*)"/g, "'$1'");
console.log(name) // 'a', 'b'

更好的写法

1
2
3
4
5
6
7
8
// 将驼峰转为指定连接符
fromCamelCase (str, separator = '_') {
return str.replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2').replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2').toLowerCase();
},
// 将下划线连接符转为驼峰形式
toCamelCase (str) {
return str.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2, offset) => p2 ? p2.toUpperCase() : p1.toLowerCase());
},

参考:
MDN replace