【问题标题】:Sensible approach to callbacks on object prototype methods in JavaScript/jQuery?JavaScript/jQuery 中对象原型方法回调的明智方法?
【发布时间】:2015-07-13 16:58:54
【问题描述】:

我在下面所做的是否是一种合理的方法,允许回调在对象原型中定义的函数上运行,这样范围是正确的?

我一直在努力设置this 值的正确方法,当对象的原型方法是响应可能源自AJAX 请求或单击绑定或其他的回调的方法时。

这是一个简化的注释版本:

// everything is inside an object which provides the namespace for the app

var namespace = {

// a fairly vanilla object creation routing, which uses the prototype
// approach for defining the functions on the object

newObj : function(params) {
    var MyObj = function(params) {
        this.property = params.property
    };
    MyObj.prototype = namespace.ObjPrototype;
    return new MyObj(params);
},

// the prototype itself, which defines 2 related functions

ObjPrototype : {

    // The first is called to do some form of asynchronous operation
    // In this case it is an ajax call

    doAsync: function (params) {
        $.ajax({
            type: "get",
            url: params.url,
            data: params.data,
            dataType: params.datatype,
            success: namespace.objClosure(this, "asyncSuccess", ["data"]),
        });

        // the final line above is the key here - it asks a function (below)
        // for a closure around "this", which will in turn run the 
        // function "asyncSuccess" (defined next) with the argument "data"
    },

    // This is the actual callback that I want to run.  But we can't
    // pass this.asyncSuccess to the ajax function above, because the
    // scope at execution time is all wrong

    asyncSuccess : function(params) {
        this.property = params.data;
    },
},

// This is the bit I sort of invented, to help me around this problem.
// It returns a function which provides a closure around the object
// and when that returned function is run it inspects the requested
// arguments, and maps them to the values in the JS default
// "arguments" variable to build a parameters object which is then
// passed to the function on the object

objClosure : function(obj, fn, args) {
    return function() {
        if (args) {
            var params = {};
            for (var i = 0; i < args.length; i++) {
                params[args[i]] = arguments[i];
            }
            obj[fn](params);
        } else {
            obj[fn]();
        }

    }
}

}

现在,显然实际的目标回调 MyObj.asyncSuccess 需要知道它将获得一个 params 对象,以及它将是什么结构,并且该知识必须由调用函数 MyObj.doAsync 共享,否则这个看起来效果不错。

我的问题是——我完全疯了吗?我是否错过了一些明显的东西,可以以更简单/不那么复杂的方式为我解决这个问题?到了这个阶段,我是不是离兔子洞太远了?

我已经阅读了很多关于 SO 的问题,它们都解决了我的部分问题,但我似乎还没有深入了解普遍接受的解决方案。我不可能是唯一一个想要这样做的人:)

编辑

我已接受以下答案,但您也需要阅读所有 cmets 才能将其结合在一起。感谢大家的帮助!

【问题讨论】:

  • 这看起来很有用,谢谢。我可能需要更深入地研究文档,但我不确定如何让 bind() 获取任意数量的参数。如果您执行 fn.bind(obj, arguments),该函数会接收一个包含 JS 默认“参数”对象的参数,而不是将其解压缩到函数期望的许多实际参数中。如果这是有道理的。所以你不能在一个像fn(arg1, arg2, arg3)这样的函数上使用它
  • 当你想为你的调用“展开”参数时,如果你想摆脱数组的问题,请使用 Function.apply(参见 mdn)。我这里做了一个小例子:jsbin.com/feyuwogobi/1使用jsbin之类的快速测试此类问题。
  • 魔术!这正是我所追求的。

标签: javascript jquery ajax closures prototypal-inheritance


【解决方案1】:

你不是把事情复杂化了吗?看看下面的代码是否对你有帮助。我没有完全理解你的意图,但下面的代码应该可以帮助你

function newObj(params) {
    function asyncSuccess(params) {
        this.property = params.data;
    }

    function objClosure(obj, fn, args) {
        return function() {
            if (args) {
                var params = {};
                for (var i = 0; i < args.length; i++) {
                    params[args[i]] = arguments[i];
                }
                obj[fn](params);
            } else {
                obj[fn]();
            }

        }
    }
    this.property = params.property
    this.doAsync = function (params) {
    console.log('reached async');
        $.ajax({
            type: "get",
            url: params.url,
            data: params.data,
            dataType: params.datatype,
            success: objClosure(this, "asyncSuccess", ["data"]),
        });       
    }    
}
var k = new newObj({'property':'xyz'});
k.doAsync();

在看到“GameAlchemist”的评论后,我研究了 objClosure 函数,我认为我们可以通过使用以下代码进一步即兴发挥:我仍然不确定 this.property 或 data 的值是什么来给出正确的解决方案,因此只是假设几件事

function newObj(params) {
    function asyncSuccess(params) {
        this.property = params ? params.data : null;
    }

    function objClosure(args) {
        return function() {
            if (args) {
                var params = {};
                for (var i = 0; i < args.length; i++) {
                    params[args[i]] = arguments[i];
                }
                asyncSuccess(params);
            } else {
                asyncSuccess();
            }

        }
    }
    this.property = params.property
    this.doAsync = function (params) {
    console.log('reached async');
        $.ajax({
            type: "get",
            url: params.url,
            data: params.data,
            dataType: params.datatype,
            success: objClosure(["data"]),
        });       
    }    
}

这里有几个问题: 如果您已经将 params.data 传递给数据,即 data:params.data 如何再次分配值 this.property = params.data?很少有事情令人困惑,但我希望上述解决方案有效:)

【讨论】:

  • “你不是把事情复杂化了吗?” - 可能,这就是我问题的症结所在:)
  • 我所拥有的和你所拥有的之间的主要区别似乎在于对象构造。我采用了基于原型的方法,您在构造函数中定义函数,一些是公共的,一些是私有的。对我来说有趣的是 objClosure 位在此更改中仍然存在,因为这是我主要质疑的位。所以,这是令人鼓舞的!我认为对象构造的风格在这里并不是最重要的(尽管我可能是错的)——我认为原型方法让我在以后添加其他共享函数时更加灵活。
  • 顺便说一句,我有几个对象可能想要使用 objClosure,所以我可能会在所有构造函数/原型之外定义它,以便它更可重用,但这确实增加了正如你所指出的,调用它。
  • 我对 objClosure 不好,我没有给予足够的重视:function objClosure(obj, fn, args) { var _args = args.slice(); _args.unshift(obj);返回 Function.bind.apply(fn, _args); }
  • @RichardJ Prototype 主要用于继承或者如果您想为现有对象添加功能!您无需在原型中使用某些东西来使其可用于所有对象。在具有公共访问权限的对象本身内使用它。我们将原型与 jquery 或其他对象一起使用的原因是该对象已经构建并且您希望使其他功能可用。如果功能是最初设计的,那么 jquery 将只有一个公共函数,而不是原型中使用的函数
猜你喜欢
  • 1970-01-01
  • 2014-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 2011-10-22
相关资源
最近更新 更多