使用此关键字和显示模块模式的严格违规

Posted

技术标签:

【中文标题】使用此关键字和显示模块模式的严格违规【英文标题】:Strict Violation using this keyword and revealing module pattern 【发布时间】:2011-09-12 03:46:27 【问题描述】:

无法让以下内容通过 jslint/jshint

/*jshint strict: true */
var myModule = (function() 
    "use strict";

    var privVar = true,
        pubVar = false;

    function privFn() 
        return this.test; // -> Strict violation.
    

    function pubFn() 
        this.test = 'public'; // -> Strict violation.
        privFn.call(this); // -> Strict violation.
    

    return 
        pubVar: pubVar,
        pubFn: pubFn
    ;

());

myModule.pubFn();

我知道这是由于在函数声明中使用 this 引起的,但我读了 Crockford 写的一些东西,他说违规是为了防止全局变量污染 - 但这里唯一的全局变量是我m 明确定义...myModule。其他所有内容都保存在直接函数范围内,我应该能够使用this 来引用该模块。

有什么想法可以让这种模式通过吗?

更新:如果我使用函数表达式而不是声明,这似乎可行,即

var pubFn = function ()  ...

虽然我不喜欢这种格式,但我更喜欢函数名称和命名参数更接近,并且声明看起来/感觉更清晰。老实说,我不明白为什么会引发违规行为 - 这种模式没有理由这样做。

【问题讨论】:

听起来像是可以忽略 jslint 抱怨的情况之一。顺便说一句,pubVar 在以myModule.pubVar 访问时如何工作?它并没有真正让模型外部的代码访问模块中的变量,对吗?我会认为,如果您想实际获取/设置模块变量的当前值,则需要 getter 和 setter 函数。 你说得对,我觉得这看起来有点滑稽。如果您尝试myModule.pubVar = true,您只需重写对象的属性。内部pubVar 将保持为false 更新:我更喜欢var foo = function() 而不是function foo(),因为它有助于避免托管问题。此外,我喜欢它确实 使函数看起来更像其他变量,因为在 JS 中,函数或任何其他值之间没有区别——它们是一流的。个人喜好,我知道——但我想我会抛出一些想法。 伪题外话:如果你仍然想使用这种我个人不喜欢的模式......你可以使用“crockford”变体,即直接在返回对象中键入公共接口。它甚至更短,更容易维护。无论如何,为私有成员使用闭包是不值得的。 @ikaros45 “无论如何,对私有成员使用闭包是不值得的。”我想知道为什么 【参考方案1】:

这里真正的问题是,如果您从 模块上下文 内(从 IIFE 内)调用 privFnthis 将是 @987654324 @ 在严格模式下; window 如果不是在严格模式下。唉,如果从 IIFE 中调用该函数将会失败。

这是因为从 IIFE 中调用函数时没有 所有者(对象),而 返回的模块对象是函数在调用时的所有者来自 IIFE 上下文之外,例如this === myModule 调用 myModule.pubFn() 时。

严格模式和 JSHint/JSLint 都在努力帮助你,你永远不应该忽略它们产生的错误/警告,而要弄清楚它们为什么警告你。

如果您 100% 确定 privFnpubFn 等不会在模块之外的任何地方被调用,只需在任何生成警告的函数中添加注释 /*jshint validthis: true */。或者,IIFE 中的一条注释将阻止 JSHint 在模块内的任何函数上生成此错误。


众多可能的解决方案之一

存储this 的范围(在此示例中为self)以显式引用模块。这将显示并确保您的意图。

/*jshint strict: true */
var myModule = (function() 
    "use strict";

    var privVar = true,
        pubVar = false,
        self = this;

    function privFn() 
        return self.test;
    

    function pubFn() 
        self.test = 'public';
        //privFn.call(this); // Will have no effect, as `privFn` does not reference `this`
        privFn();
    

    return 
        pubVar: pubVar,
        pubFn: pubFn
    ;
());

myModule.pubFn();

【讨论】:

完全同意你的回答只是想再补充一件事。如果您在以大写字母开头的函数中使用“this”,它不会给您任何警告。因为函数以大写字母开头意味着您将使用它作为带有 new 关键字的构造函数。即使在严格模式下,您也允许在构造函数中使用它:) 我对在不同地方使用的数组映射(回调,上下文)回调有同样的问题。在这种情况下,“this”具有完全有效的用法,因为它由 map() 的第二个参数提供。顺便说一下,其他数组方法也是如此,例如 some()、every()、reduce()、reduceRight()。我认为 JSHint 和 JSLint 可以检查此类函数是否始终用作具有提供上下文的数组方法回调【参考方案2】:

JSHint 有一个名为 validthis 的 option,它:

[...] 当代码在严格模式下运行并且您在非构造函数 [...] 中使用 this 时,当您确定使用 @987654324 时,会抑制有关可能的严格违规的警告@ 在严格模式下有效。

在 JSHint 抱怨的函数中使用它,在你的情况下,它看起来像这样:

function privFn() 
    /*jshint validthis: true */
    return this.test; // -> No Strict violation!


function pubFn() 
    /*jshint validthis: true */
    this.test = 'public'; // -> No Strict violation!
    privFn.call(this); // -> No Strict violation!

在每个适用的函数中指定它似乎很痛苦,但是如果您在模块函数的顶部设置选项,您可能会隐藏 genuine 严格模式违规自己。

【讨论】:

【参考方案3】:

不幸的是,这是此设置的预期错误,因为 jslint/jshint 不知道在全局上下文中声明的函数将在以后用作对象方法。

【讨论】:

但是这些函数没有在全局代码中声明。它们嵌套在您的函数表达式(用于包装模块代码)中。

以上是关于使用此关键字和显示模块模式的严格违规的主要内容,如果未能解决你的问题,请参考以下文章

es6笔记二

es6 一些小知识

JSHint “可能的严格违规。”使用“绑定”时

如何正确解决这个 JSHint “可能的严格违规”

深入理解ES6之《用模块封装代码》

深入理解ES6之《用模块封装代码》