【问题标题】:What is 'this' before an object is instantiated in js?在 js 中实例化对象之前的“this”是什么?
【发布时间】:2009-08-16 06:55:57
【问题描述】:

我不明白以下内容:

var x = function() {
    this.foo="foo";
    return function() {
        this.bar = "bar";
        return foo+bar;
    };
}(); // returns inner

alert(x()); // 'foobar', so both 'this' variables are set
alert(x.bar); // undefined - but wasn't it used correctly?
alert(new x().bar); // ok, works

我的假设是第一次生成并使用默认的“this”范围/变量映射,然后当调用“new”时,会发送一个带有新“this”的新对象(函数?)并返回。或者,也许 x 不是一个合适的对象?但是,“this”最终是如何被设置并用于制作“foobar”的呢?

我需要知道什么才能理解这一点?

【问题讨论】:

  • 阅读所有答案并点击过去讨论的链接。

标签: javascript scope this


【解决方案1】:

首先让我们回顾一下 JavaScript 的一些要点,然后我们可以处理您的示例。

函数的上下文

一个误解是一个上下文。每个函数都在上下文中调用,可以使用关键字this。让我们编写一个可以用来检查上下文的函数:

var probe = function(){
  // if the context doesn't have a name, let's name it
  if(!this.name){
    this.name = "lumberjack";
  }
  // print the name of my context
  console.log(this.name);
};

我们开始吧:

name = "global!";

// when we call a function normally it still have a context:
// the global context
probe(); // prints: global!

var ctx = {name: "ctx"};

// we can set a context explicitly using call()
probe.call(ctx); // prints: ctx

// we can set a context explicitly using apply()
probe.apply(ctx); // prints: ctx

// it is set implicitly, if we call a function as a member
ctx.fun = probe;
ctx.fun(); // prints: ctx

// or we can create a brand new object and set it as a context:
// that's what "new" does
var t = new probe(); // prints: lumberjack

// let's sum it up:
console.log(name);     // prints: global!
console.log(ctx.name); // prints: ctx
console.log(t.name);   // prints: lumberjack

这就是为什么很容易搞砸并在不经意间陷入全局环境的原因。

构造函数中的返回值

很多人在看到构造函数返回值时会感到困惑。这是合法的。构造函数可以返回一个对象、一个函数或一个数组。该值将用作实例。旧实例将被丢弃。

var myClass = function(){
  // if it is called as a constructor, "this" will be a new instance
  // let's fill it up:
  this.a = 42;
  this.b = "Ford";
  this.c = function(){ return "Perfect"; };
  // done? let's discard it completely!
  // and now for something completely different...
  return {
    owner: "Monty Python",
    establishment: "Flying Circus"
  };
};
var t = new myClass();
alert(t.owner + "'s " + t.establishment);

正如预期的那样,它显示了“Monty Python's Flying Circus”。

如果构造函数返回其他内容(例如,数字、字符串、null、未定义),则返回的结果将被丢弃并使用旧实例。

例子

您的示例很难理解,主要是因为它的编写方式。让我们通过重写来简化它。

首先让我们处理x

var x = function() {
  this.foo = "foo";
  return function() {
    this.bar = "bar";
    return foo + bar;
  };
}(); // returns inner

我们可以看到匿名函数(1stfunction)被立即执行,所以我们可以内联它:

// next assignment can be simplified because
// top "this" is window or the global scope
//this.foo = "foo"; =>
foo = "foo";
x = function() {
  this.bar = "bar"; // this line depends on its context, or "this"
  return foo + bar; // this line uses global "foo" and "bar"
};

所以最后我们有两个全局变量:foo(一个字符串)和x(一个函数)。

现在让我们回顾一下 1st 警报:

alert(x()); // 'foobar', so both 'this' variables are set

再次,让我们内联x()

// next assignment can be simplified because
// top "this" is window or the global scope
//this.bar = "bar"; =>
bar = "bar";
// at this moment both global "foo" and "bar" are set
alert(foo + bar); // => "foo" + "bar" => "foobar"

第二个nd警报同样简单:

alert(x.bar); // undefined - but wasn't it used correctly?

它不需要太多的重写。 x 是一个函数,我们没有给它添加任何属性,所以 x.bar 是未定义的。如果你添加它,你可以看到结果:

x.bar = "bar2";
alert(x.bar); // bar2

第 3rd 条警报演示了 JavaScript 的 OOP 实际操作:

alert(new x().bar); // ok, works

(附注:它仅在您先运行 x() 时才有效,否则它会因为 bar 未定义而爆炸)。

让我们这样重写:

var t = new x();
alert(t.bar); // bar

现在我们来分析构造函数。它有两个语句:赋值和返回。后者被忽略,因为它返回一个字符串。所以我们可以这样重写:

x = function(){
  this.bar = "bar";
};
var t = new x();
alert(t.bar); // bar

我希望现在一切看起来都很容易。

【讨论】:

  • 这是一个非常有用的教程(尽管在不知道“this”如何工作的情况下很难遵循第一个内联步骤)。谢谢!
  • 我重新排列了我的原始帖子,使其更符合逻辑。感谢您提出如何改进的建议!
【解决方案2】:

这是 new 运营商的主要抱怨......

该运算符创建一个继承自操作数构造函数原型的新对象,然后调用该函数,将新对象分配给this

如果您在调用构造函数时忘记使用 new 运算符,则会得到正常的函数调用,并且this 绑定到全局对象 (window) 而不是新对象。

每当您的函数使用 this 尝试初始化自己的实例时,它都会附加全局变量。

在您的示例中,全局对象以两个新变量结束:

window.foo
window.bar

因此有些人更喜欢prototypal inheritance 而不是伪经典的方法。

【讨论】:

【解决方案3】:

回答您在标题中提出的问题:this 将引用全局对象。

请记住,this 在 JavaScript 中的行为与在 Java 或 C++ 等语言中的行为不同;它纯粹用于上下文,并且每次调用给定函数时都不一定引用相同类型的对象。 From the MDC documentation:

this 可以通过四种方式传递
[...]
如果没有使用上述方法,则全局对象作为上下文对象传递,例如,当 this 发生在任何构造函数之外的顶层时,或者当调用函数而不作为对象的方法调用时,如在func(arg1, arg2).

有关其他问题的答案,请参阅:Just when I think I finally understand Javascript scope…

【讨论】:

    【解决方案4】:

    正如 Shog9 所说,这与您在 Java 等中看到的正常范围不同。

    AFAIK Javascript 使用动态范围,就像 Common LISP/eLisp 一样,而不是像 Scheme/Lisp-1 那样的词法范围。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-24
      • 2011-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 2019-08-02
      • 1970-01-01
      相关资源
      最近更新 更多