不要用战术上的勤奋掩盖站略上的懒惰

前两天,有个同学在群里问一道面试题:

  1. 写一个函数,返回一个长度为5的数组
  2. 数组里的元素都是 2-32 不重复的整数
  3. 需要用递归的方式写,不超过15行

这道题其实蛮简单,而且我觉得出的并不好,不过最后再说,先回到群里。然后就有热心同学动手写代码了,但是他犯了两个严重错误:

第一个错误

我们都知道 Math.random() 会返回一个 [0, 1) 的随机数,我们可以乘上一个数然后取整来获取一定范围内的随机数,比如 Math.random() * 100 >> 0 就能取到 0 ~ 99。那位同学也想到了,但是这次范围不太规整,2~32正好是31个数,跟平时不太一样。于是他可能突然脑子抽筋,先试了 Math.floor,又试了 Math.ceil,都不行,然后,他就用了 Math.round

这是他第一个错误。四舍五入之后,随机数就不再均匀,此时他有很多种选择,但他偏偏选择了一定不对的方案。

第二个错误

然后我就指出“你这种做法是错的”,接着他就犯了第二个也是更严重的错误,他写了个循环,跑100w次,检查结果是否符合预期。

这里的问题在于,首先他只检查能否覆盖到 2 ~ 32,其次他完全没有考虑到概率。

结论

这就是典型的,用战术上的勤奋,掩盖战略上的懒惰。看起来似乎也很努力,响应很及时,实际上纯粹瞎搞。

明明应该先想清楚,或者先查清楚,再去做好的事情。变成了“上来就瞎JB搞”,搞出一个疑似有效的方案,就心满意足。遇到挑战的时候,则顺手抄起一套方案去验证,也不管这套方案的设计思路和覆盖面。

这样做的结果,运气好的时候,勉强不出错;运气不好,事故错误一个不会少。类似的例子,在我们周围不胜枚举,比如 996,不管产品卖不卖的出去,先按住大家把活儿干了再说。看起来为了明天拼拼拼,其实只是老板一厢情愿的豪赌(或者小赌)。

解决方案

不要只顾着埋头苦干,也要学会抬头看路。不要在错误的道路上努力耕耘,赶紧找到正确的道路。

比如找工作。有些同学海投,能投则投,所有都投,然后没有面试机会,回来抱怨说三本没人权。实际一看简历,非常烂,没有重点,没有区分,看不出他擅长什么,能做什么岗位。

准备简历并不简单。

  1. 要找几家公司作为主攻。比如拥有自己的产品、有大牛坐镇、赛道比较喜欢、技术成长空间比较大。
  2. 针对这几家公司的 JD,针对性的修改简历。比如做后台系统,那你就突出 Vue/React 等框架经验;比如做小程序,你就突出小程序开发。
  3. 海投简历也应该根据岗位调整。前端岗位有一些区分度,需要突出不同技术能力和经验。这样做可以让你显得更加适合一些岗位。比如:
    1. 数据可视化:Canvas、SVG、WebGL
    2. JS 框架、语言开发:编译原理、执行基础
    3. 后台等工具类开发:三大框架
    4. Hybrid 开发,比如 RN
    5. 小程序开发:小程序、普适类框架

随便做个简历,乱投,有面试就去面,也不考察公司,最后要么找不到工作,要么找不到好工作,都是典型的“用战术的勤奋掩盖战略的懒惰”问题。

另外,如果一件事是正确的,但是暂时没有收获到正确的结果,那么不要着急变换思路,再想想再看看再查查,排除错误,要在正确的道路上找到正解。

吐槽面试题

这道题出的不好。看得出来想考察候选人是否会使用递归(可能还要考察尾递归优化),但是题目本身不用递归就能做,而且效果更好。其实随便出个归并排序之类的就好了嘛。

总结

我们从小接受到很多歌颂勤奋的教育,所以大家喜欢“没有功劳,也有苦劳”,用勤奋来感动自己。现在一定要明白,闷头苦干是不对的,一定要看请道路,选好方向。

与诸君共勉。

在 Chart.js 的线图里增加竖线

我经常使用 Chart.js 生成图表,无它,名字好记+免费+持续更新。现在主要基于 Vue 开发项目,所以经常使用 vue-chart.js。前阵子遇到一个需求:

  1. 把一组数据画成线图
  2. 用户可以任意点击时间
  3. 用数据生成饼图

那么就需要在线图中画一条竖线,标识出当前时间点。本以为这个需求不复杂,没想到不仅 Chart.js 不支持,包含这个功能的插件因为 Chart.js 升级的关系,暂时没法用。所以只好自己实现。

继续阅读“在 Chart.js 的线图里增加竖线”

我厂技术沙龙

其实是我厂老板的个唱……但仍然非常值得看。

近期我厂老板春哥回国,在北京和深圳分别开了一场技术沙龙,介绍他的职业生涯、开发实践、最新成果等等。作为程序员的终极形态,我觉得这两场演讲非常值得有志于把程序员当成终身职业的同学学习。也可以回答很多类似“35岁的程序员应该怎么走”的问题。

先来是北京场:

然后是深圳场:

两场的内容并不一样,可以理解成上下半场。建议顺序观看。

最后说说最近半年来的工作感受。

首先远程工作还是挺好的。虽然光环褪去,负面感受更多更具体,不过对比普通上下班,感觉还是蛮好的。

其次,春哥是很牛的程序员,但作为老板有不少缺陷;公司也同样不完美。不过世上本就没有完美的老板和公司,在 OpenResty,春哥证明了他的想法的确有前瞻性,我们有机会改变未来,所以在这里工作还是很兴奋的。

希望接下来会更好。

浏览器切换中

最近比较忙,博客有些荒废。忙过这阵子再写吧。

接下来的三周,会强迫自己把开发浏览器切到 Firefox,争取从现有项目中找到兼容性问题,并且修复。

再接下来如是反复,Chrome > Firefox > Edge > Safari > Chrome……

以后客户越来越多了,兼容性得多费心了。

下午4点更新

Firefox 不支持 SourceMap……已经切回 Chrome 了,晚上继续尝试。

8-14 更新

