How to check that ES6 “variable” is constant?

2020-04-04 02:02发布

Does anyone know some tricks how to do it? I tried to use try-catch:

"use strict";

const a = 20;

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

But unfortunately it works only in "strict" mode. Without "use strict" it perform all statements silently, without modification of a. Also I cannot wrap this code into some handy function (isConstant(someConst) for example) as any argument I'll pass to that function will be a new variable. So anyone know how to create isConstant() function?

4条回答
狗以群分
2楼-- · 2020-04-04 02:18

Just check if your reassignment actually did something:

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);

You need the try/catch because reassign Could throw an exception (like in Firefox), but when it doesn't (like in Chrome), you just check whether your "this always changes the value" reassignment actually did anything.

A simple test:

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

And if you declare the consts in different context, pass that context in to force resolution on the correct object.

downside: this won't work on vars declared outside of object scopes. upside: it makes absolutely no sense to declare them anywhere else. For instance, declaring them in function scope makes the const keyword mostly useless:

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

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

test(); // -> 5

Even though a is constant inside test(), if you pass it to anything else, it's passed as a regular mutable value because it's now just "a thing" in the arguments list.

In addition, the only reason to have a const is because it does not change. As such, constantly recreating it because you're calling a function that makes use of it more than once, means your const should live outside the function instead, so again, we're forced to put the variable in an object scope.

查看更多
SAY GOODBYE
3楼-- · 2020-04-04 02:21

The question refers to incompliant behaviour in earlier ES6 implementations, notably V8 (Node.js 4 and legacy Chrome versions). The problem doesn't exist in modern ES6 implementations, both in strict and sloppy modes. const reassignment should always result in TypeError, it can be caught with try..catch.

There can't be isConstant function because const variable cannot be identified as such by its value.

It's preferable to run a script in strict mode and thus avoid problems that are specific to sloppy mode.

Even if a variable was defined in sloppy mode, it's possible to enable strict mode in nested function scope:

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

(() => {
  'use strict';

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

It's beneficial to use UPPERCASE_CONSTANT convention which is used in JavaScript and other languages. It allows to unambiguously identify a variable as a constant without aid from IDE and avoid most problems with accidental reassignments.

查看更多
欢心
4楼-- · 2020-04-04 02:34

Based on some of the answers here I wrote this code snippet (for client side JS) that will tell you how a "variable" called varName was last declared - I hope it's useful.

Use the following to find out if x was last declared as const:

// 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} = 0`)
      console.log("... last made with let")
    } catch (error) {
      console.log("... last made with const")
    }
  }
} catch (error) {
  console.log("... not found. Undeclared.")
}

Interestingly, declaring without var, let or const, i.e x = 0, results in var getting used by default. Also, function arguments are re-declared in the function scope using var (they are passed by value, not reference).

查看更多
Root(大扎)
5楼-- · 2020-04-04 02:41

I don't think there is, but I also don't think this is a big issue. I think it might be useful to have the ability to know if a variable is const, and this exists in some other languages, but in reality since you (or someone on a team) will be defining these variables, you'd know the scope and the type of the variables. In other words, no you can't, but it's also not an issue.

The only case where it might be useful is if you could change the mutable property during runtime, and if changing this property had actual performance benefits; let, const, and var are treated roughly equally to the compiler, the only difference is that the compiler keeps track of const and will check assignments before it even compiles.

Another thing to note is that just like let, const is scoped to the current scope, so if you have something like this:

'use strict';

const a = 12;

// another scope
{
  const a = 13;
}

it's valid. Just be careful that it will look up in higher scopes if you don't explicitly state const a = 13 in that new scope, and it will give a Read Only or Assignment error:

'use strict';

const a = 12;

{
  a = 13; // will result in error
}
查看更多
登录 后发表回答