箭头函数中的 WebSocket 回调未按词法设置“this”

Posted

技术标签:

【中文标题】箭头函数中的 WebSocket 回调未按词法设置“this”【英文标题】:WebSocket callback within arrow function not setting 'this' lexically 【发布时间】:2016-05-07 04:42:35 【问题描述】:

我正在尝试在 Angular 2 中创建一个 WebSocket 服务。这是我目前所拥有的:

import Injectable from "angular2/core"
@Injectable()
export class ServerService 

    public ws:WebSocket;

    public myData = ;

    constructor() 
        let ws = new WebSocket('ws://localhost:8080/');

        ws.onopen = (event:Event) => 
            console.log("Socket has been opened!");
        ;

        ws.onmessage = (event:Event) => 
            this.myData = JSON.parse(event.data);
        ;
    ;

问题是,当onmessage 运行时,this 关键字变成了WebSocket 对象,而不是我的ServerService 对象。

似乎我的箭头函数正在充当标准函数。这可能是什么原因造成的?还有其他方法可以返回ServerService 对象吗?

【问题讨论】:

您的代码没问题,this 将指向 ServerService。 check the console on this plunk 我认为您正在分配 event.data 类型为 WebSocket 的对象可能就是原因,在 onmessage method 中分配值之前尝试使用控制台 你怎么知道this关键字变成了web socket对象? @BingLu 在我调试时这样说。 【参考方案1】:

好的,所以在看到 Abdulrahmans 在 plunk 中工作的示例后,我认为我的环境可能有问题。原来我的 TypeScript 正在编译为 ES5,并且正在输出:

function ServerService() 
                var _this = this;
                this.myData = ;
                var ws = new WebSocket('ws://localhost:8080');
                ws.onopen = function (event) 
                    console.log("Socket has been opened!");
                ;
                ws.onmessage = function (event) 
                    _this.myData = JSON.parse(event.data);
                ;
            

我的箭头函数确实被删除了,但被替换为应该(?)在功能上等效的东西。它在 Firefox 中按预期工作,但在 Chrome(我正在调试的地方)中没有。

我将我的 TypeScript 编译器切换为输出 ES6,现在我的 javascript 中有箭头函数,并且我的代码在两个浏览器中都能正常工作。

【讨论】:

你拯救了我的一天。谢谢【参考方案2】:

我使用Function.prototype.bind() 修复了它,它仅适用于老式匿名函数,而不是箭头函数。

所以而不是:

ws.onmessage = (event:Event) => 
    this.myData = JSON.parse(event.data);
;

我做到了:

ws.onmessage = function(event:Event) 
    this.myData = JSON.parse(event.data);
.bind(this);

【讨论】:

【参考方案3】:

我认为 websocket 在相应实例的上下文中执行了回调(使用callapply 方法)。

解决方法包括将 websocket 事件处理程序包装到一个 observable 中:

initializeWebSocket(url) 
  this.wsObservable = Observable.create((observer) => 
    this.ws = new WebSocket(url);

    this.ws.onopen = (e) => 
      (...)
    ;

    this.ws.onclose = (e) => 
      if (e.wasClean) 
        observer.complete();
       else 
        observer.error(e);
      
    ;

    this.ws.onerror = (e) => 
      observer.error(e);
    

    this.ws.onmessage = (e) => 
      observer.next(JSON.parse(e.data));
    

    return () => 
      this.ws.close();
    ;
  ).share();

这样您就可以使用箭头函数和上下文 this:

this.wsObservable.subscribe(data => 
  this.myData = data;
);

有关详细信息,请参阅本文(“基于事件的支持”部分):

https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html

【讨论】:

以上是关于箭头函数中的 WebSocket 回调未按词法设置“this”的主要内容,如果未能解决你的问题,请参考以下文章

不要在选项 property 或回调上使用箭头函数

深入理解ES6箭头函数中的this

为啥在初始页面加载时多次调用 React Ref 回调(作为箭头函数或内联函数)?

箭头函数

vue 中的this问题

ES6 中的箭头函数中的“this”指的是啥?