Javascript 代码片段笔记

2016/03/30 routerpromiseevent

# 千位分隔符

function commafy(num) {

    num = num + '';

    var reg = /(-?\d+)(\d{3})/;

    while(reg.test(num)) {
        num = num.replace(reg, '$1,$2');
    }

    return num;
}

# jQuery 实现简单 Router

(function($) {
    var pathname = location.pathname;
    $.route = function() {
        $.each(arguments, function(index) {
            var path = this["path"];
            var func = this["func"];
            if(path && func) {
                if(pathname.match(path)) {
                    $(function() {
                        func.apply(this);
                    });
                }
            }
        });
    };
})(jQuery);

// Example
$.route(
    {
        // always match
        // http://example.com/foo/bar/baz.html
        path: /./,
        func: function() {
            console.log("always match!");
        }
    },

    {
        // url contains /sample/index.html
        // http://example.com/foo/bar/sample/index.html
        path: /\/sample\/index\.html/,
        func: function() {
            console.log("sample page!");
        }
    }
);

# 中文输入法截断解决方案

// 利用 compositionstart 和 compositionend 来捕获 IME (input method editor) 的启动和关闭事件
$('#text').on('input', function(e){
    if($(this).prop('comStart')) return;

    var value = $(this).val();
    console.log('当前输入:' + value);

    // ....
})
.on('compositionstart', function(e){
    $(this).prop('comStart', true);
    console.log('中文输入,开始');
})
.on('compositionend', function(e){
    $(this).prop('comStart', false);
    console.log('中文输入,结束');
});

# jQuery pub/sub 简单实现

(function($) {

    var o = $({});

    $.subscribe = function() {
        o.on.apply(o, arguments);
    };

    $.unsubscribe = function() {
        o.off.apply(o, arguments);
    };

    $.publish = function() {
        o.trigger.apply(o, arguments);
    };

}(jQuery));

# 观察者模式

var eventSplitter = /\s+/;

var Events = {
    /**
     * _callback = {
     *      tail: [Object],
     *      next: {
     *          callback: [Function],
     *          context: [Object],
     *          next: {
     *              callback: [Function],
     *              context: [Object],
     *              next: [Object]
     *          }
     *      }
     * }
     *
     * 1. 事件列表最顶层存储了一个 tail 对象,它是最后一次绑定的回调事件的 next 的引用;
     * 2. evts 允许指定多个事件名,通过空白字符进行分隔(如空格, 制表符等);
     * 3. 当事件名称为"all"时, 在调用 fire 方法触发任何事件时,
     * 均会调用"all"事件中绑定的所有回调函数;
     */
    on: function(evts, callback, context) {
        var evt, calls, node, tail, list;

        if(!callback) return this;

        evts = evts.split(eventSpliter);
        calls = this._callbacks || (this._callbacks = {});

        while(evt = evts.shift()) {
            list = calls[evt];
            node = list ? list.tail : {};
            node.next = tail = {};
            node.callback = callback;
            node.context = context;

            calls[evt] = {
                tail: tail,
                next: list ? list.next : node;
            }
        }

        return this;
    },

    off: function(evts, callback, context) {
        var evt, calls, node, tail, cb, ctx;

        if(!(calls = this._callbacks)) return this;
        if(!(evts || callback || context)) {
            delete this._callbacks;
            return this;
        }

        evts = evts ? evts.split(eventSpliter) : Object.key(calls);

        while(evt = evts.shift()) {
            node = calls[evt];
            delete calls[evt];

            if(!node || !(callback || context)) continue;

            tail = node.tail;
            while((node = node.next) !== tail) {
                cb = node.callback;
                ctx = node.context;

                if((callback && cb !== callback) || (context && ctx !== context)) {
                    this.on(evt, cb, ctx);
                }
            }
        }

        return this;
    },

    fire: function(evts) {
        var evt, node, calls, tail, args, all, rest;

        if(!(calls = this._callbacks)) return this;
        all = calls.all;
        evts = evts.split(eventSpliter);
        rest = Array.prototype.slice.call(arguments, 1);

        while(evt = evts.shift()) {
            if(node = calls[evt]) {
                tail = node.tail;
                while((node = node.next) !== tail) {
                    node.callback.apply(node.context || this, rest);
                }
            }

            if(node = all) {
                tail = node.tail;
                args = [evt].concat(rest);

                while((node = node.next) !== tail) {
                    node.callback.apply(node.context || this, args);
                }
            }
        }

        return this;
    }
}

# 短小强悍的 Javascript 异步调用库

var queue = function(funcs, scope) {
    (function next() {
        if(funcs.length > 0) {
            funcs.shift().apply(scope, [next].concat(Array.prototype.slice.call(arguments, 0)));
        }
    })();
}


// Example
var obj = { value: null };

queue([
    function(callback){
        var me = this;
        setTimeout(function(){
                me.value = 10;
                callback(20);
        });
    },
    function(callback, add){
        console.log(this.value + add);
        callback();
    },
    function(){
        console.log(obj.value);
    }
], obj);

# 干掉鼠标右键菜单

function NoRightClick(pid) { // pid: flash's parentNode id
    var el = document.getElementById(pid);
    if(el.addEventListener) {
        el.addEventListener('mousedown',function(event){
            if(event.button == 2){
                event.stopPropagation(); // for firefox
                event.preventDefault();  // for chrome
            }
        },true);
    }
    else {
        el.attachEvent('onmousedown', function() {
            if(event.button == 2) {
                el.setCapture();
            }
        });

        el.attachEvent('onmouseup', function() {
            el.releaseCapture();
        });

        el.oncontextmenu = function() {
            return false;
        };
    }
};

# 判断是否是素数

function isPrime(num) {
    return n < 2 ? false : !/^(11+?)\1+$/.test(Array(num+1).join(1));
}

# Babel 的 polyfill 和 runtime 的区别

  • babel-plugin-transform-runtime: 适用于 JavaScript 库和工具包的实现,避免 babel 编译的工具函数在每个模块里重复出现,减小库和工具包的体积

  • babel-polyfill: 转译 javascript 新的对象和方法,为当前环境提供一个垫片

总结:

  • 具体项目还是需要使用 babel-polyfill,只使用 babel-runtime 的话,实例方法不能正常工作(例如 "foobar".includes("foo"))

  • JavaScript 库和工具可以使用 babel-runtime,在实际项目中使用这些库和工具,需要该项目本身提供 polyfill

上次更新: 2021/8/7 上午3:33:06