npm是node的包管理系统,通过package.json声明模块间的依赖关系。然而node采用CommonJS规范组织的模块在浏览器中无法直接使用。bower是前端资源的包管理系统,通过bower.json来组织js、css和图标资源。bower管理的js包,一般采用全局变量模式,或者requirejs推崇的AMD规范,亦或UMD。

UMD(通用模块定义)

由于现行的javascript版本中,没有原生的依赖管理实现,而随着前端和Nodejs的越来越流行,javascript的代码量越来越大,于是对依赖管理和包管理的需求越来越高,于是社区就出现了CommonJS和AMD两大依赖管理的阵营。目前CommonJS规范是Nodejs的内置支持,主要用于后端js。AMD被requirejs支持,是一种前端依赖管理的选择。

全局变量

// MyDependency is in your global scope
var MyModule = function() {};

CommonJS,Nodejs支持的规范

var MyDependency = require('my-dependency');
module.exports = function() {};

AMDrequirejs 支持的规范

define(['my-dependency'], function(MyDependency) {
  return function() {};
});

UMD,兼容所有规范

(function (root, factory) {
  if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory(require('b'));
  } else if (typeof define === 'function' && define.amd) {
    // AMD
    define(['b'], function (b) {
      return (root.returnExportsGlobal = factory(b));
    });
  } else {
    // Global Variables
    root.returnExportsGlobal = factory(root.b);
  }
}(this, function (b) {
  // Your actual module
  return {};
}));
继续阅读 →

Mocha + Chai 进行BDD测试,是目前较为流行的Nodejs测试方法。本文介绍mocha测试用例失败时如何在非IDE环境下单步Debug找出问题。

mocha 配置

$ npm install mocha
$ mkdir test
$ $EDITOR test/test.js

内容如下

var assert = require("assert")
describe('Array', function(){
  describe('#indexOf()', function(){
    it('should return -1 when the value is not present', function(){
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    })
  })
})

执行如下命令开始测试

$  mocha

配置 Makefile

如果你更喜欢 Make 风格,可以配置相应的Makefile文件。

test:
    @./node_modules/.bin/mocha --reporter nyan

.PHONY: test

然后执行

$ make test

配置 package.json

然后在package.json 调用make命令。

{
...
  "scripts": {
    "test": "make test"
  },
...
}

然后执行

$ npm test

这里两次包装和转换,考虑到如果哪天将mocha换成Jasmine或者jsUnit等其他的测试框架也无需更改持续集成的配置。

继续阅读 →

由于一些网络通讯协议的限制,你必须使用window.btoa()方法对原数据进行编码后,才能进行发送。接收方使用相当于window.atob()的方法对接受到的base64数据进行解码,得到原数据。例如,发送某些含有ASCII码表中0到31之间的控制字符的数据。

window.btoawindow.atob不支持中文

对于unicode编码的字符进行base64编码之后,通过浏览器原生的btoa方法界面中文会乱码。

在bash终端,将“中文”转成 base64 编码

$ echo 中文 | base64
5Lit5paHCg==

在Chrome console通过window.atob解码,结果为乱码

> window.atob('5Lit5paHCg==')
中文

在Chrome console中执行windows.btoa编码,报错

> window.btoa('中文')
Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

从错误提示看,btoa仅支持 ASCII 编码。

继续阅读 →

Javascript 采用回调函数(callback)来处理异步编程。从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验。于是便有了 CommonJS 的 Promises/A 规范,用于解决回调金字塔问题。本文先介绍Promises相关规范,然后再通过解读一个迷你的 Promises 以加深理解。

什么是 Promise

一个 Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。它允许你以一种同步的方式编写异步代码。例如,如果你想要使用Promise API 异步调用一个远程的服务器,你需要创建一个代表数据将会在未来由 web 服务返回的 Promise 对象。唯一的问题是目前数据还不可用。当请求完成并从服务器返回时数据将变为可用数据。在此期间,Promise 对象将扮演一个真实数据的代理角色。接下来,你可以在 Promise 对象上绑定一个回调函数,一旦真实数据变得可用这个回调函数将会被调用。

Promise 对象曾经以多种形式存在于许多语言中。

去除厄运的回调金字塔(Pyramid of Doom)

Javascript 中最常见的反模式做法是回调内部再嵌套回调。

// 回调金字塔
asyncOperation(function(data){
  // 处理 `data`
  anotherAsync(function(data2){
      // 处理 `data2`
      yetAnotherAsync(function(){
          // 完成
      });
  });
});
继续阅读 →

开发环境需要在本地映射域名和端口,无奈选择安装nginx。本文介绍如何将homebrew安装的nginx以符合MacOS标准的方式默认启动在80端口。

安装nginx

brew install nginx

拷贝plist文件到启动脚本目录

sudo cp /usr/local/opt/nginx/homebrew.mxcl.nginx.plist /Library/LaunchDaemons/

文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>homebrew.mxcl.nginx</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/opt/nginx/bin/nginx</string>
        <string>-g</string>
        <string>daemon off;</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/usr/local</string>
  </dict>
