【问题标题】:What object javascript function is bound to (what is its "this")?javascript函数绑定到什么对象(它的“this”是什么)?
【发布时间】:2013-01-13 19:28:16
【问题描述】:

我知道函数内部是this

var func = function {
    return this.f === arguments.callee; 
    // => true, if bound to some object
    // => false, if is bound to null, because this.f === undefined
}

var f = func; // not bound to anything;

var obj = {};
obj1.f = func; // bound to obj1 if called as obj1.f(), but not bound if called as func()

var bound = f.bind(obj2) // bound to obj2 if called as obj2.f() or as bound()

编辑:

您实际上不能调用obj2.f(),因为f 不会成为obj2 的属性

编辑结束。

问题是:如何在这个函数之外找到函数绑定的对象?

我想实现这个:

function g(f) {
  if (typeof(f) !== 'function') throw 'error: f should be function';

  if (f.boundto() === obj)
    // this code will run if g(obj1.f) was called
    doSomething(f);

  // ....

  if (f.boundto() === obj2)
    // this code will run if g(obj2.f) or g(bound) was called
    doSomethingElse(f);
}

在不改变函数绑定对象的情况下部分应用:

function partial(f) {
   return f.bind(f.boundto(), arguments.slice(1));
}

共识:

你做不到。外卖:使用bindthis要非常小心:)

【问题讨论】:

  • 除非函数是使用.bind() 创建的,否则函数不会绑定到任何东西。它们可以被对象引用,但没有对象绑定。
  • 我强烈怀疑答案是你不能这样做。
  • @h2ooooooo boundto() 是一种假设的方法,它不存在。让其他人认为不可能的代码更有效率有什么意义? :) 开个玩笑……
  • 函数是独立的实体。 this 所指的内容是在调用它们时确定的(即在运行时)。但是,如果f 已经使用.bind 绑定,那么再次调用.bind 无论如何都不会改变this
  • 另请参阅stackoverflow.com/q/7282158/471129,以讨论从绑定结果中获取原始函数的可能性,这是获取this 的另一只鞋。 (答案是你不能,至少使用标准绑定。)

标签: javascript function functional-programming this ecmascript-5


【解决方案1】:

部分申请

你可以做部分应用:

// This lets us call the slice method as a function
// on an array-like object.
var slice = Function.prototype.call.bind(Array.prototype.slice);

function partial(f/*, ...args */) {

    if (typeof f != 'function')
        throw new TypeError('Function expected');

    var args = slice(arguments, 1);

    return function(/* ...moreArgs */) {
        return f.apply(this, args.concat(slice(arguments)));
    };

}

这个函数绑定到什么对象?

此外,您的问题的第一部分有一个非常直接的解决方案。不确定这是否适合您,但您可以很容易地在 JS 中进行猴子补丁。猴子补丁bind是完全可以的。

var _bind = Function.prototype.apply.bind(Function.prototype.bind);
Object.defineProperty(Function.prototype, 'bind', {
    value: function(obj) {
        var boundFunction = _bind(this, arguments);
        boundFunction.boundObject = obj;
        return boundFunction;
    }
});

在任何其他脚本运行之前运行它,任何使用bind的脚本,它都会自动将boundObject属性添加到函数中:

function f() { }
var o = { };
var g = f.bind(o);
g.boundObject === o; // true

(注意:我假设您在上面的 ES5 环境中,因为您使用的是bind。)

【讨论】:

  • 感谢您的回答,这两部分都非常有用(我会使用它们:)。我有一个小问题,如果你不介意的话。我了解var slice = Function.prototype.call.bind(Array.prototype.slice); 的工作原理(经过几分钟的思考)。但是你为什么不能只使用 var slice = Array.prototype.slice.call; 呢?它会成功的,不是吗?还是我错了?...
  • 另外,如果您能抽出时间回复another question of mine,我将不胜感激。谢谢!
  • var slice = Array.prototype.slice.call; 等同于var slice = Function.prototype.call... 即它存储了call 函数,所以它不起作用。但是,你确实明白了。我们想要存储一个执行slice.call 的函数。 call.bind(slice) 就是这样做的方法。干杯:)
  • 我明白了。所以我可以使用Array.prototype.slice.call(arguments),但是当我将此函数存储到一个变量时,它会忘记谁应该使用call。实际上,与我最初的问题相同的误解。理解“功能就是价值”不止一层。 JavaScript 远非简单……确实是披着羊皮的狼。
  • 只要修改 .bind 来捕获原来绑定的对象,你也可以捕获原来绑定的函数..那么你也可以支持rebind,解决了别人问的一些相关场景关于。
