为什么在闭包的私有函数内部不能直接访问此属性?

3
在我的回答中,我提到JavaScript闭包的“公共”属性不能在“私有”函数中访问。在A JavaScript VM that interprets code written in JSON中给出了一个例子。
function anobject(){
    var privatefunction = function(){
        //publicfunction(); //wrong; you have no access to it
        console.log(this); //refer to the global object, not the object creating
    };
    this.publicfunction = function(){
        console.log(this); //refer to the object creating
    }
}

我认为这是由于某些向后兼容性问题,privatefunction 必须属于全局对象。因此,公共函数只是一个分配给this属性的匿名函数。这就解释了为什么调用publicfunction会失败,因为它需要先引用this

然而,以下修复仍然无效:

function anobject(){
    var privatefunction = function(){
        //publicfunction(); //wrong; you have no access to it
        console.log(this); //refer to the object creating
    }.bind(this);
    this.publicfunction = function(){
        console.log(this); //refer to the object creating
    }
}

我明确指定privatefunction应该与创建对象绑定,调用publicfunction应该可以工作,但实际上并没有。我必须执行以下操作:

function anobject(){
    var privatefunction = function(){
        this.publicfunction();
        console.log(this); //refer to the object creating
    }.bind(this);
    this.publicfunction = function(){
        console.log(this); //refer to the object creating
    }
}

另一个解决方法(我正在使用的方法)如下:

function anobject(){
    var privatefunction = function(){
        publicfunction();
        console.log(this); //refer to the object creating
    };
    var publicfunction = function(){
        console.log(this); //refer to the object creating
    }
    this.publicfunction = publicfunction;
}

现在进入问题环节。这种行为背后的原因是什么?通过禁止未明确指定访问this属性,它试图避免什么?

更新:问题的主要部分是:当解释器在作用域链中找不到名称时,为什么不应查看this属性?


我认为你对JavaScript中作用域的工作方式有一些误解。你可以在这里看一下:https://dev59.com/cXRB5IYBdhLWcg3wz6ed - Matteo Tassinari
与作用域无关,主要问题是为什么当JavaScript找不到名称时它不查看this对象? - Earth Engine
2个回答

4
这里的问题在于引用 this 是由函数/方法的调用者决定的,例如:
function anobject(){
    // here "this" is the object
    var privatefunction = function(){
        // here "this" is the object **that called privatefunction()**
    };
    this.publicfunction = function(){
        // this method will always be called by the object, so "this" is the object
    }
}

为了实现你想要的,你也可以尝试这个方法:
function anobject(){
    var that = this;
    var privatefunction = function(){
        [do what you like using "that"]
    };
    this.publicfunction = function(){
        [here you can use "this" directly, or "that"]
    }
}

请参见如何使用JavaScript作用域,JavaScript变量的作用域是什么?以及互联网上的相关信息。


我在我的帖子中几乎已经解释过了,请仔细阅读。无论如何,您提供了另一种解决方法。 - Earth Engine
你所说的技术 privatefunctionthis 是调用它的对象。这是不正确的。如果我定义另一个公共函数并让它调用 privatefunction,如果我在全局范围内调用公共函数进行测试,this 仍然是全局对象。 - Earth Engine
正如我在我的回答中所说,当你调用privatefunction()时,即使在对象内部,由于你没有像这样调用它:this.privatefunction()(因为它不是该对象的方法),所以函数内部的this是全局对象。如果我表达不清楚,请告诉我,我会在有时间的时候尽力解释得更好。 - Matteo Tassinari

1
在第二个示例中,publicfunction() 被声明为对象 this 的一个属性
this.publicfunction = function(){
    console.log(this);
}

因此,该函数不能通过其名称publicfunction()直接访问,这与上下文无关。该函数实际上不属于任何上下文,而是属于其作为属性的对象。

在您的示例中,当privatefunction()调用publicfunction()时,会引发错误,因为没有声明为变量名称为publicfunction()的函数,而不是因为this的属性不可访问:

function anobject(){
    var privatefunction = function(){
        //publicfunction(); // You cannot call publicfunction() because there is
                            // no function defined with this name
    }.bind(this);
    this.publicfunction = function(){
        console.log(this);
    }
}

看这个例子,即使在anobject()内部,publicfunction()也是无法访问的:
function anobject(){
    this.publicfunction = function() {
        console.log(this);
    }
    // publicfunction(); // Wrong because publicfunction()
                         // was not defined as a variable
}

然而,如果publicfunction()anobject()的上下文中被定义为一个变量,那么这个函数将可以通过它的名称进行访问。例如,你可以在anobject()的闭包中简单地声明function publicfunction()

function anobject(){
    function publicfunction() {
        console.log(this);
    }
    publicfunction(); // You can access publicfunction() here
}

但在这种情况下,publicfunction()anobject()的执行上下文之外是不可访问的,因此它可以被视为“私有”函数:
var a = new anobject();
if(typeof a.publicfunction === 'undefined')
    console.log('Public function is undefined outside of anobject()');

因此,我们使用this关键字来声明publicfunction(),这使得它在anobject()的上下文之外可以被访问,但这样做只会将该函数定义为anobject()属性
因此,访问该函数的唯一方法是通过调用this.publicfunction()
function anobject(){
    var privatefunction = function(){
        this.publicfunction();
        console.log(this);
    }.bind(this);
    this.publicfunction = function(){
        console.log(this);
   }
}

关于 this 关键字的更多细节:函数内部如何使用 "this" 关键字?

JavaScript 中的变量 vs. 属性:http://javascriptweblog.wordpress.com/2010/08/09/variables-vs-properties-in-javascript/


好的。您的意思是在JavaScript中,没有对象前缀的名称始终指代变量,而不是任何对象的属性吗? - Earth Engine

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