【问题标题】:Why do I need to use 'this' to access a value in the map?为什么我需要使用“this”来访问地图中的值?
【发布时间】:2013-07-06 02:45:27
【问题描述】:

考虑这段代码:

hello 函数中,我使用this.msg。这工作正常。但是,如果我不使用 this,它会给出一个 msg 未定义的错误。为什么 JavaScript 会混淆,而在许多其他 OO 语言中,this 仅用于使代码对读者显式?

var smallMap = {
    msg: "Hellow ",
    hello: function(name) {
        console.log(this.msg + name);
    }
};

【问题讨论】:

  • 因为smallMap 是一个对象?我个人觉得明确>隐含:)
  • Javascript 并不是真正的 OO,它只是在必要时通过设置 this 来伪装它。
  • @Barmar 这是一个主观论点,这取决于您对 OOP 的理解。
  • 是的。每种 OO 语言在实现访问实例变量的方式上都略有不同。 JS 要求你通过this 来做。

标签: javascript oop


【解决方案1】:

“为什么 JavaScript 会被混淆,”

不是。它有一组特定的规则来管理对对象属性的访问如何工作。只是这些规则与其他所有 OO 语言都不相同。

“在许多其他 OO 语言中,this 仅用于使代码对读者明确”

在某些 OO 语言中,例如 Java,this有时只是为了使代码对读者明确,但实际上它并不是完全可选的 - 它是必需的 区分实例变量(成员)和其他同名变量(例如,方法中的局部变量)。

JavaScript 是面向对象的,但它没有类,也没有像 Java 等其他 OO 语言那样的成员方法。在 JavaScript 中,函数是一种对象,一般而言,任何变量或对象属性都可以设置为引用任何函数 - 因此,即使您在对象字面量中定义函数,例如函数不是 owned 由有问题的对象。一个例子:

function test1() {
    alert('test1');
}
var obj1 = {
    prop1 : 'obj1',
    method1 : test1,
    method2 : function() {
       alert(this.prop1);
    }
}
var test2 = obj1.method2;

obj1.method1();                 // alerts 'test1'
test1();                        // alerts 'test1'
obj1.method2();                 // alerts 'obj1'
test2();                        // alerts undefined (probably; see note below)
test2.call({prop1 : 'test2'});  // alerts 'test2'
delete obj1.method2;            // remove the property
alert(obj1.method2);            // alerts undefined - property no longer exists
test2.call({prop1 : 'test2'});  // alerts 'test2' - the function still exists

注意obj1.method1 引用了一个定义在字面量之外的函数,这个函数可以直接用test1() 调用。同样,test2 已设置为引用已在文字中定义但可以直接调用的函数 - 即使您实际上删除了 method2 属性,您仍然可以直接调用 test2()

我定义了method2 / test2 以便它使用this.prop1this 的值取决于函数的调用方式。如果你用“点”表示法来调用它,比如obj1.method2(),那么在函数this 中将是点之前的对象。如果您像test2() 一样直接调用该函数,那么在非严格模式下this 将是window,但在严格模式下可能是未定义的或其他一些值 - 有关更多详细信息,请参阅MDN。或者,您可以使用.call().apply() 调用函数,并将this 显式设置为其他对象,如上例所示。