【解决方案2】:

javascript 中的函数在技术上并不绑定到任何东西。它可以声明为对象的属性,如下所示:

var obj = {
    fn: function() {}
}
obj.fn();

但是,如果您将函数本身放入它自己的变量中,则它不会绑定到任何特定对象。

例如,如果您这样做:

var obj = {
    fn: function() {}
}
var func = obj.fn;
func();

然后,当你调用func(),它又执行fn() 函数时,它在调用时将与obj 完全没有关联。将对象与函数关联是由函数的调用者完成的。它不是函数的属性。

如果要使用fn.bind(obj),则会创建一个新函数,该函数只是在内部执行对obj.fn() 的调用。它不会神奇地为 javascript 添加任何新功能。实际上,您可以查看 .bind() 的 polyfill 以了解它是如何工作的 here on MDN


如果您期望 this 无论如何调用函数都始终是一个特定对象,那么 javascript 就不是这样工作的。除非函数实际上是一个 shim,它在被调用时强制与硬线对象关联(.bind() 所做的),否则函数没有硬线关联。关联是由调用者根据调用函数的方式完成的。这与其他一些语言不同。例如,在 C++ 中,如果没有正确的对象在调用时与之关联,就无法调用函数。该语言根本无法解析函数调用,并且会出现编译错误。

如果您在 javascript 中对类型进行分支,那么您可能没有正确使用该语言的面向对象功能或没有充分发挥您的优势。

【讨论】:

    【解决方案3】:

    与其将函数func 绑定到对象,为什么不尝试将func 视为一个对象,它可以将obj1 和obj2 作为其属性?

    例如:

    var func = function {
        this.object; // Could be obj1 or obj2
        return this.f === arguments.callee;
        // => true, if this.object is not null
    }
    
    var f = func;
    
    f.object = obj1; // or func.object = obj2;
    

    你也可以写一个函数来处理对象是obj1还是obj2:

    function g(f) {
      if (typeof(f) !== 'function') throw 'error: f should be function';
    
      if (f.object === obj)
        // this code will run if g(f) was called
        doSomething(f);
      if (f.object === obj2)
        // this code will run if g(f) or g(bound) was called
        doSomethingElse(f);
    }
    

    原因是您想将 obj1 和 obj2 视为函数 f 的属性。但是,当您绑定时,您会将函数添加为 obj1 或 obj2 的属性。可以将函数绑定到多个对象,因此寻找绑定函数的一个对象是没有意义的;因为您将函数添加为对象的子集。

    在这种情况下,由于您希望将对象视为函数的子集,因此在函数中添加一个可以保存 obj1 或 obj2 的属性可能是有意义的。

    【讨论】:

    • var this.object 语法无效,您的意思是this.object = obj1
    • @bfavaretto 哎呀,是的,我做到了。接得好!我已经编辑了我的答案。
    【解决方案4】:

    如果您是执行绑定的人,您可以在函数中添加一个字段来记录 this 以供以后测试。

    var that = "hello";
    var xyz = function () { console.log(this); }.bind(that);
    xyz.that = that;
    
    // xyz is callable as in xyz(), plus you can test xyz.that without calling
    

    【讨论】:

    • 我明白,但是没有...这个函数 f 来自另一个模块,我不知道那个模块用它做了什么... :) 越来越有趣了,真的...只是开玩笑,对不起。
    • 对,所以也许你可以替换系统绑定来为你捕获它(yuk!);)
    • 似乎是唯一的选择,但我不确定它会影响具有不同全局命名空间的模块...
    猜你喜欢
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    相关资源
    最近更新 更多