可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The following TypeScript:
enum PrimaryColors { Red, Green, Blue };
Produces the following JavaScript:
var PrimaryColors;
(function (PrimaryColors) {
PrimaryColors[PrimaryColors["Red"] = 0] = "Red";
PrimaryColors[PrimaryColors["Green"] = 1] = "Green";
PrimaryColors[PrimaryColors["Blue"] = 2] = "Blue";
})(PrimaryColors || (PrimaryColors = {}));
;
I am embarrassed to admit that I don't understand what the JavaScript is doing.
The function in parentheses is assigning string values using another assignment as the index/key. I have not seen anything like this before.
And what is the purpose of the (PrimaryColors || (PrimaryColors = {}) following the function?
If the answer is to learn JavaScript properly, I will readily accept it, provided it comes with a suggested source that clearly explains what I am seeing here.
回答1:
I believe:
PrimaryColors[PrimaryColors["Red"] = 0] = "Red";
is equivalent to:
PrimaryColors[0] = "Red";
PrimaryColors["Red"] = 0;
See this reference.
The expression x = 7 is an example of the first type. This expression
uses the = operator to assign the value seven to the variable x. The
expression itself evaluates to seven.
For example:
console.log((x = 7));
outputs:
7
Similarly:
var x = {};
console.log((x["hi"] = 7));
Also outputs 7.
As for the second thing, PrimaryColors
is initially undefined.
var x;
console.log(x); // undefined
In a boolean context, undefined
evaluates to false
:
console.log(!undefined); // true
console.log(!!undefined); // false
Sanity check:
console.log((!undefined) === true); // true
console.log((!!undefined) === false); // true
console.log(undefined === false); // false
This is a common usage of short circuiting. Because PrimaryColors
is initially undefined (false), it will pass {}
to the function.
PrimaryColors || (PrimaryColors = {})
回答2:
Maybe this will help.
(function() {})();
This is an 'immediately executing function'. It defines a function as an expression, and then invokes it.
var x = y || y = {};
If a common pattern for initializing something to a default value. If y does not have a value, the 1st part of the or-statement is false, so it executes the 2nd part, which assigns a value to y. The value of that 2nd expression is the new value of y. So x becomes that value of y -- which is the new value if it wasn't already defined.
x[y] = z;
Objects in JS are associative arrays. In other words, string-object pairs, like IDictionary(string,object). This expression is setting the key with value y to the value of z, in the dictionary x;
x[x["a"] = 0] = "a";
So, same thing here, but with a nested expression, which is:
x["a"] = 0;
So that just sets the value of key "a". Nothing fancy. But this is also an expression, whose value is 0. So substitute that in the original expression:
x[0] = "a";
Keys need to be strings, so it's actually the same thing as:
x["0"] = "a";
Which just sets yet another key in the dictionary. Result is that these statements are true:
x["0"] === "a";
x["a"] === 0;
回答3:
I found this question because I was wondering why use an IIFE at all when you can just init the var with {}
right off. The previous answers don’t cover it, but I’ve found my answer in the TypeScript Deep Dive.
The thing is, enums can be split into multiple files. You just have to explicitly initialize the first member of second, third, etc. enums, so this:
enum Colors {
Red,
Green,
Blue
}
enum Colors {
Cyan = 3,
Magenta,
Lime
}
transpiles to this:
var Colors;
(function (Colors) {
Colors[Colors["Red"] = 0] = "Red";
Colors[Colors["Green"] = 1] = "Green";
Colors[Colors["Blue"] = 2] = "Blue";
})(Colors || (Colors = {}));
var Colors;
(function (Colors) {
Colors[Colors["Cyan"] = 3] = "Cyan";
Colors[Colors["Magenta"] = 4] = "Magenta";
Colors[Colors["Lime"] = 5] = "Lime";
})(Colors || (Colors = {}));
As you probably know, redeclaring a variable within the same scope is harmless, but reinitialization is not.
I think they could probably just go:
var Colors;
Colors || (Colors = {});
Colors[Colors["Cyan"] = 3] = "Cyan";
// ...
and skip the closure, but maybe I’m still missing something.
回答4:
It is used to create an associated map (in other words an object) where you will retrieve the 'name' of the enum value by using the index as key and vice versa. In other words: PrimaryColors["Red"]
(or PrimaryColors.Red
using dot notation) will yield 0
. PrimaryColors[0]
(dot notation would be invalid here) will yield "Red"
.
Understanding the implementation is actually not that hard if we consider three concepts:
- The assignment of values to existing variables in javascript evaluates to a value (so it's an expression rather than a statement in spirit)
- Object attributes (keys) can be accessed via brackets given their key
- Object attributes need to be of type string or Symbol but other values will be propagated to a string if possible.
Therefore:
PrimaryColors[PrimaryColors["Red"] = 0] = "Red";
is equivalent to
const valueToBeUsedAsIndex = PrimaryColors.Red = 0; // assignment evaluates to 0, i. e. valueToBeUsedAsIndex has value 0
PrimaryColors[valueToBeUsedAsIndex] = "Red"; // PrimaryColors[0] is "Red". Technically this assignment yields a value too ("Red" in this particular case) but the value is discarded as it's not needed anymore
// at this point PrimaryColors looks like this: { Red: 0, "0": "Red" }