【问题标题】:How to check that ES6 "variable" is constant?如何检查 ES6“变量”是否恒定?
【发布时间】:2015-06-09 14:20:42
【问题描述】:

有人知道一些技巧吗?我尝试使用try-catch

"use strict";

const a = 20;

var isConst = false;
try {
   var temp = a; a = a+1; a = temp;
} catch (e) {
   isConst = true;
}

但不幸的是,它只能在“严格”模式下工作。如果没有“使用严格”,它会静默执行所有语句,无需修改 a。此外,我不能将此代码包装到一些方便的函数中(例如isConstant(someConst)),因为我将传递给该函数的任何参数都是一个新变量。那么有人知道如何创建isConstant() 函数吗?

【问题讨论】:

  • 对于我来说 const a=2;a++; 抛出一个 SyntaxError(“对 const a 的无效赋值”),使用 Firefox 37.0,即使没有严格模式,我也没有启用 javascript.options.strict。另一个是TypeError:“重新声明 const a”,如果我这样做 const a=2;delete a;const a=2;const a=2;
  • 有趣的问题。我认为大多数浏览器都无法确定——至少according to Mozilla's compatibility chart——但我很好奇是否有办法检测到它。
  • 你用什么浏览器?因为就 Firefox 37.0 而言,尝试重新定义常量并捕获语法错误的 try-catch 可能会起作用。
  • @Xufox Chrome Canary。
  • 这是什么原因?

标签: javascript ecmascript-6


【解决方案1】:

我不认为有,但我也不认为这是一个大问题。我认为能够知道变量是否为const 可能很有用,并且这存在于其他一些语言中,但实际上由于您(或团队中的某个人)将定义这些变量,您会知道变量的范围和类型。换句话说,不,你不能,但这也不是问题。

它可能有用的唯一情况是您可以在运行时更改mutable 属性,并且更改此属性是否具有实际的性能优势; letconstvar 与编译器大致相同,唯一的区别是编译器会跟踪 const 并在编译之前检查分配。

另外需要注意的是,就像letconst 的作用域是当前作用域,所以如果你有这样的东西:

'use strict';

const a = 12;

// another scope
{
  const a = 13;
}

它是有效的。请注意,如果您未在该新范围内明确声明 const a = 13,它将在更高范围内查找,它会给出 Read OnlyAssignment 错误:

'use strict';

const a = 12;

{
  a = 13; // will result in error
}

【讨论】:

  • 好答案。但不幸的是,如果您在“非严格”模式下运行脚本,v8 不会引发错误。无情 - 谢谢。
  • 如果你的代码中没有use strict;,通常像jshint 这样的linters 会抱怨,所以我认为这有点没有实际意义。在任何类型的生产环境中包含use strict;的代码非常罕见。
  • 好吧,我同意你的看法。给我点赞。但实际上这个问题更多的是学术性的,而不是一个真正的问题。我只是偶尔来这个问题如何正确检查“变量”是一个常数。我什至不知道它有用的真实用例:)
【解决方案2】:

根据此处的一些答案,我编写了这段代码 sn-p(用于客户端 JS),它将告诉您最后一次声明“变量”的方式——我希望它有用。

使用以下内容找出 x 上次声明的内容(取消注释 x 的声明以对其进行测试):

// x = 0
// var x = 0
// let x = 0
// const x = 0

const varName = "x"    
console.log(`Declaration of ${varName} was...`)
try {
  eval(`${varName}`)
  try {
    eval(`var ${varName}`);
    console.log("... last made with var")
  } catch (error) {
    try {
      eval(`${varName} = ${varName}`)
      console.log("... last made with let")
    } catch (error) {
      console.log("... last made with const")
    }
  }
} catch (error) {
  console.log("... not found. Undeclared.")
}

有趣的是,如果不声明 varletconst,即 x = 0,会导致默认使用 var。此外,函数参数在函数范围内使用var 重新声明。

【讨论】:

  • 您可以使用eval(`${varName} = ${varName}`); 来保留变量的值,同时仍然检查是否允许赋值
  • @MatthewJensen 好多了,谢谢! (已更正上面的代码。)
【解决方案3】:

只需检查您的重新分配是否真的做了什么:

var isConst = function(name, context) {
  // does this thing even exist in context?
  context = context || this;
  if(typeof context[name] === "undefined") return false;
  // if it does exist, a reassignment should fail, either
  // because of a throw, or because reassignment does nothing.
  try {
    var _a = context[name];
    context[name] = !context[name];
    if (context[name] === _a) return true;
    // make sure to restore after testing!
    context[name] = _a;
  } catch(e) { return true; }
  return false;
}.bind(this);

您需要 try/catch,因为重新分配 可能 会引发异常(例如在 Firefox 中),但是当它不抛出异常时(例如在 Chrome 中),您只需检查您的“这总是会改变value" 重新分配实际上做了任何事情。

一个简单的测试:

const a = 4;
var b = "lol";
isConst('a'); // -> true
isConst('b'); // -> false

如果您在不同的上下文中声明 const,则传递该上下文以强制解析正确的对象。

缺点:这不适用于在对象范围之外声明的变量。好处:在其他任何地方声明它们绝对没有意义。例如,在函数范围内声明它们会使const 关键字几乎无用:

function add(a) {
  return ++a;
}

function test() {
  const a = 4;
  console.log(add(a));
}

test(); // -> 5

尽管 a 在 test() 中是常量,但如果将它传递给其他任何东西,它也会作为常规可变值传递,因为它现在只是 arguments 列表中的“一个东西”。

此外,拥有const 的唯一原因是因为它不会改变。因此,不断地重新创建它,因为您正在调用一个不止一次使用它的函数,这意味着您的 const 应该位于函数之外,因此,我们不得不再次将变量放在对象范围内。

【讨论】:

  • 注意 context 变量,您可以在其中传递您希望变量存在的上下文。
  • 嗯,第一次运行就不行了:(function() { const e = 30; console.log(isConst('e')); })()第二次就可以了。
  • 好点,让我看看我是否可以更新答案以覆盖你。
  • context[name] = !a; 你在这里填充上下文(很可能是全局上下文)
  • 是的,抱歉,这是一个错字。我没有污染,顺便说一句,我错误地重写了。 context[name] 已经存在,因为这正是我们要寻找的。为此更新了代码。
【解决方案4】:

该问题涉及早期 ES6 实现中的不兼容行为,尤其是 V8(Node.js 4 和旧版 Chrome 版本)。这个问题在现代 ES6 实现中不存在,无论是严格模式还是草率模式。 const 重新分配应该总是导致TypeError,它可以被try..catch 捕获。

不能有isConstant 函数,因为const 变量不能通过它的值来识别。

最好在严格模式下运行脚本,从而避免特定于草率模式的问题。

即使变量是在草率模式下定义的,也可以在嵌套函数范围内启用严格模式:

const foo = 1;
// ...
let isConst = false;

(() => {
  'use strict';

  try {
    const oldValue = foo;
    foo = 'new value';
    foo = oldValue;
  } catch (err) {
     isConst = true;
  }
})();

使用 JavaScript 和其他语言中使用的 UPPERCASE_CONSTANT 约定是有益的。它允许在没有 IDE 帮助的情况下明确地将变量标识为常量,并避免大多数意外重新分配的问题。

【讨论】:

    猜你喜欢
    • 2015-08-25
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 2018-09-09
    相关资源
    最近更新 更多