链式挪用&工场

链式挪用

精华在于重用一个初始操纵.

可以把要领的链式挪用手艺写到本身所写的全部 js 库中,把本身喜好的要领串起来挪用.两个部份:一个建立代表 HTML 元素的对象的工场,另有一批对这个 HTML 元素实行某些操纵的要领.每一个这类要领都可以在要领名前附加一个圆点后到场挪用链中.要领的链式挪用可以被视为挑选网页上的一个元素并对其举行多个操纵的历程.
addEvent($('.example'), 'click', function () {
    // Without chaining:
    $(this).hide();
    setStyle(this, 'color', 'green');
    $(this).show();

    // With chaining;
    $(this).hide().setStyle('color', 'green').show();
});

挪用链组织

$函数通常会返回一个HTML 元素(的鸠合):

function $() {
    var eles = [];
    for (var i = 0, len = arguments.length; i < len; ++i) {
        var ele = arguments[i];
        if (typeof ele === 'string') {
            ele = document.getElementById(ele);
        }
        if (arguments.length === 1) {
            return ele;
        }
        eles.push(ele);
    }
    return eles;
}

假如把这个函数改形成一个组织器,把那些元素作为数组保存在一个实例属性中,并让一切定义在组织器函数的 prototype 属性所指对象中的要领都返回用来挪用要领的谁人实例的援用,那末它就具有链式挪用的才能.
做一下革新:首先把$函数改成一个工场要领,担任建立支撑链式挪用的对象,这个函数应当能接收元素数组情势的参数,所以我们可以运用和本来一样的公有接口:

(function () {
    // Use a private class.
    function _$(els) {
        this.eles = [];
        for (var i = 0, len = els.length; i < len; ++i) {
            var ele = els[i];
            if (typeod ele === 'string') {
                ele = document.getElementById(ele);
            }
            this.eles.push(ele);
        }
    }

    // The public interface remains the same.
    window.$ = function () {
        return new _$(arguments);
    };
})();

由于一切对象都邑继续其原型对象的属性和要领,所以我们可以让定义在原型对象中的那几个要领都返回用以挪用要领的实例对象的援用,如许便可以对哪些要领举行链式挪用.如今在_$这个私有组织函数的 prototype 对象中增加要领:

(function () {
    function _$(eles) {
        // ...
    }
    _$.prototype = {
        each: function (fn) {
            for (var i = 0, len = this.eles.length; i < len; ++i) {
                fn.call(this, this.eles[i]);
            }
            return this;
        },
        hide: function (0 {
            var that = this;
            this.setStyle('display', 'none');
        });
        setStyle: function (prop, val) {
            this.each(function (ele) {
                ele.style[prop] = val;
            });
            return this;
        },
        show: function (0 {
            var that = this;
            this.setStyle('display', 'block');
        });
        return this;
        addEvent: function(type, fn) {
            var add = function (ele) {
                if (window.addEventListener) {
                    ele.addEventList(type, fn, false);
                }
                else if (window.attachEvent) {
                    ele.attachEvnet('on' + type, fn);
                }
            };
            this.each(function (el) {
                add(el);
            });
            return this;
        }
    };
    window.$ = function () {
        return new _$(arguments);
    };
})();

每一个要领的末了一行return this;会讲挪用要领对象传给挪用链上的下一个要领.
jQuery 就是如许,window 对象或许某个 HTML 元素是挪用链的锚点,多有操纵都挂系在上面.

运用回调从支撑链式挪用的要领猎取数据

链式挪用很合适于赋值器要领,然则关于取值器要领,并不愿望要领返回 this.不过运用回调手艺可以返回你想要的数据而不是 this.

// Accessor without function callbacks: returning requested data in accessors.
window.API = window.API || function () {

    var name = 'Hello world';
    // Privilleged mutator
    this.setName = function(newName) {
        name = newName;
        return this;
    };
    // Privileged accessor method.
    this.getName = function () {
        return name;
    };

};

