Javascript hashCode 函数

网上找了好一轮,找到个比较像样而且足够短的 hashCode 实现,是从 Java 的 hashCode 中借鉴而得的。原理见 Java hashCode() ,也可以跟这里的 Java String 的源码 参照对比一下。

为了使用的方便,稍稍再改良了一下

function hashcode(str) {
  var hash = 0, i, chr, len;
  if (str.length === 0) return hash;
  for (i = 0, len = str.length; i < len; i++) {
    chr   = str.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
}

hashcode("this is a string")
//-1853110172

这里接受的参数是一个 String,其它类型怎么办?可以先做一个统一的处理,比如

hashcode(JSON.stringify(obj))

序列化之后再使用 hashCode 函数,基本所有类型数据都通吃,除了含有循环嵌套的对象。

PS:
函数实现中有一行使用了 “|” 运算符,只是利用 Bitwise 运算符转换参数为 32bit,用来确保结果是个 32位整数。

Javascript void(0)

JavaScript-logo常有人疑惑一个看上去很简单的问题,<a href=”javascript:void(0)”>some button</a> 里面的 void(0) 是什么意思?又或者不明白为什么好像很多人要这么写而不是其它更加简洁的写法,比如 <a href=”#”>some button</a>

href=”javascript:void(0)” 实际上是为了在 link 被点击的时候,返回一个空值,告诉浏览器什么都不用做。因为浏览器会把当前页面里的内容替换为返回的结果。(这个效果反而让我感到奇怪,因为真的很少这么用)

而 href=”#” 看上去简洁,但是它其实有个默认的行为,会把页面重新定位到页面顶部,这就是为什么页面会跳一下的一个原因。因为更多的情况,点击后页面保持在原位才是我们想要的结果。使用 # 甚至可能出现一些更加奇葩的问题

除了这个,还有没有其它更加简洁的写法呢?会不会是 <a href=””>some button</a> 呢?这个点击后会刷新当前页面,显然不符合要求。应该是 <a href=”javascript:;”>some button</a> ,什么都不执行。但有时过分简洁也容易造成“不清晰”的印象从而引起不必要争拗。如果从代码可读性考虑,也为了团队内沟通少点分歧,我会建议使用 href=”javascript:void(0)”。

void 操作符最原始就是用来“制造” undefined ,特别是某段代码只需要其副作用(side effect)又不希望有返回值产生。void 本身没有副作用,也没有正作用(返回值是空),但却能包含一切,犹如黑洞一般神秘。

== 参考 ==
Void Operator in MDN

Replace [class*=”span”] in LESS

Bootstrap 里的 grid system 里面 (源代码) 有这么一段,

[class*="span"] {
  float: left;
  margin-left: @gridGutterWidth;
}

RECESS 去跑会看到提示:Universal selectors should be avoided。这个既可以说是 selector 的问题,也可以说是 RECESS 的问题,但可以在运行的时候加个参数忽略掉。

自己去写扩展时也会写到类似的规则,比如需要兼容某浏览器,如果不这样写,就会需要像 Bootstrap-IE6 那样写 Continue reading

不错的 JavaScript 格式

在重制 在线密码生成器 这个单页工具时,尝试的一种交互方式需要 tooltip (来自 Bootstrap) 作为操作提示的载体,动态修改 tooltip 的内容。于是查阅了其 源代码,虽然没发现有这样的功能支持(后来小改了一行之后就可以了),但是却发现其代码格式有些有趣的地方。

Tooltip.prototype = {

  constructor: Tooltip

, init: function (type, element, options) {
    var eventIn
      , eventOut

    this.type = type
    // ...
    this.options.selector ?
      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
      this.fixTitle()
    }
// ...
}

可以注意到下面几点: Continue reading

Javascript Object 杂谈

看到 一个论坛帖子 问到:(类似下面的代码)

var arr = [1, 2, 3];
arr.a = 'a';
arr.b = 'b';

为什么可以这样写?arr 自身到底是什么?

原因其实很简单,因为 js 里一切都是对象,array 也是 object,所以它就能做所有 object 能做的事情,比如,给它加个属性 a

如果 Array 能那么搞,BooleanDate, Function, NumberRegExpString 也可以就不足为奇了。

function toStr(obj) {
  return Object.prototype.toString.call(obj); 
}

function addAttr(arr) {
  for(var i = arr.length-1; i >= 0; i--) {
    arr[i].attr = toStr(arr[i]);
  }
  return arr;
}

function checkAttr(arr) {
  for(var i = 0; i < arr.length; i++) {
    console.log(typeof arr[i], arr[i].attr);
  }
}

var arr = [
    new Boolean(false), 
    new Date(), 
    new Function(), 
    new RegExp('\\w+'), 
    new Number(1.0), 
    new String('str')];
checkAttr(addAttr(arr));

/** output >>
object [object Boolean]
object [object Date]
function [object Function]
object [object RegExp]
object [object Number]
object [object String]
*/

细心的人会发现上面都是使用 new 构造的对象,如果换成是字面方式 (literal) 定义的原生类型 (primitive) 又会是如何呢? Continue reading

Number 的失真边界

Javascript 中的 Number 是个神奇的类型,它模糊了 整形浮点数 之间的边界,当你拿到一个 Number 的时候,它就只是一个 Number,而且可以很方便地表示非常大的数 Number.MAX_VALUE 或者,或者非常小的数 Number.MIN_VALUE。
实际应用场合里面除了要表示大数和小数,有时还会讲究下精度的。

MDN 的 Number.toPrecision 中有描述 ECMA-262 only requires a precision of up to 21 significant digits. Other implementations may not support precisions higher than required by the standard.”

我们可以知道 JavaScript 的精度在 21 位。究竟是不是代表只要少于 21 位的数都能精确表示呢?简单来个试验就可以知道了。可以猛击这个链接 看看你的浏览器里的 js 到底能有多精确

var arr = [];
for(var i = 0; i< 21; i++) {
  arr.push('9');
  console.log((i+1) + ' : ' + arr.join('') + ' >>' + Number(arr.join('')));
}

我在 Firefox 11 上的结果是到 16 位的时候就已经失真了。

如果是小数的情况:

var arr = ['9.'];
for(var i = 0; i< 21; i++) {
  var str = arr.join('') + '9';
  push((i+1) + ' : ' + str, Number(str));
  arr.push('9');
}

同样地,小数位达到 15 位时出现失真。上面的试验用的是 Number() 进行转换,这与手写同样长度的数字效果是一样的。

幸运的是,这些精度基本够用了。但如果哪位仁兄希望用 JavaScript 来处理一些高精度方面的问题,就必须得注意了,其实 15 位已经是一道坎了。

函数抽象

JavaScript 里 Array 非常常用。还记得当初是因为直接字符串拼接效率好差,但是用 […].join(”) 却神奇地快而使我印象深刻。

在 JavaScript 1.6 里还引入了一些新的方法,虽然有些看上去好像没什么特别,甚至会使人疑惑为什么需要多这么个方法。我很好奇这是基于什么设计出来的。

忘记了是从哪里看来的,说一些方法是为了可以支持函数式编程而引入的。比如 forEach, filter, every, map, reduce

实际使用过后,还真是尝到甜头了。最直接的感觉就是,代码变短了(不是字符数,而是独立的语法单位数量)。比如 every

如果没有它,你需要写这些代码

var arr = [15, 20, 30];
var isbigger = true;
for (var i = 0; i < arr.length; i++) {
    if (!(arr[i] > 10))  {
        isbigger = false;
        break;
    }
}
isbigger === true

有了它,你可以这样写

var arr = [15, 20, 30];
arr.every(function(it) {
    return it > 10;
}) === true;

少定义了一个变量 isbigger, 没有了 for 循环,自然也不需要考虑什么时候 break 了。需要做的判断可以非常清晰地用 it > 10 表达出来。真是简单明快。

或许想要表达的就是这种抽象方式,将某种特定过程抽象成一个 every,使用者可以只关心所需要的逻辑。而这个逻辑是用一个函数来表示的,作为参数传入到 every 当中去。由于 JavaScript 支持函数作为参数,使得使用函数来抽象非常的方便,不需要额外构建一些模式来进行胶连。如果从这个角度看,的确很符合函数式编程的模式。

再看看 every 的实现,和第一段代码的主体是非常的相像的。

/**
 * Copyright (c) Mozilla Foundation http://www.mozilla.org/
 * This code is available under the terms of the MIT License
 */
if (!Array.prototype.every) {
    Array.prototype.every = function(fun /*, thisp*/) {
        var len = this.length >>> 0;
        if (typeof fun != "function") {
            throw new TypeError();
        }

        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this && !fun.call(thisp, this[i], i, this)) {
                return false;
            }
        }

        return true;
    };
}

