Javascript - 创建可链接的函数而不会污染一切

时间:2013-10-21 22:22:52

标签: javascript angularjs chainable

我是JS的新手,但我已经完成了对这个主题的研究,没有找到详尽的答案。

我正在使用Angular JS进行项目,我有一个公开某些功能的服务:

var app = angular.module('myApp', [])

app.service('mySrv', function() {

    this.myFoo = function() {
        var anObject = {}
        //some code on the object...
        return anObject
    }

    this.myGoo = function() { // function(object) maybe?
        //some code on an object again
        return aNewObject
    }
}

然后在我的控制器中我想做像JQuery那样的事情,将myFoo的输出链接为myGoo的输入

$scope.myObj = mySrv.myFoo(['some','params']).myGoo('another_one')

我发现我可以使用原型属性来增加可链接性,如此处所述How to make chainable function in JavaScript? 但同样的答案说它不应该在对象上完成,这将是我的情况......

最后但并非最不重要的是,另一种方法是JavaScript Object Method Chaining: useful? 坦率地说,我不明白......

有人可以清除我心中的所有迷雾吗?

<小时/> 修改 使用和混合来自@plalx和@Connor的优秀示例,我设法让这个代码片段正常工作,正如您所看到的,缺少@plalx示例的“可链接部分”。

这部分究竟到底是做什么的?

var o = {
    setTest: function (test) {
        this.test = test;
        return this
    },
    getTest: function () {
        this.test = this.test.toUpperCase()
        return this
    },
    splitMe: function() {
        return this.test.split('')
    }
};

console.log( o.setTest('hello').getTest().splitMe() )

2 个答案:

答案 0 :(得分:1)

你必须这样使用return this;

app.service('mySrv', function() {

    this.myFoo = function() {
        var anObject = {}
        //some code on the object...
        return this;
    }

    this.myGoo = function() { // function(object) maybe?
        //some code on an object again
        return this;
    }
}

如果它是可连锁的,你不应该返回任何其他东西,因为你无论如何都无法使用它。就像jQuery中的所有内容一样,有些offset();不可链接。

答案 1 :(得分:1)

以下是一个关于如何使任何对象方法可动态链接的示例。

调用chainable(obj)将在内部创建一个新对象,该对象公开与obj相同的API,但是每个函数现在都将返回该新对象,而该对象又允许链接。如果要调用函数并获取该函数的原始结果,可以调用endChain()

以下实现效率不高,因为它必须遍历obj的所有成员。 更好的方法是通过从您想要链接的函数返回当前实例(this)来使您的方法可链接。

DEMO

var chainable = (function () {

    function wrapFn(fn, obj) {
        return function () {
            var result = this._result = fn.apply(obj, arguments);

            return this;
        };
    }

    function Chainable(obj) {
        var k, v;

        for (k in obj) {
            if (typeof (v = obj[k]) !== 'function') continue;

            this[k] = wrapFn(obj[k], obj);
        }
    }

    Chainable.prototype.endChain = function () {
        return this._result;
    };

    return function (obj) {
        return new Chainable(obj);
    };

})();

var o = {
    setTest: function (test) {
        this.test = test;
    },
    getTest: function () {
        return this.test;
    }
};

console.log(chainable(o).setTest('test').getTest().endChain());

这是一个不需要迭代对象成员的不同实现。然而,它在语法上不太吸引人,我不确定它是否更有效,因为它必须slice个参数。

<强> DEMO

var chainable = (function (slice) {
    var Chainable = {
        init: function (obj) {
            this._obj = obj;

            return this;
        },

        chain: function (fn) {
            var obj = this._obj;

            this._result = obj[fn].apply(obj, slice.call(arguments, 1));

            return this;
        },

        end: function () {
            return this._result;
        }
    };

    return function (obj) {
        return Object.create(Chainable).init(obj);
    };
})(Array.prototype.slice);

var o = {
    setTest: function (test) {
        this.test = test;
    },
    getTest: function () {
        return this.test;
    }
};

console.log(chainable(o).chain('setTest', 'test').chain('getTest').end());

编辑:

  

使用和混合来自@plalx和@Connor的优秀示例,我已经   设法让这个片段正常工作,正如你所看到的那样,它已经丢失了   @plalx例子的“可链接部分”。

如果您的代码已经以可以链接的方式编写(例如已经从某些函数返回chainable,则不需要this之类的内容。我应该说得更清楚。我刚刚编写了chainable实现作为链接如何工作的示例。

但是,使用chainable,您可以将任何对象设为可链接,即使它们的所有函数都没有明确返回this。如果您想通过其API使用不支持它的对象,这可能很有用。

E.g。

var o = {
    fn1: function () { console.log('fn1'); },
    fn2: function () { console.log('fn2'); }
};

//chainable(o).fn1().fn2(); //other implementation

chainable(o).chain('fn1').chain('fn2');

这是一个performance test,它表明您的对象可动态链接,对性能产生巨大的负面影响。出于这个原因,我不建议使用该实验方法。