// Implementation code
var o = new API;
console.log(o.getName()); // Displays 'Hello world'.
console.log(o.setName('nanci').getName()); // Display 'nanci'

// Accessor with function callbacks.
window.API2 = window.API2 || function () {

    var name = 'Hello world';
    // Privilleged mutator
    this.setName = function(newName) {
        name = newName;
        return this;
    };
    // Privileged accessor method.
    this.getName = function (callback) {
        callback.call(this, name);
        return this;
    };

}

// Implementation code
var o2 = new API2;
o2.getName(console.log).setName('nanci').getName(console.log); // Displays 'Hello world' and then display 'nanci'

小结

运用链式挪用可以防备屡次反复运用一个对象变量,削减代码量.
假如想让类的接口保持一致,让取值器像赋值器那样也支撑链式挪用,那末可以运用回调.

———-another part———-

工场

一个类或许对象中能够包括别的对象.在建立这类成员对象时,可以运用 new 关键字或许类组织函数.然则会致使两个类发生依靠.工场形式就是用来消弭这类依靠.他运用一个要领来决议究竟要实例化哪一个详细的类.简朴的工场运用一个类(通常是单体)来天生实例,庞杂的工场形式运用子类来决议一个成员变量应当是哪一个详细的类的实例.

简朴工场

假如你想开几个自行车市肆,每一个店都有几种型号的自行车出卖,用一个类示意:

// BicycleShop class.

var BicycleShop = function () {};
BicycleShop.prototype = {
    sellBicycle: function(model) {
        var bicycle;

        switch(model) {
            case 'The Speedter':
                bicycle = new Speedter();
                break;
            case 'The Lowrider':
                bicycle = new Lowrider();
                break;
            case 'The Comfort Cruiser':
            default:
                bicycle = new ComfortCruiser();
        }

        Interface.ensureImplements(bicycle, Bicycle);
        bicycle.assemble();
        bicycle.wash();

        return bicycle;
    }
};

sellBicycle 要领依据所要求的自行车型号用 switch 语句建立一个自行车的实例.种种型号的自行车实例可以交换运用,由于他们都完成了 Bicycle 接口(接口在工场中很重要,假如不对对象举行某种范例搜检以其确保其完成了必需的要领,那末工场形式并不能带来什么优点).

// The Bicycle interface.

var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair']);

// Speedster class.
var Speedster = function () { // implement Bicycle
    ...
};
Speedster.prototype = {
    assemble: function () {
        ...
    },
    wash: function () {
        ...
    },
    ride: function () {
        ...
    },
    repair: function () {
        ...
    }
};

要出卖某种型号自行车,只须要挪用 sellBicycle 要领即可:

var californiaCruisers = new BicycleShop();
var yourNewBike = californiaCruisers.sellBicycle('The Speedster');

假如你想在供货目次中到场一款新车型,更好的解决办法是把 sellBicycle 要领中”建立新实例”这部份事情转交给一个简朴工场对象.

// BicycleFactory namespace.

var BicycleFactory = {
    createBicycle: function (model) {
        var bicycle;

        switch(model) {
            case 'The Speedter':
                bicycle = new Speedter();
                break;
            case 'The Lowrider':
                bicycle = new Lowrider();
                break;
            case 'The Comfort Cruiser':
            default:
                bicycle = new ComfortCruiser();
        }

        Interface.ensureImplements(bicycle, Bicycle);
        return bicycle;
    }
};

BicycleFactory 是一个单体,用来把 createBicycle 要领封装在一个定名空间中,这个要领返回一个完成了 Bicycle接口的对象,然后可以对其举行组装和洗濯:

// BicycleShop class, improved.

var BicycleShop = function () {};
BicycleShop.prototype = {
    sellBicycle: function (model) {
        var bicycle = BicycleFactory.createBicycle(model);

        bicycle.assemble();
        bicycle.wash();

        return bicycle;
    }
};