就这样一个独立功能的简单函数就可以实现关注点的分离。而且,分离之后,各自都可以很好地测试,关注点的单一,使得测试更加容易进行。功能变得简单(专注)了,少打了许多字,代码更加清晰易懂,出错的几率也相应地减少。出错少了,你会感觉是自己变聪明了(Ruby 创始人在一次演讲中说到的一个很有趣的观点)。

同样的原理,我觉得是可以很方便地应用到自己编写的代码当中去,仔细分析代码,分离不同的关注点,是实现自下而上的编程方式的重要一环。

也许看上去很简单,但是我觉得这种抽象方式作为一个很好的基础,可以帮助我们设计出更好的代码。

动态加载脚本

动态装载脚本是很简单的,不是通过 script 标签实现,就是通过 XMLHTTP 请求内容之后执行。后者会受到同域限制,而处于各种原因,脚本跨域引用是很常见的,这样对比起来前者适应性更广。

既可以用很 DOM 的方式创建 script 标签:

function getScript(path, callback) {
    var el = document.createElement("script");
    if (callback) {
        el.onload = el.onreadystatechange = function() {
            if (!this.readyState || this.readyState === 'complete' || 
                    this.readyState === 'loaded') {
                el.onload = el.onreadystatechange = null;
                callback();
            }
        };
    }
    el.src = path;
    document.getElementsByTagName("HEAD")[0].appendChild(el);
}

也可以使用 document.write(): Continue reading

低调的 valueOf()

JavaScript Object 上的 valueOf() 方法,是不是看上去很冷门的样子?貌似都不怎么用的。但是,真的没有用到吗?

可能从命名上,你可以了解到它可以返回当前对象的原始值(primitive value)。Number, Date, Boolean 对象会返回其原始值。比如:

new Number(10).valueOf() === 10  // true

作为 Object prototype 上的方法,它够低调的了。低调到用了都不知道。因为实际上,你几乎用不着去调用它,在需要的时候,会被自动调用。比如,进行比较操作,看下面的代码。 Continue reading