JS - 链式写法

链式写法无疑是jQuery发扬光大的,因为在整理设计模式,所以就对链式写法整理了下。

起手式

链式写法最基本简陋的,大概就是这么比划一下了:

var Base = function(num) {
    console.log('init...');
    this.number = num;
}
Base.prototype.add = function(num) {
    console.log('add...');
    this.number += num;
    return this;
}
Base.prototype.minus = function(num) {
    console.log('minus...');
    this.number -= num;
    return this;
}
var base = new Base(12);
base.add(10).minus(5);

当我们需要一个对象是,我们需要通过new来创建一个。

可是作为前端,谁没用过jQuery呀,使用时候也没看到需要new来创建一个对象呀。那么,我们来升级下。

改进版

不需要通过new来创建对象,开箱即用:

var BB = function(selector) {
    return new Base(selector);
}
Base = function(selector) {
    console.log('init');
    this.doms = document.getElementsByTagName(selector);
    return this;
}
Base.prototype = {
    toArray: function() {
        console.log('toArray');
        return this;          
    },
    showList: function() {
        console.log(this.doms.length);
        return this;         
    }
}
BB('span').toArray().showList();

我们把new对象的操作封印在了BB的返回中。下边又有需求了,需要扩展呀,作者你又调皮欺负我们没用过jQuery? jQuery可是通过$.fn.extend可以写扩展的,你这个没有。

来,我们再改改。

扩展版

实现支持扩展功能

var BB = function(selector) {
    return new Base(selector);
}
Base = function(selector) {
    console.log('init');
    this.doms = document.getElementsByTagName(selector);
    return this;
}
Base.prototype = {
    toArray: function() {
        console.log('toArray');
        return this;          
    },
    showList: function() {
        console.log(this.doms.length);
        return this;         
    },
    extend: function(obj){
        // 以下代码未考虑objKey存在以及obj[objKey]不为函数的情况
        for(var key in obj) {
            this[key] = obj[key];
            // 或
            // Base.prototype[key] = obj[key];
        }
    }
}
Base.prototype.extend({'myAdd': function(a,b){console.log(a + b); return this}})
BB('span').toArray().showList().myAdd(2,3);

功能是有了,不过读者又说了。不好,你这个还有缺陷,平白多了一个Base对象,看着这么多语,我jQuery可没这么冗余。恩,这下可有点麻烦,容我想一想。

单对象版

不再出现冗余对象Base

var BB = function(selector, context) {
    return new BB.fn(selector, context);
}
BB.fn = function(selector, context){
    console.log('init');
    this.doms = document.getElementsByTagName(selector);
    return this;    
}
BB.fn.prototype = {
    toArray: function() {
        console.log('toArray');
        return this;
    },
    showList: function() {
        console.log(this.doms.length);
        return this;
    },
    extend: function(obj){
        // 以下代码未考虑objKey存在以及obj[objKey]不为函数的情况
        for(var key in obj) {
            this[key] = obj[key];
        }
    }   
}
BB.prototype.extend({'myAdd': function(a,b){console.log(a + b); return this}})
BB('span').toArray().showList().myAdd(2,3);

读者看了看,好是好了,不过你的扩展写法为什么要出现prototype,这对新手不友好,都ES6时代了,你好要我搞prototype

好好好,是我的错。爱护前端小白从我做起。

仿jQuery版

prototyep也包裹起:

var BB = function(selector, context) {
    return new BB.fn.init(selector, context);
}
BB.fn = {
    init: function(selector, context){
        console.log('init');
        this.doms = document.getElementsByTagName(selector);
        return this;
    },
    toArray: function() {
        console.log('toArray');
        return this;
    },
    showList: function() {
        console.log(this.doms.length);
        return this;
    },
    extend: function(obj){
        // 以下代码未考虑objKey存在以及obj[objKey]不为函数的情况
        for(var key in obj) {
            this[key] = obj[key];
        }
    }   
}
BB.fn.init.prototype = BB.fn
BB.fn.extend({'myAdd': function(a,b){console.log(a + b); return this}})
BB('span').toArray().showList().myAdd(2,3);

真实的jQuery如何呢?差不多

jQuery版

jQuery彻底的使用fn抛弃了原型链prototype.让BB.fn直接等于BB.prototype。替代BB的原型链。

var BB = function(selector, context) {
    return new BB.fn.init(selector, context);
}
BB.fn = BB.prototype = {
    init: function(selector, context) {
        console.log('init');
        this.doms = document.getElementsByTagName(selector);
        return this;
    },
    toArray: function() {
        console.log('toArray');
        return this;
    },
    showList: function() {
        console.log(this.doms.length);
        return this;
    },
    extend: function(obj){
        // 以下代码未考虑objKey存在以及obj[objKey]不为函数的情况
        for(var key in obj) {
            this[key] = obj[key];
        }
    }   
}
// WD.fn.init.prototype = WD.fn;
BB.fn.init.prototype = BB.prototype;
BB.fn.extend({'myAdd': function(a,b){console.log(a + b); return this}})
BB('span').toArray().showList().myAdd(2,3);

所以说到这里,我们的单对象版其实就好用了。

为了能让小白用的舒服,源代码复杂点有如何?也许就是jQuery作者的心声了。

通过参考jQuery的链式写法,我们能学习或理解到如下:

  • 尽可能少用全局变量:否则就会出现我们的改进版两个全局对象

  • 为小白服务到家

    • 拒绝使用new
    • 拒绝使用prototype

    链式写法的舒适性,也能看到作者的良苦用心:写js代码,一样可以有容易上手的交互

参考链接

@2018-03-02 21:03