</plist>

想要方便的启动和关闭nginx可以将下面两个别名添加到$HOME/.profile

# Nginx needs to bind to port 80 so must run as /Library/LaunchDaemon with sudo
alias start-nginx='sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.nginx.plist'
alias stop-nginx='sudo launchctl unload /Library/LaunchDaemons/homebrew.mxcl.nginx.plist'

参考阅读

  1. How do I start nginx on port 80 at OS X login?
  2. Installing Nginx in Mac OS X Maverick With Homebrew

Underscore.js是一个很精干的库,压缩后只有5.2KB。它提供了几十种函数式编程的方法,弥补了标准库的不足,大大方便了JavaScript的编程。

本文仅探讨Underscore.js的两个函数方法 _.throttle_.debounce 的原理、效果和用途。

通常的函数(或方法)调用过程分为三个部分:请求、执行和响应。(文中“请求”与“调用”同义,“响应”与“返回”同义,为了更好的表述,刻意采用请求和响应的说法。)

某些场景下,比如响应鼠标移动或者窗口大小调整的事件,触发频率比较高。若稍处理函数微复杂,需要较多的运算执行时间,响应速度跟不上触发频率,往往会出现延迟,导致假死或者卡顿感。

在运算资源不够的时候,最直观的解决办法就是升级硬件,诚然通过购买更好的硬件可以解决部分问题,但是也需要为此付出高额的成本。特别是客户端和服务器模式,要求客户端统一升级硬件基本不可能。

在资源有限的前提下,处理函数无法即时响应高频调用。退而求其次,只响应部分请求是否可行呢?某些场景下的密集性请求,具备很强的同质和连续性。比如说,鼠标移动的轨迹参数。响应越及时效果越平滑,但是如果响应速度跟不上时,反而会出现卡顿感,如果适当的丢弃一些请求效果更流畅。

throttledebounce 是解决请求和响应速度不匹配问题的两个方案。二者的差异在于选择不同的策略。

电梯超时

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应。假设电梯有两种运行策略 throttledebounce ,超时设定为15秒,不考虑容量限制。

  • throttle 策略的电梯。保证如果电梯第一个人进来后,15秒后准时运送一次,不等待。如果没有人,则待机。
  • debounce 策略的电梯。如果电梯里有人进来,等待15秒。如果又人进来,15秒等待重新计时,直到15秒超时,开始运送。
继续阅读 →

随着互联网的飞速发展,我们在网页里完成越来越多的事情。Web应用程序开始逐渐替代传统的桌面程序,然而HTTP协议设计之初没有考虑到面向应用开发的需求,HTTP协议服务端只能被动的响应客户端(浏览器)的请求,若服务端需要实时的给客户端推送消息,HTTP协议就需要借助与轮询和长连接等变通的技术。这些技术虽然勉强能够实现,但是都有些弊端。直到websocket协议推出,才真正的意义上解决了实时数据传输问题。但是由于旧版本浏览器不兼容websocket协议,为了更好的向下兼容,社区遍有了socket.io框架,该框架能智能识别浏览器端对websocket的支持情况,对于不支持的旧版本浏览器采用长轮询的方式通讯。本文将讨论4中常见的实时Web通讯技术。

继续阅读 →

一朋友的公司邮箱只能接受5M的附件,还世界500强企业真心落后。无奈只能分卷压缩大文件,Mac OS X下没有盗版的WinRar可用,所以只好采用ZIP格式分卷了

创建分卷压缩文件

将目录分卷压缩

zip -s 100m -x "*.DS_Store" -r split-foo.zip foo/
  • -s 切分单元的大小,可选的单位有k(kB), m(MB), g(GB), t(TB),默认为m
  • -r 或者 --recurse-paths 递归目录
  • -x 或者 --exclude 忽略文件

切分已有zip文件

zip existing.zip --out new.zip -s 50m

将创建

new.zip
new.z01
new.z02
new.z03

继续阅读 →

最近帮外甥女写一段C程序作业。代码量不多,所有采用Sublime Text + gcc的方式。遇到了奇怪的segmentation fault,没有显示具体错误行号,所有需要借助gdb(The GNU Project Debugger 是*nix环境下著名的调试程序)返回更多有效信息和断点调试。

安装

brew install gdb

开启调试编译选项

希望gdb调试时输出行号与堆栈等详细信息需要gcc编译的时候使用-g选项

gcc -o course_test -g -rdynamic course_test.c

注意 homebrew安装的gnu版本的gcc在macox上不支持-rdynamic选项,此处使用的是xcode提供的gcc

继续阅读 →

页面底部放置了几个功能按钮,当页面长度超过窗口高度时需要滚动到底部才能操作。点击按钮前多了一步滚动操作,多少有些不方便。如果元素位于可视区域之外时,可以固定漂浮于窗口底部就可以解决这种不便。

不在可视区域内时固定漂浮于底部

继续阅读 →