A puzzle about this/@ in Javascript/Coffeescript

点点圈 提交于 2019-12-05 10:23:46

So, first off, there's a bug with the CoffeeScript REPL, issue 1444, which I reported after Telemachus brought this to my attention.

But the more interesting issue here (and one that I need to note in my CoffeeScript book) is that this in the outermost scope of a Node.js module isn't global—it's that module's exports. Try this out:

console.log this is exports
console.log do -> this is global

You'll find that both statements evaluate to true when you run that code in a Node module. That's why name and @name evaluate to different things: name by itself will always point to global.name, unless it's in the scope of a var name declaration; but @name will only point to global.name in a function called in the global context (the default). In a Node.js module, outside of any function, it'll point to exports.name.

I don't know why you get different results, but know that the invocation of a function implicitly involves setting this. The inner function "setName()" therefore has its very own this value independent of the value of this in the outer function in which it is defined. Thus, the fact that you set this via that ".call()" invocation has no effect whatsoever on the this value inside the inner "setName()" function, because when "setName()" is called there's no receiver involved.

I don't know much CoffeeScript but a fair bit about JavaScript so I can only attempt to explain this from the point of view of what the compiled code does (or should be doing):

(function(){
  // In here "this" === [global]
  //
  // The purpose of this wrapper pattern is that it causes "this" to be
  // the global object but all var declared variables will still be 
  // scoped by the function.

  var ctx = this;     // let's keep test a ref to current context

  var setName;
  setName = function(name) {
    console.log(this === ctx);   // !! true !!

    // Because in here "this" is the global context/object
    // this is setting [global].name = name
    return this.name = name;
  };

  setName('Lulu');         // It's context will be [global]

  console.log(name);       // (name === [global].name) == true
  console.log(this.name);  // (this.name === [global].name) == true

}).call(this);

What is (or should be) happening is effectively this (assuming browser where global is window):

(function() {
  var setName;
  setName = function(name) {
    return window.name = name;
  };
  setName('Lulu');
  console.log(window.name);   // 'Lulu'
  console.log(window.name);   // 'Lulu'
}).call(this);

So, why doesn't it match up between the engines?

Because the different environments use different means of handing the global object to you and handling scoping. It's hard to say with certainty and every environment may have a separate reason for its behavior. It depends very much on how they evaluate the code (this is assuming none of the engines have bugs).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!