乐趣区

关于javascript:从一道面试题掌握ES6的综合运用

本文作者:相逢在旱季

在面试的过程中,常常能看到候选人写熟练掌握或者精通 ES6,那就拿一道题试一试你的掌握情况吧,题目其实非常简单,如下所示。

// 有以下数据结构
const data = [{
    key: 'name',
    value: '豆皮范 er'
}, {
    key: 'age',
    value: 1,
}, {
    key: 'from',
    value: '数据平台'
}]

// 实现一个转换函数 processFn
// 依据对应的 key,value 生成对象
// 要求尽可能的多用一些 es6 的新个性,尽可能的少去申明变量,缩小副作用。const processFn = data => processData
{
    name: '豆皮范 er',
    age: 1,
    from: '数据平台'
}

好多候选人一看题目,思路一下就有了,啪一下就写进去了,很快啊。
先看下实现的最多的版本


const processFn = data => {const processData = {};
    for (let i = 0; i < data.length; i++) {const key = data[i].key;
        const value = data[i].value;
        processData[key] = value;
    }
    return processData;
}

很规范的实现,不是吗,问这外面用到了哪些 ES6 的个性,能够看出,次要用到了三个,一个是箭头函数=>,一个是块级申明let,一个是常量申明const

这里能够在棘手问一个拓展的考点,如何实现一个真正的 const,因为这里的 const 实际上保障的,并不是变量的值不得改变,而是变量指向的那个内存地址所保留的数据不得改变, 那如何实现一个真正只能只读的对象呢,上面给出答案。

const constantize = (obj) => {Object.freeze(obj);
  Object.keys(obj).forEach((key, i) => {if ( typeof obj[key] === 'object' ) {constantize( obj[key] );
    }
  });
};

着手 ES6 的革新

持续下面的题,其实如果独自问 ES6 有哪些新个性,很多人都能答出来很多,然而一旦使用到真正的代码编写中,就还是应用回老一套,其实就这道题而言比方 箭头函数 解构赋值 扩大运算符 应用表达式作为对象的属性名 , 等等这些ES6 的个性都能使用其中,上面就把这些个性一一代入到这个题当中,来体验一下综合使用 ES6 的感觉吧。
首先,先加上 解构赋值 解构赋值 能够很不便的从一个对象中取值,这里能够在获得数组中每一个对象的 keyvalue 时使用上。

const processFn = data => {const processData = {};
    for (let i = 0; i < data.length; i++) {const { key, value} = data[i];
        processData[key] = value;
    }
    return processData;
}

在这里还能够顺带的把 for 循环改成 for...of, for...of能够遍历一个迭代器(iterator), Array实质上就是一个迭代器,因为它实现了迭代器接口。

这里又能够拓展的去问一个问题,规范三连,什么是迭代器?javascript 都有哪些迭代器?如何实现一个迭代器?这个大家下来后自行答复

还是先持续革新代码。把解构的过程放在 for…of 上,代码就变得更简洁了一些。

const processFn = data => {const processData = {};
    for (let { key, value} of data) {processData[key] = value;
    }
    return processData;
}

而后,能够加上 扩大运算符 了,对于一个对象来说,扩大运算符 的作用是能够合并一个对象,在 ES6 的时代,也能够应用 Object.assign。这两个办法其实都能够。但无论用哪个办法,都会遇到一个问题,属性是一个动静的变量,在ES5 的时代,想把属性作为变量进行对象赋值,只能应用以下形式

const keyName = 'name';
const obj = {}
obj[keyName] = 'xxxx';

ES6 容许咱们应用字面量的模式来实现这个事件,只须要给属性表达式加上一个 [] 即可,当初组合应用 扩大运算符 对象属性表达式 来优化一下代码吧。

const processFn = data => {const processData = {};
    for (let { key, value} of data) {Object.assign(processData, {[key] : value})
    }
    return processData;
}

或者

