js(面试题)

21 篇文章 0 订阅

shuffle:顾名思义,将数组随机排序,常在开发中用作实现随机功能。

我们来看看一个 shuffle 可以体现出什么代码品味

错误举例

function shuffle(arr) {
   arr.sort(function () {
      return Math.random() - 0.5;
   });
}

// ES6
const shuffle = (arr) => {
  arr.sort(() => Math.random() - 0.5);
}

请老铁千万不要这样写,这体现了两个错误:

  1. 你的这段代码一定是从网上抄/背下来的,面试官不想考这种能力
  2. 很遗憾,这是错误的,并不能真正地随机打乱数组。

Why? Check:https://blog.oldj.net/2017/01/23/shuffle-an-array-in-javascript/comment-page-1/#comment-1466

思考

下面来到了第一反应:思考问题。

数组随机化 -> 要用到 Math.random -> 看来每个元素都要 random 一下 -> 处理 arr.length 要用到 Math.floor -> 需要用到 swap

第一版

由此有了第一版代码:

function shuffle(arr) {
  var i;
  var randomIndex;
 
  for (i = arr.length; i > 0; i--) {
    randomIndex = Math.random() * i;
    swap(arr, i, randomIndex);
  }
}
  • 为什么用 randomIndex 不用 j? -> 更有意义的变量命名
  • 为什么要把 i 和 randomIndex 的声明放在最前方? -> ES5 里的变量提升(ES6 里有没有变量提升?没有,不仅 constlet 都没有,连 class 也没有)
  • 为什么第 3 行和第 5 行中留一个空格?将声明的变量和函数体分开,一目了然的逻辑使代码更加清晰易维护

什么,JavaScript 中木有 swap 函数?

写一个,使逻辑更加清晰 & 重复利用:

function swap(arr, indexA, indexB) {
  var temp;
 
  temp = arr[indexA];
  arr[indexA] = arr[indexB];
  arr[indexB] = temp;
}

第二版

一点点小的改动:

function shuffle(arr) {
  arr.forEach(function (curValue, index) {
    var randomIndex = Math.random() * index;

    swap(arr, index, randomIndex);
  });
}

arr.forEach 替代原本的 for 循环。

不希望有人质疑:JS 由于函数调用栈空间有限,用 for 循环不是比 forEach 效率更高吗?

拿出这段话压压惊:

”We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.”-- Donald Knuth

JavaScript 天生支持函数式编程(functional programing),放下脑海中的 CPP-OOP,请好好珍惜它。

有了 High-order function & First-class function 的存在,编写代码的逻辑愈发清晰简洁好维护

第三版

且慢,同学不写一个 ES6 版本的吗?

const shuffle = arr => {
  arr.forEach((element, index) => {
    const randomIndex = Math.floor(Math.random() * (index + 1))
 
    swap(arr, index, randomIndex)
  })
}

使用 ES6 的箭头函数(arrow function),逻辑的表达更为简洁清晰好维护。(我会告诉你箭头函数还因为本身绑定的是外部的 this,解决了一部分 this 绑定的问题嘛。注意我没有说全部)。

进阶

何不用 ES6 重写一下 swap 函数?

const swap = (arr, indexA, indexB) => {
  [arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]]
}

怎么样,ES6 的对象解构赋值(Destructuring)燃不燃?好用不好用?

但如果单独写一个 swap 函数,这样写没毛病,如果 shuffleswap 一起写呢:

const shuffle = arr => {
  arr.forEach((element, index) => {
    const randomIndex = Math.floor(Math.random() * (index + 1))
 
    swap(arr, index, randomIndex)
  })
}

const swap = (arr, indexA, indexB) => {
  [arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]]
}

出现调用错误,const 声明的变量没有变量提升,在 shuffle 调用 swap 的时候 swap 还木有出生呢~!

So 这样?

const swap = (arr, indexA, indexB) => {
  [arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]]
}

const shuffle = arr => {
  arr.forEach((element, index) => {
    const randomIndex = Math.floor(Math.random() * (index + 1))
 
    swap(arr, index, randomIndex)
  })
}

老铁没毛病。但主要逻辑 shuffle 放在后,次要逻辑 swap 放在前有没有不妥?

最终解答

function shuffle(arr) {
  arr.forEach((element, index) => {
    const randomIndex = Math.floor(Math.random() * (index + 1))
 
    swap(arr, index, randomIndex)
  })
}
 
function swap(arr, indexA, indexB) {
  [arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]]
}

为啥用 ES5 的方式来写 function,AirBnb 的 ES6 规范建议不是用 const + 箭头函数来替代传统的 ES5 function 声明式吗?

子曰:

  • 编程规范是人定的,而你是有选择的
  • 软件开发不是遵循教条,代码世界本没有标准答案

我用传统 ES5 function 是因为:

我想利用它的变量提升实现函数主逻辑前置,进而从上到下,层层逻辑递进。再一次出现这两个次:逻辑简洁好维护

总结

  • 你问:有没有高水平的代码来让面试官眼前一亮?

  • 我答:只有好读又简洁,稳定易维护的代码,没有高水平的代码一说。

  • 你问:说好的代码品味呢?

  • 我答:都藏在每一个细节的处理上:)

地址

原文地址:https://www.rayjune.me/2018/03/13/see-code-taste-from-shuffle/

作者:RayJune https://github.com/rayjune

来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值