放开你的思路 #117

Open
lifesinger opened this Issue · 20 comments

14 participants

lifesinger Jim Liu scgy5555 Hong yelo Qiming zhao AJ Cotton Hou 李旭东 Jeff aa88kk chaozh 唐友华 vinqon
lifesinger
Owner

放开你的思路

前些天,陈皓在微博上抛出了异步请求的一个问题:

coolshell 网站提供了 10 进制转 16 进制的 JSONP 服务,即
GET 请求 http://coolshell.cn/t.php?n=10&callback=xxx
会返回 xxx('0xa');

问题:
使用 JavaScript 调用上面的 API 输出 1 - 30 的 16 进制表示(最好按顺序打印)

我的直觉方案

首先封装下 JSONP 的调用接口:

function getScript(url) {
  var script = document.createElement('script')
  script.src = url
  document.documentElement.appendChild(script)
}

function decimal2Hex(n, callback) {
  window['callback' + n] = function(result) {
    callback(n, result)
  }
  getScript('http://coolshell.cn/t.php?n='
      + n + '&callback=callback' + n)
}

function print(n, result) {
  console.log(n, result)
}

然后,对于陈皓提出的问题,一个循环就能解决:

for(var i = 1; i < 31; i++) {
  decimal2Hex(i, print)
}

好像很简单。

可是,大家在讨论什么

这条微博很火,涌现出各种方案。比如

 // 闭包法
 for (var i = 1; i < 31; i++) {
  (function(n) {
    xss_rpc_call2(n, function(result) {
      print(n, result)
    })
  })(i)
}

// 递归法
function recursive_solution(n) {
  if (i === 31) return
  xss_rpc_call(n, function(result) {
    print(n, result)
    recursive_solution(i + 1)
  })
}
recursive_solution(1)

// 数组法
// ...

// 自制 Async 法
// ...

// 数组优化法
// ...

// 性能加超时法
// ...

详见陈皓的总结:http://coolshell.cn/t.html

性能加超时法另当别论,这是另一个话题。前面的递归法、数组法、Async 法等等,看得直让让我纳闷。这是在玩什么呢?

仔细探究了一番源码后,发现罪魁祸首是那两个命名怪异的方法:xss_rpc_callxss_rpc_call2。看其中一个:

function xss_rpc_call2(n, callback) {
  var t = Math.round(Math.random() * 100000)
  var callbackName = "xss_rpc_callback" + n + t
  var url = "http://coolshell.cn/t.php?n=" + n
      + "&callback=" + callbackName

  xss_ajax(url)

  window[callbackName] = function(result) {
    var timeout = Math.round(Math.random() * 1000)
    setTimeout(function() {
      callback(result)
    }, timeout)
  }
}

为了排版,上面的代码稍有修改。其中 xss_ajax 类似 getScript 方法。

还记得前面的 decimal2Hex 方法不?来对比看下:

function decimal2Hex(n, callback) {
  window['callback' + n] = function(result) {
    callback(n, result)
  }
  getScript('http://coolshell.cn/t.php?n='
      + n + '&callback=callback' + n)
}

除了面试官故弄玄虚的 setTimeout,以上两个方法的核心区别是给 window 添加的全局回调函数有差异。decimal2Hex 里,直接 callback(n, result),这就避免了闭包陷进。

进一步看源码,可以看出大家对题目中的

最好按顺序打印

非常在乎。大部分方案是在解决这个问题。

顺序输出

顺序输出,最好的解决方式是,打电话给陈皓,让 JSONP 接口更强大,比如:

http://coolshell.cn/t.php?min=1&max=30&callback=xxx

如果真要解决,修改 print 方法,到最后再统一显示:

var cache = []
var count = 0

function print(n, result) {
  cache[n] = result
  if (++count === 30) {
    cache.forEach(function(result, n) {
      console.log(n, result)
    })
  }
}

类似问题,在 npm 等命令行工具里,都是忽略掉,谁先到达谁先显示就行。还可以进一步优化成尽快显示,但对于陈皓这个面试题来说,真心没必要。如果在其他场景中有需要,很可能有其他更好的解决方案。

上面是引子,下面是我觉得有意思的。

放开你的思路

在微博上的所有解决方案中,好像没看到修改 xss_rpc_call2 的。陈皓只是限定不能修改服务端接口,并没限定 JS 代码。

无论是面试题,还是工作中遇到的真实问题,我们往往会局限在舒适区。比如对于页面性能优化,后端会从后端的角度去思考,前端会从前端的角度去分析。这种分工细化,可以将页面性能优化得不错,但却很难达到优秀,很难产生出类似 Facebook BigPipe 式的创新类优化。

陈皓说,你无权修改我的 API。

很多人又很听话了,开始乖乖地在这种假设的限制下去想解决方案。可是,为什么无权修改呢?在工作中,类似的话太多了:

PD 说:这个需求一定要实现,不能去掉。
设计说:这个按钮必须这样,不能修改。
PM 说:周五必须上线,不能延期。
...

可是 TMD,作为直接为代码负责的你,为什么不能说:

你无权剥夺我合理拒绝的权力。

这个话题点到为止。在《程序员的职业素养》里专门有章节阐述此话题,推荐大家购买阅读并真实实践之。如果你学不会说“不”,如果你不懂得拒绝,你永远无法成为专业的程序员。

有任何不同看法,欢迎讨论。

(完)

这是 WTP(Web 技术与产品交流)微信公众帐号的第一篇文章。WTP 在每个工作日(偶尔休息日)会定期给大家推送一篇原创文字。请搜索 WTP 关键字,或扫描二维码订阅: