Chris Yip

Project manager @ feifei.com.

Read this first

Use io.js and Node.js with Homebrew at OS X

Install io.js and Node.js with homebrew is super easy:

brew install iojs node

But you make notice this message for io.js formula:

This formula is keg-only.
iojs conflicts with node (which is currently more established)

Because of io.js uses node for its binary’s name, so Homebrew won’t link io.js’s binary to /usr/local/bin, unless force to link with command brew link iojs --force. That means you can’t use io.js directly (e.g. node app.js). You have to use absolute path (e.g. /usr/local/Cellar/iojs/1.0.4/bin/node app.js), or use nvm:

nvm use 0.11
node -v  v0.11.15
nvm use iojs
node -v  v1.0.4

Although it can work, but you have to change asolute path after io.js updated, or nvm may be unavailable in some situations (e.g. needs source $NVM_PATH/nvm.sh). It’s inconvenience. So I created a shell script to help things easier:

!/usr/bin/env sh

if [ -d "$(brew --prefix)/Cellar" ]; then
...

Continue reading →


Function direct call vs func.call() vs func.bind() in JavaScript

JavaScript 有三种函数调用方式 (其他奇葩的不讨论) :

  1. func()
  2. func.call()func.apply()
  3. var binded = func.bind()

第一种很好理解,就是直接调用。

第二种很常用,经常用于调整函数的 this 指向的对象。如:

function say_hello () {
  return 'Hello, ' + this.name
}

say_hello.call({ name: 'John' }) // 'Hello, John'

第三种是 EcmaScript 5 新加入的 Function.prototype.bind()。它和 call() 类似,但它不是直接调用,而是返回一个新的函数:

function withdrawCash (amount) {
  if (this.bank.cash < amount) {
    throw new Error('You do not have enough money')
  }
  this.bank.cash = this.bank.cash - amount
  console.log('Cash ' + amount + ' out')
}

var John = withdrawCash.bind({
  bank: {
    cash: 1000
  }
})

var Marry = withdrawCash.bind({
  bank: {
    cash: 10000
  }
})

John(1000)        // 'Cash 1000 out'
Marry(10000)      // 'Cash 10000 out'
Marry(1)          // throw 'You do not have enough money'
withdrawCash(100) // throw 'TypeError: Cannot read property 'cash' of undefined'

从例子...

Continue reading →


apm upgrade: Failed to change folder ownership under npm cache

If you meet errors similar to the following one while upgrading apm packages via command line:

npm ERR! Failed to change folder ownership under npm cache for %s /Users/Chris/.atom/.node-gyp/.atom/.apm/_git-remotes/https-github-com-Glavin001-Coffee-Formatter-git-41cd2663
npm ERR! Error: ENOENT, readdir '/Users/Chris/.atom/.node-gyp/.atom/.apm/_git-remotes/https-github-com-Glavin001-Coffee-Formatter-git-41cd2663/description'
npm ERR! If you need help, you may report this *entire* log,
npm ERR! including the npm and node versions, at:
npm ERR!     <http://github.com/npm/npm/issues>

Its mostly just because of there are not just one versions of node-gyp under ~/.atom/.node-gyp.

In my case, there were 3 versions:

ls ~/.atom/.node-gyp/.node-gyp
0.11.10 0.11.13 0.17.0

The way to fix this issue is very simple, just run the following command to force Atom to recreate .node-gyp folder:

rm
...

Continue reading →


Markdown and CommonMark

CommonMark,最早的名字叫 Standard Markdown,后来迫于 Markdown 原作者 John Gruber 的压力而改名。

虽然这已经是今年九月初的事情了,而且我在当时就已经表达了我的看法,但还是完整说说我的观点。

Markdown 根据不同的需求、场景是可以被扩展成不同的 Markdown。比如说 GitHub Flavored Markdown 的代码块语法和 Task Lists 就是很好的例子。

GitHub Flavored Markdown Task Lists

GitHub 针对 Markdown 所扩展的特性对程序员来说非常有用,但是对于一般的使用者呢?没用。

如果有了一个 CommonMark,这些特性要不要加进去标准里呢?

所以我认为 Markdown 应该保持它作为一个简洁高效的写作工具的纯粹性,剩下的就由特定群体去扩展、去维护。

请看 Gruber 通过 @Markdown 账号所表达的意思:

Continue reading →


Node.js, Travis CI and Coveralls

在 GitHub 或 npm 社区混多了,你肯定见过这些图示:

Badges

这些图示是 shield.io 提供的服务,当然,像 npm、build、coverage 这些是别的服务提供的:

  • npm - david-dm.org
  • build - travis-ci.org
  • coverage - coveralls.io

其中 build 的作用是在你有 push 或 pull request 的时候,会执行设置好的 build 脚本,并记录 build 的详细日志:

Travis CI Project Page

通过 Travis CI 可以很方便地执行自动化 build 测试,并且跟踪是否有 commit 会导致 build 错误。

而 Coveralls 则是跟踪测试用例的覆盖程度。我之前在 node-extensions 里添加了 Mocha 测试,但是有部分没写测试用例,结果 push 到 npm 之后,在项目里更新之后引发别的包出错,排查后发现是没写测试用例的那部分代码造成的。像这种情况,就可以要求 coverage 达到 100% 才能用在项目里,这样就能大大提高健壮性。

启用 Travis CI

首先去到 Travis CI 官网使用 GitHub 账号登录,然后进行如下操作:

Travis CI Add Project

Travis CI Enable Project

完成后在项目根目录添加一个 .travis.yml 文件,内容可以参考我的:

language: node_js
node_js:
  - "0.11"
  - "0.10"
matrix:
  allow_failures:
    - node_js: "0.11"
  fast_finish: true
script: "npm run-script test-travis"
after_script: "npm install coveralls && cat ./coverage/lcov.info | coveralls"

languagenode_js:告诉 Travis CI 这个项目是一个 Node.js 项目,并指定需要测试 v0.10 和 v0.11。

matr...

Continue reading →


Grunt and gulp.js

这是为敝司暑期实习生准备的 Grunt 和 gulp.js 入门的 slide,就目前而言,我倾向于在项目中使用 gulp.js,在小插件、module 上使用 Grunt。

View →


Node.js 101: generator

之前介绍了 Promise and async,现在来说说 ECMAScript 6 新加入的 Generator。

In computer science, a generator is a special routine that can be used to control the iteration behaviour of a loop. In fact, all generators are iterators. – Wikipedia

Generator 是一种控制程序迭代的方法,让顺序执行的迭代程序变成异步执行 (此异步和 Node.js 的异步调用有差异) 。在 Node.js 里,它的存在目的就是可以写出更好的控制流。

假设有如下代码:

function loop (arr, cb) {
  for (let i = 0, len = arr.length; i < len; i++) {
    cb(arr[i])
  }
}

如果需要扩展这个代码,增加更多的回调:

function loop (arr, cb) {
  for (let i = 0, len = arr.length; i < len; i++) {
    for (let j = 0, jlen = cb.length; j < jlen; j++) {
      cb[j](arr[i])
    }
  }
}

loop(arr, [cb1, cb2, cb3, cb4, ...cbN])

换成 generator 就可以这样:

function* loop (arr) {
  for (let i = 0, len = arr.length; i < len; i++) {
    yield arr[i]
  }
}

var ge = loop(['Phil Colson', 9])

var name = ge.next().value
  , profile = getProfile(name)
accessLog(name)

var
...

Continue reading →


Node.js 101: Promise and async

先回顾一下 Sagase 的项目结构:

lib/
    cli.js
    sagase.js
Gruntfile.js
package.json

上一篇讲了 package.json,这一篇讲 lib/sagase.js

因为代码比较长,就分开一节节地讲,完整的点开 GitHub 看吧。

'use strict';

通知编译器进入 strict mode,主要的作用是让编译器帮你检查一些坑,有经验的 JavaScript 开发者可以忽略。

var fs = require('fs-extra'),
    async = require('async'),
    _ = require('lodash'),
    Promise = require('bluebird'),
    path = require('path')

引用所有依赖项,如果是使用 --harmony,建议使用 const 代替 var 关键字,可避免变量被修改。

var mar = 'March'
mar = 'June'
console.log(mar) // 'June'

const july = 'July'
july = 'May'
console.log(july) // 'July'

function readFiles (opts) {} 包含很多信息,一个一个说。

return new Promise(function (resolve, reject) {}

返回一个 Promise 对象。

由于 Node.js 的特点是异步,一般都需要通过异步来处理:

// get all files in DIR
fs.readdir(DIR, function (err, files) {
  if (err) {
    return errorHandler(err)
  }
  // loop files
  files.forEach(function (file) {
    // get the stat of each
...

Continue reading →


Node.js 101: create a package

这是我为公司同事科普如何开发 Node.js app 的系列文章。

什么是 Node.js?

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

如何安装?

Download 或 brew install node

如何运行 Node.js

 bash script, works on Linux / Mac
touch demo.js
echo "console.log('hello world)" > demo.js
node --harmony demo.js
 output "hello world"

node --hamrony -e "console.log('hi there')"
 output "hi there"

什么是 --harmony

Harmony 可以理解为一个为了解决 ECMAScript / JavaScript 混乱局面的项目。

作为一个 JavaScript 开发者都应该去了解 ECMAScript 5,作为一个 Node.js 开发者更应该去了解 ECMAScript 6。

因此,Chrome 在内核里加入 ECMAScript 5 / 6 的内容,并提供选项,供开发者尽早试验新的特性;而基于 V8 引擎的 Node.js 自然也会有,这个选项就是 --harmony

具体的细节请善用 Google,Node.js 能用的 ES 6 特性请查阅 GitHub 上的...

Continue reading →


Control Flow in JavaScript

这是一篇我在飞飞商城针对 JavaScript Control Flow 的演讲稿文字版。


Control Flow 是什么?

Control Flow 就是控制流,简单地归纳,就是代码执行的顺序(和控制的手段)。

常见的控制流有:

  • if-else
  • switch-case
  • for / while loop
  • goto

问题儿:JavaScript

由于 JavaScript 的运行环境,决定了她拥有异步执行的能力。

if (checkEmail() === false ||
    checkUsername() === false) {
  // validation failed
}

如果是使用 ajax 来做验证的话,就不能如此实现。

function checkEmail () {
  $.get({
    url: 'api/check_email.json'
  })
  // where is the return?!
}

function checkUsername () {
  $.get({
    url: 'api/check_username.json'
  })
  // where is the return?!
}

如果使用 async: false,那就会造成浏览器的阻塞。

怎么办呢?幸好,解决方案是有的。

Callback

亦即回调函数

function checkEmail (data, callback) {
  $.get({
    url: 'api/check_email.json',
    data: data,
    success: function (data) {
      callback.success.call(null, data)
    },
    error: function (err) {
      callback.error.call(null, err)
    },
    complete: function () {
...

Continue reading →