【讨论】:

    【解决方案2】:

    因为this 在 Javascript 中是特殊的,并不总是适用于同一个对象。例如我可以这样做:

    var smallMap = {
        msg: "Hellow ",
        hello: function(name) {
            console.log(this.msg + name);
        }
    };
    
    var smallMap2 = {
        msg: "Hellow2 ",
        hello: function(name) {
            console.log(this.msg + name);
        }
    };
    //here i set `this` to refer to `smallMap2` within smallMap
    smallMap.hello.call( smallMap2, "the name" );
    //will print "Hellow2 the name" instead of "Hellow the name"
    

    this 指的是函数被调用的对象,而不一定是函数是其属性的对象。有关详细信息,请参阅此内容:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

    还可以查看callapplybind,了解与此相关的更多信息。

    【讨论】:

    • +1 用于提供实际代码示例并演示为什么 OP 的解释 无法 起作用
    【解决方案3】:

    您创建的是一个对象(或至少就 JS 而言)。当您想访问对象自身的属性时,您需要使用this 关键字。

    JS 中的对象只是变量和函数的集合。当您想在其中使用对象自己的变量时,您需要使用this。您已通过在对象定义中使用{} 或花括号来创建此对象。

    在对象范围之外,您需要使用 <objectName>.var_or_func

    【讨论】:

    • 请注意,在对象的范围内,如果以将this 设置为相关对象的方式调用相关函数,则只能使用this 访问其属性。跨度>
    【解决方案4】:

    你编码的不是一个类,而是一个对象。

    javascript 中的 this 一词与范围有关,与此代码和平所在的对象无关。

    你可以通过这样做得到你想要的东西:

    var smallMap = (function(){
        var msg = "Hellow ";
    
        return {
            hello : function(name) {
                console.log(msg + name);
            }
        }   
    
    })();
    

    也就是说你也不能使用this.msg。试试看,你会看到的。

    【讨论】:

      【解决方案5】:

      由于 Javascript 不是真正面向对象的(它是原型的,意思是对象可以直接从其他对象继承),所以从作用域的角度来看,事情与 OO 的工作方式不同:

      让我们以你的例子为例,并用它来继承另一个对象的“hello”函数:

      var new_obj;
      new_obj.hello = smallMap.hello;
      

      现在,另一个对象具有相同的功能。唉,什么时候会在new_obj上调用这个函数,例如:

      new_obj.hello("test");
      

      除非我们定义new_obj.msg,否则我们将得到undefinedtest

      所以在这里我们应该小心:我们真正想要什么?使用new_obj.msg 或始终使用smallMap.msg。如果答案是后者,我们应该在函数 hello 中使用 smallMap.msg 而不是 this.msg。当然是 TMHO...

      【讨论】:

      • “Javascript 不是真的面向对象” - 是的。经典继承并不是 OO 的唯一有效形式。
      • @nnnnnn 我会回复,但其他人已经做得比我好,解释了为什么封装特性在 JS 中存在问题:stackoverflow.com/a/108773/1057429
      • 是的,我以前读过那个答案。请注意,它开始声明自己“只是一个意见”,提到了一些问题,然后得出结论“我的投票仍然是 Javascript 是面向对象的”,所以这不支持 my 的观点?那里讨论的主要问题是关于 JS 不是真正的 OO 的想法,因为它无法同时提供封装和继承,但即使我们假设 JS 是真的,你必须拥有的想法OO 的继承本身是不正确的(当然,有很多理由认为它是可取的)。
      • @nnnnnn 现在它变成了一个哲学论点 :) 好吧,你是对的 - 我想这是一个见仁见智的问题。顺便说一句,我不会说 Javascript 的原型继承不被视为 OO 继承,即使它与类继承的工作方式不同。
      • 我同意你的观点,原型继承仍然是继承,是面向对象的。我指的是您链接到的答案中的观点,基本上说JS可以进行封装或继承,但不能同时进行(尽管我不确定我是否同意)。无论如何,听起来你和我至少在同一章节,如果不在同一页上,所以不需要太深奥。
      【解决方案6】:

      在 Javascript 中,不可能静态确定 this 具有哪些属性。可以删除msg 属性,或者可以将hello 方法附加到新对象或作为普通函数调用。因此,Javascript 必须假设一个非限定标识符是指一个变量而不是一个对象属性,即使该对象碰巧有一个具有正确名称的属性。可以使用 with 语句强制 Javascript 查看对象属性,但由于您无法分辨属性是什么,这通常被认为是一个可怕的想法。

      【讨论】:

        【解决方案7】:

        使用对象字面量 ({ ... }) 与定义类不同。 “hello”函数的作用域与您在外部定义它时的作用域相同,例如smallMap.hello = function() { ... }

        【讨论】:

          【解决方案8】:

          this 的原因是您可以写成arg1.func(arg2, arg3) 并将arg1 称为thisfunc 的定义中,而不是写成func(arg1, arg2, arg3)this 是动态解析的,与其他静态解析的变量不同(这就是为什么没有它就无法访问 msg 的原因)。它的存在是为了方便调用“方法”的点表示法,它是为 Java 开发人员提供舒适的毯子。 (尽管由于 JavaScript 没有函数重载,x.f(y,z) 为函数 f 提供了有用的命名空间。)

          【讨论】:

          • “它是为 Java 开发人员提供舒适的毯子。” - 是吗?根据我的经验,它往往会给 Java 开发人员带来更多问题,因为他们通常认为 this 将与 Java 中的工作方式完全相同。
          • @nnnnnn 实际上,JavaScript 通常看起来与 Java 类似,但实际上并非如此。
          猜你喜欢
          • 2015-03-12
          • 1970-01-01
          • 1970-01-01
          • 2016-06-06
          • 2019-01-03
          • 2011-04-25
          • 1970-01-01
          • 2015-12-22
          • 1970-01-01
          相关资源
          最近更新 更多