Javascript 对象中的上下文丢失

Javascript 对象中的上下文丢失,javascript,ecmascript-6,Javascript,Ecmascript 6,我有以下代码: /// global context function Outer(){ /// Outer context this.print = function(){ console.log("1: "+this) inside(); /// "this" is bound to the global object. Why not bound to the Outer object? function inside()

我有以下代码:

/// global context
function Outer(){
    /// Outer context
    this.print = function(){ 
        console.log("1: "+this) 
        inside(); /// "this" is bound to the global object. Why not bound to the Outer object?
        function inside(){ 
            console.log("2: "+this) 
        } 
   }; 
} 
function print(){
    console.log("3: "+this);
}
var obj = new Outer; 
obj.print(); /// "this" is bound to the Outer object.
print(); /// "this" is bound to the global object.
为什么在方法调用中,
this
有一个全局对象?有人能解释一下吗?

如前所述

在大多数情况下,其值取决于函数的调用方式。它不能在执行期间通过赋值进行设置,每次调用函数时可能会有所不同。ES5引入了bind方法来设置函数this的值,而不管它是如何调用的,ES2015引入了arrow函数,它们提供自己的this绑定(它仍然是封闭词法上下文的this值)

在函数内部,其值取决于函数的调用方式

由于以下代码不处于严格模式,并且由于调用未设置其值,因此将默认为全局对象

因此,在严格模式下,如果这不是由执行上下文定义的,那么它仍然是未定义的

发布的代码不是在严格模式下执行的,因此当调用它时,这个
等于函数中的全局变量(
窗口
),如
print()

如果这不是理想的行为,并且
print
预期将与最初定义它的对象分开调用(例如,当作为回调传递时),可以使用ES6中的箭头将函数绑定到其词法
this

function Outer(){
    this.print = () => { ... }; 
} 
在ES5
bind
self=中,可以使用此
配方:

function Outer(){
    this.print = (function(){ ... }).bind(this);
}

function Outer(){
    var self = this;
    this.print = function(){
      self;
      ...
    };
}
如果不希望使用其他上下文调用函数,则可以使用其他上下文调用该函数:

print.call(this);
或:

它的值由函数的调用方式决定

您的“inside”函数在“print”函数内调用,该函数有一个对象引用,但该对象没有“inside”函数作为属性,或者该对象没有调用“inside”函数

“this”指的是调用它的对象,而不是调用它的范围。
因此,如果全局对象对您来说不明显,则可能意味着您混淆了函数的范围和上下文。 作用域是vars函数在调用期间可以访问的对象。上下文(这)由调用函数的方式决定


现在看看你是如何在“内部”调用函数的,并回答这个问题。它是否被任何物体生动地拥有?这就是为什么你有global,因为它遵循以下两条JS规则

  • 规则1。在Javascript中,新函数=新范围-这是规则
  • 规则2。当函数作为对象的方法调用时,
    设置为调用该方法的对象
代码说明:

  • 对于规则2,
    this.print()
    Outer
    对象的一个特性。因此,
    将引用外部对象
因此console.log 1将打印
1:[对象]

  • 对于规则1,Outer中的函数
    inside()
    不是Outer对象的特征。因此,将创建新的作用域,并将此
    分配给窗口。最后一个函数“print()”也是如此
因此console.log 2将打印
2:[对象窗口]

和console.log 3也将打印
3:[对象窗口]

希望有帮助

运行代码:

参考:


的值取决于对预期函数的调用方式,通常,前导父对象在其中扮演重要角色

前导父对象是什么意思?

sampleObj.getDetails(); // here 'sampleObj' is the leading parent object for 'getDetails()' function
让我们尝试使用以下示例代码段调用函数的一些示例来清除这些内容

function globalFunction(){
    console.log('global this: ', this);
}
var simpleObj = {
  name : 'john' 
};
var complexObj = {
  outer: function(){
    console.log('outer this: ', this);
  }
}
globalFunction(); // will log global object as no leading parent object is available

complexObj.outer(); // will log 'complexObj' object as leading parent object is 'complexObj'

complexObj.outer = complexObj.outer.bind(simpleObj);
complexObj.outer(); // will log 'simpleObj' object as we have set preference as 'simpleObj' for this **

complexObj.outer.call(); // will log global object as we arent setting any preference for 'this' ***

complexObj.outer.call(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ***

complexObj.outer.apply(); // will log global object as we arent setting any preference for 'this' ****

complexObj.outer.apply(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ****
希望它能帮助你

**什么是绑定:

***所谓:


****什么是适用的:

如果您阅读ES6中的新内容,您可以理解它

在arrow函数之前,每个新函数都定义了自己的此值 (如果是构造函数,则为新对象,在严格模式下未定义) 函数调用,如果函数作为 “对象方法”等)

这意味着每个函数都定义了它自己的
这个
,对于一个简单的函数,它将始终是一个全局对象,而不是定义它的函数的上下文。仅用于比较未定义自己的
(箭头函数)的函数:


如果函数是一个方法(
obj.func()
),它将指向
obj
;否则它将指向
窗口
。使用或将使console语句2按预期方式打印。@vox严格模式只会将
窗口
更改为
未定义
,而不会从箭头函数更改为外部对象。还要注意,
print
已经存在,不是一个好的名称选择。例如,在firefox上,虽然是可写的,但我在覆盖它时遇到了问题(可能是一个主题本身)。@vox我说:“为什么”。我没有说:“如何解决”。这是不同的事情。@MaximPro,因为规范上是这么说的。如果您的问题是“为什么ECMAScript规范会这么说”,那么它太宽泛了。@MaximPro解释为什么TC-39在这里做出了一个特定的选择,将顶级函数的
这个
上下文绑定到全局对象?为什么他们后来改变了主意,在严格模式下它是未定义的?会议记录和专家讨论线程中有很多关于这方面的资料,这个问题不适合SO格式。旁注:你认为因为你不理解我写的东西而指责我不称职会让我不太可能投票来结束你的问题吗?你的答案如何解决问题并不是“为什么”问题的答案。你说怎么修理。我没有要求你这么做,答案引用了MDN参考,清楚地解释了“为什么”。我可以对你说同样的话
function globalFunction(){
    console.log('global this: ', this);
}
var simpleObj = {
  name : 'john' 
};
var complexObj = {
  outer: function(){
    console.log('outer this: ', this);
  }
}
globalFunction(); // will log global object as no leading parent object is available

complexObj.outer(); // will log 'complexObj' object as leading parent object is 'complexObj'

complexObj.outer = complexObj.outer.bind(simpleObj);
complexObj.outer(); // will log 'simpleObj' object as we have set preference as 'simpleObj' for this **

complexObj.outer.call(); // will log global object as we arent setting any preference for 'this' ***

complexObj.outer.call(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ***

complexObj.outer.apply(); // will log global object as we arent setting any preference for 'this' ****

complexObj.outer.apply(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ****
/// global context
function Outer(){
    /// Outer context
    this.print = function(){ 
        console.log("1: "+this) 

        let inside = () => { 
            /// "this" is Outer object here
            console.log("2: "+this) 
        } 
        inside(); 
   }; 
}