Firefox 团队曾经抱怨 Google Chrome 不正当竞争,故意在几个大型产品中给 Firefox 挖坑,打压他们在用户心目中的地位。然而经过我这一周来的使用,我觉得 Firefox 实在做得不够好,很难让开发者选择它作为主力开发工具。

  1. 使用 Vue Cli 会不停报错,找不到 sw.js,连接不到 sockjs-node 等等
  2. 使用 webpack 无法加载到 sourcemap,无法 debug。(应该是配置问题,因为 Vue Cli 3 可以。
  3. 报错多了就影响性能,各种卡顿。
  4. 断点不会自动跳过去,屏幕没有明显的视觉效果,开始被迫重启好几次
  5. 断点不会自动更新堆栈、变量状态,需要点击一次“Step over”

问题很多,很影响开发效率。可惜我厂老板特别爱用 Firefox,所以我还必须照顾下 Firefox,经过一周的挣扎,现在倒是好一些了,Windows 10 的表现还不错。

crontab 笔记

在服务器上定时跑程序。重点:

  1. 要执行的程序,比如 php,必须全局可用
  2. 执行的程序,必须处理内部的文件结构,因为执行路径不太确定

生成日期时间配置,可以使用这个工具

生成日志

1 0 * * * php /var/www/laravel/artisan check:daily > /var/log/laravel.log

每天凌晨 00:01 执行 php,跑一个 laravel command,并且把日志放在 /var/log/laravel.log

续写日志,而不是覆盖

使用 >> 替换 >

使用 Webpack + Mocha 进行单元测试(二)

作为一名远程工作者,我并不能保证100%的电源供应,就目前的经验来看,我大约有30%的时间靠电池工作。所以我对能耗也比较敏感,把 eslint 和单元测试(mocha)放在 webpack 里做热加载,我觉得实在没什么必要,还是单独写单独跑吧。

查看 mocha-webpack 的 README,它对自己的介绍是:mocha-webpack 基本上等效于 $ webpack test.js output.js && mocha output.js 命令的封装,但是更高效、更强大。

让我们忽略后半句,先不去研究它高效和强大在什么地方,只看前半句。如果本质如此的话,那就不复杂,我只要写一个 webpack 配置文件生成所有的测试,然后跑这些测试就行了。最终方案:

  1. 一个入口文件,import 所有测试文件
  2. 用 webpack 打包入口文件,忽略掉不需要测试的内容(比如 CSS)
  3. 跑这个测试
  4. 平时写测试随便改下 import 路径就行了

测试文件多而杂,一个一个 import 太麻烦,可以写一个脚本帮我们:

const context = require.context('.', true, /.+\.test\.js?$/);
context.keys().forEach(context);
module.exports = context;

然后配合这样的配置文件即可:

const path = require('path');
const nodeExternals = require('webpack-node-externals');
const {resolve} = require('./webpack.base.config');

resolve.alias.test = path.resolve(__dirname, '../test');

const config = {
  target: 'node',
  entry: 'test/index.js',
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: 'test.build.js',
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
        ],
      },
      {
        test: /\.ya?ml$/,
        use: [
          'json-loader',
          'yaml-loader',
        ],
        exclude: /node_modules/
      },
      {
        test: /\.(css|styl)$/,
        loader: 'null-loader',
      },
      {
        test: /\.(png|jpg|gif|svg|woff2|eot|woff|ttf|ico)$/,
        loader: 'null-loader',
      },
      {
        test: /\.pug$/,
        loader: 'pug-plain-loader',
      },
    ],
  },
  mode: 'none',
  resolve,
};

module.exports = config;

执行的时候,直接用 shell 脚本来跑就行。

解决 composer global install 失败的问题

今天突发奇想,打算写会儿 PHP,按照 Laravel 文档,准备先安装 laravel/installer,结果 composer global require laravel/install 安装失败,提示错误信息:

Your requirements could not be resolved to an installable set of packages.

出问题的包是 symfony/console,需要的版本和已安装版本不一致,安装新版本失败。简单搜了一下,答案比较多,有说版本问题,有说权限问题,有说 PHP 模块问题。

我看了一眼权限,应该没问题。然后 composer help 看了看命令参数,发现有一个命令,可以查看所有已安装,现在版本有些旧了,可以升级的包:composer global outdated。执行之,发现以前安装过 laravel/installer 1.x,自然也安装了 1.x 需要的 symfony/console 作为依赖。那么,多半是这个旧版本阻止了新版本的安装,导致新版 laravel/installer 安装失败。

于是 composer global remove laravel/installer 先删除就版本,然后安装新版本,就 OK 了。

然后,折腾了半天,我又不想写 PHP 了……

使用 `position: sticky` 的要点

position 属性非常重要,它有五个可选值。“这五个选项是哪些?它们的作用如何?”是我非常喜欢的面试题。以我的经验,凡是这道题答得好的,后面多半没啥问题;这道题答不出或者错漏的,后面能翻盘的概率很低。

属性及释义大家请看 MDN,本文不再赘述。

position: sticky 比较复杂,简单来说,它包含以下特性:

  1. 当它的位置让它可以正常呈现的时候,它的定位等同于 position: static,随着正常的文档流滚动
  2. 当它的位置不足以让它正常显示,但它的父元素有足够多让它显示的空间,它的定位等同于 position: fixed
  3. 当它的父元素的空间不够让它显示,它的定位等同于 position:absolute

言说太复杂,大家可以看下这个演示,基本上就能明白了:

继续阅读“使用 `position: sticky` 的要点”