这个 BicycleFactory 对象可以供种品种用来建立新的自行车实例.有关可供车型的一切信息都集合在一个处所治理,所以增加更多车型很轻易:

// BicycleFactory namespace, with more models.

var Bicycle: function (model) {

    var bicycle;

    switch (model) {
        case 'The Speedster':
            bicycle = new Speedster();
            break;
        case 'The Lowrider':
            bicycle = new Lowrider();
            break;
        case 'The Flatlander':
            bicycle = new Flatlander();
            break;
        case 'The ComfortCruiser':
            bicycle = new ComfortCruiser();
    }

    Interface.ensureImplements(bicycle, Bicycle);
    return bicycle;
}

这是一个简朴工场的例子,他把成员对象的建立事情交给一个外部对象,这个外部对象可所以一个简朴的定名空间,也可所以一个类的实例.

示例: XHR 工场

用 Ajax 手艺提议异步要求是如今 Web 开辟的一个罕见使命.用于提议要求的对象是某品种的实例,详细是哪品种取决于用户的浏览器.假如代码中须要屡次实行 ajax 要求,那末可以把建立这类对象的代码提取到一个类中,并建立一个包装器来包装在现实提议要求时所要阅历的一系列步骤,简朴工场异常合适该场所,依据浏览器特征天生一个 XMLHttpRequest 或许 ActiveXObject 实例.

// AjaxHandler interface.
var AjaxHandler = new Interface('AjaxHandler', ['request', 'createXhrObject']);

// SimpleHandler class.

var SimleHandler = function () {}; // implements AjaxHandler
SimpleHandler.prototype = {
    request: function (method, url, callback, postVars) {
        var xhr = this.createXhrObject();
        xhr.onreadystatechange = function () {
            if (xhr.readyState !== 4) return;
            (xhr.status === 200) ? callback.success(xhr.responseText) : callback.failure(xhr.status);
        };
        xhr.open(method, url, true);
        if (method !== 'POST') {
            postVars = null;
        }
        xhr.send(postVars);
    },
    createXhrObject: function () { // Factory method.
        var methods = [
            function () {
                return newXMLHttpRequest();
            },
            function () {
                return new ActiveXObject('Msxml2.XMLHTTP');
            },
            function () {
                return new ActiveXObject('Microsoft.XMLHTTP');
            }
        ];

        for (var i =0, len = methods.length; i < len; i++) {
            try {
                methods[i]();
            }
            catch(e) {
                continue;
            }

            //If we reach this point, method[i] worked.
            this.createXhrObject = methods[i]; //Memoize the method.
            return methods[i];
        }

        // If we reach this point, none of the methods worked.
        throw new Error('SimpleHandler: Could not create an XHR object.');
    }
}

重要优点在于消弭对象间的耦合,经由过程运用工场要领而不是 new 关键字及详细类,你可以把一切实例化代码集合在一个位置.可以大大简化替换所用的类或许在运转时期动态挑选一切的类的事情.在派生子类时也更天真.可以先建立一个笼统的超类,然后在子类中建立工场要领,从而把成员对象的实例化推晚到更专门化的子类中举行.
一切这些优点都和面向对象设想的两条准绳相干: 弱化对象间的耦合:防备代码的反复.在一个要领中举行类的实例化,可以消弭反复性的代码.这是在用一个对接口的挪用庖代一个详细的完成.这些都有助于模块化代码.

不能把工场要领当万金油,而把一般函数扔在以便.假如基础不能够别的换用一个类或许不须要在运转时期在一系列类的挑选,那末就不应当运用工场要领.大多数最好运用 new 关键字和组织函数公然举行实例化,如许代码会更简朴易读..一眼就看到挪用的组织函数,不必去检察某个工场要领去晓得实例化的是什么类.

小结

    原文作者:南赐
    原文地址: https://segmentfault.com/a/1190000006914562
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