const processFn = data => {let processData = {};
    for (let { key, value} of data) {processData = {...processData, [key]: value}
    }
    return processData;
}

这样就胜利的把这些 ES6 的个性综合的使用上了,现阶段总结一下用到的 ES6 个性吧,constlet箭头函数 扩大运算符 for...of, 解构赋值 对象属性表达式 。算下来有个了。

缩小一些副作用

不过这还没有完结,题目中还有一个要求,尽可能的少去申明变量,缩小副作用。除了函数申明所必须的申明 const,剩下的还有两处显式申明,那起码能够把这种显式的申明缩小到几个呢,答案实际上是0 个,这时就须要去改变 for 循环 了,因为 for 循环 是必定会有显示的 const 或者 let 申明的,for 循环 自身就是命令式的编程形式,如果应用的是申明式的形式,那就可能会把显式申明变成参数的隐式申明,能够进一步提高代码的可读性和简洁性,这一点其实卡住了很多候选人。
解决的思路其实能够转化为,如何把一个数组进行所谓的折叠(fold),就是把数组的多项合并成一项,这里其实波及到的是一个 ES5 的数组函数,reduce,家喻户晓,对 reduce 的经典应用当然就是累加了。

[1,2,3].reduce((total, item) => total + item, 0)

其实以类比的思维,把对应的数字换成数组中的对象,把累加换成提取 keyvalue,进行合并,那数组的合并就是一种变相的累加了。有了reduce,就能够更大的施展ES6 的联合性了。

整合代码

const processFn = data => data.reduce((obj, {key, value}) => {return {...obj, [key]: value};
}, {})

最初的 return 其实也能够去掉,让代码能够在一行的空间就能够实现了,在箭头函数返回对象的时候加一个括号即可。

const data => data.reduce((obj, {key, value}) => ({...obj, [key]: value}), {})

至此,咱们对这个题的革新就完结了,综合应用了 ES6 个性,以及缩小了显式的申明。写出这样的代码,这道题的解答就能够毕业啦。

彩蛋工夫

这就完结了吗,还没有,能够看出,ES6尽管很弱小,然而要把握它还是要学习各种个性,而且还要综合一些 ES5 的个性联合在一起能力施展出最好的效用,有没有自身就谐和对立的模式来做这个事件呢,其实是有的,坐稳了,咱们要减速了。进入到申明式的世界,关上 函数式编程 的大门吧。

说起函数式编程(Functional Program),其实也是一项法令,入门的话,你们只须要记住上面几个公理和几个概念就能够了。类比小说《三体》的 光明森林法令 就是这么解释的。

  • 函数是一等公民(function first),FP的世界只有函数
  • 函数本身都有可组合的个性(composition),但函数自身是污浊(pure)的

想要持续理解函数式编程,还有两个很重要的概念,柯里化(curry)和PointFree

针对 JS 函数式编程有一个很重要的工具库,对,它不是 lodash 而是 Ramda,应用Ramda 来做一下下面的题吧。

// 关上 http://ramda.cn/docs/ 在控制台粘贴上面代码

const data = [{
    key: 'name',
    value: '豆皮范 er'
}, {
    key: 'age',
    value: 1,
}, {
    key: 'from',
    value: '数据平台'
}]
const processFn = R.reduce(
    R.useWith(R.merge, [
        R.identity,
        R.converge(R.objOf, [R.prop('key'),
            R.prop('value')
        ])
    ]), {})
    
console.log(processFn(data))

乍一看代码,我粗心了啊,没看懂,就像面壁者罗辑的咒语一样艰涩难懂和不可理喻对吧,但在某种程度上,即便只从外表来看,它又充斥了谐和简洁的美,领有高度的秩序排列和统一性,而且完全符合下面的 两条公理 两个重要概念 ,如果大家对什么是函数式编程,它次要有那些概念,具体的利用场景有哪些感兴趣的话,我会另写一篇具体的讲一讲 函数式编程

The End

退出移动版