【问题标题】:What is a receiver in JavaScript?JavaScript 中的接收器是什么?
【发布时间】:2016-06-01 08:37:36
【问题描述】:

我正在阅读 You Don't Know JS: ES6 & Beyond 并发现一些令人困惑的措辞,例如:

我们声明一个get(..)处理程序作为处理程序对象的命名方法(Proxy(..)的第二个参数),它接收对目标对象(obj)的引用,键属性名称("a") ,以及自己/接收者/代理 (pobj)。

我的问题是,上面的“接收者”是什么意思,它的名字从何而来?

好像我有一个带有成员函数“jump”的对象“a”:

var a = { jump: function() { console.log('jump!'); } };

如果我运行a.jump();,那么“a”就是接收者。

它是这样工作的吗?

对于阅读同一本书的人:当您来到Proxy First, Proxy Last部分时,您可以在代码中添加一行以更清楚地了解“get”陷阱中的上下文:

var handlers = {
    get(target, key, context) {
        console.log(greeter === context); //true, this line added
        return function() {
            context.speak(key + "!");
        };
    }
},
catchall = new Proxy({}, handlers),
greeter = {
    speak(who = "someone") {
        console.log("hello", who);
    }
};

// setup `greeter` to fall back to `catchall`
Object.setPrototypeOf(greeter, catchall);

greeter.speak();                // hello someone
greeter.speak("world");       // hello world

greeter.everyone();             // hello everyone!

如你所见,由于上面“get”陷阱的第三个参数“context”的命名,接收者可以根据词法代码而变化——greeter.everyone();。请参阅下面 Oriol 的非常详细的答案以获得更好的理解。

【问题讨论】:

  • krave 正在谈论这本书:drive.google.com/file/d/0B_u1rzdqYCnzRUlHcV9sQVpTMFE/view 参见顶部的第 182 页。
  • 在真正的书中,它在第 211 页
  • 用正确的目标编辑在线资源。
  • 我认为receiver这个词来自消息传递风格。这只是意味着接收已传递消息的对象。 a 在这种情况下接收消息以处理 jump 方法等。

标签: javascript


【解决方案1】:

接收者是发生属性查找的对象。

所以是的,如果你使用a.jump,a 就是接收者。

只有在发生属性查找时可以执行任意代码时,该概念才有意义。基本上,这意味着:

  • 访问器属性。

    您可以通过在 getter 或 setter 中使用 this 来访问接收器。接收者通常是您在其中定义属性的对象,或从该属性继承的另一个对象。

    var target = {
      get getReceiver() { return this; }
    };
    target.getReceiver; // target
    var inherits = Object.create(target);
    inherits.getReceiver; // inherits
    

    一个内置示例是__proto__,定义为Object.prototype 的一个属性,但期望在其他对象(接收器)上获取或设置。

  • 代理对象

    代理对象允许您定义get 或set 陷阱,当您尝试获取或设置代理的任何属性时,它们会运行一个函数。接收器作为该函数的参数提供。接收者通常是 Proxy 对象本身,或者继承自它的对象。

    var proxy = new Proxy({}, {
      get: function(target, property, receiver) {
        return receiver;
      }
    });
    proxy.getReceiver; // proxy
    var inherits = Object.create(proxy);
    inherits.getReceiver; // inherits
    

请注意,您可以使用Reflect.get 或Reflect.set 指定任意接收者:

Reflect.get(target, "getReceiver", arbitraryValue); // arbitraryValue ¹
Reflect.get(proxy, "getReceiver", arbitraryValue); // arbitraryValue

¹ 如果 getter 是在非严格模式下定义的,它将是 Object(arbitraryValue)。

“接收者”名称来自规范,见Object Internal Methods and Internal Slots

  • [[Get]]   (propertyKey, Receiver) → any

    从该对象返回键为propertyKey的属性的值。如果必须执行任何 ECMAScript 代码来检索 属性值,Receiver 用作 this 值时 评估代码。

  • [[Set]]   (propertyKey, value, Receiver) → Boolean

    将key为propertyKey的属性的值设置为value。如果必须执行任何 ECMAScript 代码来设置属性值, Receiver 在评估代码时用作 this 值。如果设置了属性值,则返回 true;如果设置了属性值,则返回 false 无法设置。

【讨论】:

  • 只是一个小问题,有没有一个真实的日常例子?
  • @NinaScholz 好吧,当您使用obj.prop 时,obj 是接收者。但是除非它是一个代理或者prop 有一个getter,否则你不能对接收者做任何特殊的事情,所以它是不相关的。
  • 将Reflect.get 用作代理上get 的默认值会导致在尝试访问在代理包装的目标原型上定义的属性时出错。代理拦截里面的receiver有什么用?
  • @Oriol 所以如果a = Array(0),那么a 是a.push(0) 中的接收者吗?
【解决方案2】:

我花了一些时间试图弄清楚这个“接收器”在与Reflect.get 一起使用时可以并且将用于什么,也许不出所料,这一切都和MDN explains 一样:

receiver 可选
如果遇到 getter,则为目标调用提供 this 的值。与Proxy一起使用时,可以是继承自target的对象。

据我所知,基本上,它对Reflect.get 的决定性作用是用于访问getter 属性。例如,以下 sn-p 中的 Reflect.get 调用将返回 2,而不是 undefined。

const obj = { get foo() { return this.bar; } };
Reflect.get(obj, "foo", { bar: 2 });

实际的 getter (get foo()...) 以 { bar: 2 } 作为“接收者”来调用。没有Reflect.get 的第三个参数,接收者自然而然地被暗示为对象本身——并且由于它没有bar 属性,所以返回undefined。如果它定义了 bar 属性,事情确实要简单得多:

const obj = { bar: 1, get foo() { return this.bar } };
obj.foo; /// evaluates to 1
Reflect.get(obj, "foo"); /// evaluates to 1, equivalent to above
Reflect.get(obj, "foo", { bar: "soap" }) /// evaluates to, guess what, "soap"

我认为除此之外没有其他任何东西,无论如何,Reflect.get 没有。

【讨论】:

  • 这似乎是对 Oriol 答案的扩展,但是有用例也很棒。不错的答案,+1
  • 我希望它有用。在阅读 cmets 到其他答案之后,我尝试将一个具体示例作为替代解释的一部分。
猜你喜欢
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-21
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
相关资源
最近更新 更多