何时应该使用`this.x`和`var x`?

8

在编写JavaScript类函数时,我经常使用this。但是在使用它的同时,我想知道是否使用var会有所不同。

var MyClass = function() {
  this.x = 4;
  
  return {
    getVal: this.x
  };
}

使用var和不使用var的区别:

var MyClass = function() {
  var x = 4;
  
  return {
    getVal: x
  };
}

这两者有什么区别,我应该在什么情况下使用哪个?

对于class语法,同样适用这个问题:

class MyClass {
  constructor(){
    const x = 4;
  }
}

对比

class MyClass {
  constructor(){
    this.x = 4;
  }
}

我已经编辑了这个问题和最佳答案——可能有点过头了。这个问题需要更新,以便对今天的新开发人员更有用。新手们感到困惑,他们问为什么 this.x = 1; console.log(x); 不起作用,或者为什么 const x = 1; console.log(this.x); 不起作用,或者为什么 class A { constructor(){ const x = 1; } }; console.log((new A()).x); 不起作用等等。其中一部分是属性和变量之间的区别,另一部分是作用域,而这个问题则理想地将两者结合在一起。 - Sebastian Simon
仍然有很多上下文缺失,特别是"this"关键字的工作方式严格模式和非严格模式之间的区别,脚本和模块之间的区别,隐式全局变量等。两个例子:function A(){ this.x = 1; }; const a = A(); console.log(a.x, this.x, window.x, x);在松散模式下由于缺少new关键字而记录为1 1 1 1;在严格模式下失败。this.x = 1; console.log(x);在脚本中记录为1,在模块中失败。 - Sebastian Simon
4个回答

9
使用关键字this的标识符将变成公共属性,而使用var的标识符则会变成私有变量。如今,应该使用const代替var;如果无法对特定变量使用const,则应改用let。访问语义相同。
当使用带有this关键字的标识符时,例如this.x = 4;,您正在为由this引用的对象设置一个具有键"x"和值4的属性。由于this在类的上下文中指向实例,因此这些属性成为该类的实例成员,这意味着它们将在该类的每个新创建的实例中可用。当您使用this时,表示您的意图是在类中使用它,因此需要使用new关键字进行实例化,如下所示。

示例

function Foo() {
  // Variables, scoped to the function. Private access.
  const bar = 'I am bar';
  
  // Properties, set on the instance. Public access
  this.baz = 'I am baz';
  this.secretBar = () => `It’s a secret to everybody: ${bar}.`;
}

const f = new Foo();

console.log(f.bar); // undefined
console.log(f.baz); // "I am baz"
console.log("bar" in f); // false; f does not have the property "bar".
console.log(f.secretBar()); // "It’s a secret to everybody: I am baz.";
  // `secretBar` is in the scope of `Foo`, so it has access to its variables.

在创建JavaScript类函数时,我经常使用this关键字。但是在使用它的过程中,我想知道是否使用var会有所不同。
实际上有很大的区别。除非必要,否则不应该使用this关键字创建不想出现在类实例中的变量。

如果为了论证而做的话,我在示例中添加一个 return { },那么这不就成为函数的公共作用域了吗?而不是所有的 this.variables? - Kristian
@Kristian:是的,无论您想使用return关键字返回什么都仍然可以提供给外部代码。由您决定要返回哪些私有变量或函数。公共变量是公共的,因此不必将它们放在return块中。实际上,您可以搜索“JavaScript揭示模式”,该模式使用相同的方法。顺便说一句,除非您在末尾添加(),否则您的代码将无法正常工作,例如var myClass = function() { /* code */ }(); - Blaster
我正在实例化它。但是,我几乎总是使用揭示模式,这似乎接管了this.something的常规可见性...这就是为什么我以前从未想过问这个问题的原因。 - Kristian
@Kristian:在揭示模式中,一切都使用 var 关键字创建,没有使用 this 关键字,最后你想要向外部代码公开的任何成员,都放在 return 块中。 - Blaster

1

您可以使用myClass.x访问this.x,但是对于变量x的情况就不行了。这涉及到封装。


我只是使用返回对象来封装,这样不就可以封装了吗? - Kristian

1

如果您不需要进行继承,闭包(变量情况)和对象(this情况)基本上执行相同的操作,并且可以大致互换

需要注意的区别:

  • 当您使用“this”时,构造函数需要使用“new”调用,并且如果将它们存储在变量中或作为回调传递,则需要使用.call调用方法。

  • 如果您实例化了大量对象,则可能会存在性能差异(当前引擎可能更善于处理对象-但仅当您将方法放在原型中而不是在构造函数中设置它们时)

  • 使用“var”声明的变量是私有的,无法在函数外部访问。有时这没问题,但这可能会阻止您进行继承(在Javascript中没有“protected”的概念)


“如果您将它们存储在变量上或作为回调传递,则需要使用.call调用方法。”-在2022年,考虑使用箭头函数;此外,在作为回调传递时,需要使用.bind而不是.call。相关:如何在回调中访问正确的this。“但仅当您将方法放入原型中而不是在构造函数中设置它们时才会发生这种情况。”原因是像this.method = function(){};这样设置的方法会在创建新实例时每次都被创建。但这与使用this本身无关。 - Sebastian Simon

1

this.x这样的表达式是指属性,它们就像实例变量一样。这些变量具有公共作用域。它们在面向对象编程中起到类变量的作用。

另一方面,var x;的作用范围有限。这些变量的行为类似于私有实例变量,并且只能在本地作用域内访问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接