The non-simple parameter is the new parameter syntax in ES6, which includes: 1. Default parameter values, 2. Remaining parameters, 3. Parametric deconstruction. The next step in this article is to ES7 why the use strict directive is forbidden in functions that use non-simple parameters:
function f (foo = "Bar") { // syntaxerror:illegal ' use strict ' directive in function wi Th non-simple parameter list}
The strict mode introduced by ES5 disables some syntax, such as the traditional octal number notation:
// syntaxerror:octal literals is not allowed in strict mode.
The above error principle is: the parser first resolves to the script at the beginning of the "use strict" directive, which indicates that the current entire script is in strict mode, and then the resolution to 00 will be directly error.
In addition to being placed at the beginning of the script, the "use strict" directive can also be placed at the beginning of the function body, indicating that the entire function is in strict mode, like this:
function f () { "use strict" }
One thing to note is that the "use strict" directive is positioned at the beginning of the function body , not the beginning of the entire function, which means that the parser starts at the beginning of the function body in the source code, and encounters the syntax that strict mode disables. It does not know if it should be an error (unless the upper scope is already in strict mode) because it does not know whether the following function body will contain "use strict" directives, such as:
function // parsing to here does not know whether to error, because the next function body may be {}, may also be {"Use strict"}
The syntax structure on the left side of the "use strict" directive may have a function name, a list of arguments, an additional instruction preamble that exists within the function body and to the left of "use strict", and these three structures may contain syntax that violates strict patterns, and in ES5 The syntax includes the following 4 types:
1. The function name or parameter name is a reserved word that is proprietary in strict mode, including implements, interface, let, package, private, protected, public, static, yield, for example:
function let () {
"Use Strict"
}
function F (yield) {
"Use Strict"
}
2. The function name or parameter name is eval or arguments, for example:
function eval () {
"Use Strict"
}
function F (arguments) {
"Use Strict"
}
3. Duplicate the name of the parameter, for example:
function f (foo, foo) { "use Strict"}
4. The instruction preamble to the left of "use strict" contains the traditional octal translation sequence, such as:
function f () { "\00" "Use Strict"}
When the parser encounters these kinds of syntax, if the function's upper scope is already strict mode, then say, direct error, if not?
SpiderMonkey in 2009 to implement strict mode, for the first 3 kinds of syntax error detection method is: The function name and all the parameter name is saved, wait until after the completion of parsing functions, know whether the current function is strict mode, then to check those names, here to quote a year SpiderMonkey the comments in the Checkstrictparameters method used to check the parameter names in the source code:
/* * In strict mode code, all parameter names must is distinct, must not be * Strict mode reserved keywords, and must no t be ' eval ' or ' arguments '. */ Static BOOL *cx, Jstreecontext *tc) {
The last sentence of this note also mentions that the check of the function head needs to be deferred until the function body is parsed.
The 4th kind of syntax error detection, SpiderMonkey is through a tsf_octal_char called the flag bit realization, the relevant source code:
0x1000 /* */
Here is the getter and setter for this flag bit:
void setoctalcharacterescape (booltrue) {setflag (enabled, Tsf_octal_char);} BOOL Const return flags & Tsf_octal_char; }
The following code is said, when parsing to octal escape sequence, if already in strict mode, then direct error, otherwise, do not error, only through the Setoctalcharacterescape method to record the following flag bit:
/**/if0 | | Js7_isdec (c)) { ifthis, NULL, NULL, jsmsg_deprecated_octal)) { goto error; } Setoctalcharacterescape ();}
The last thing to do is to see the "use strict", through the Hasoctalcharacterescape method to check the preamble of the previous instruction has not set that flag, some words, the comments are also written clearly:
if(directive = = Context->runtime->atomstate.usestrictatom) {/** Unfortunately, Directive Prologue members on general could contain * escapes, even while ' use strict ' directi Ves may not. Therefore * We must check whether an octal character escape have been seen in * any previous directives whenever we Encounter a "use strict" * directive, so, the octal escape is properly treated as a * syntax error. An example of the case: * * Function error () * {* ' \145 ';//octal escape * "Use strict" ; Retroactively makes "\145" a syntax error *}*/ if(Tokenstream.hasoctalcharacterescape ()) {Reporterrornumber (NULL, Jsreport_error, jsmsg_deprecated_octal); return false; }
In general, SpiderMonkey in the ES5 of these 4 kinds of "use strict" on the left side of the strict mode error detection is by recording information, delay the error of the way to achieve.
In 2012, SpiderMonkey implemented the default parameter value in ES6, the default parameter value is an expression, the parsing pattern of the expression (whether it is strict mode) should be the same as the current function, so the following code should also error:
// non-strict mode, no error function // Strict mode, error " Use strict "}
Because the function head can write the expression, so the above said ES5 should be reported in the 4 strict mode of the error, the scope is more expanded, more octal number, delete a variable, this to not what, and then remember two kinds of error types just. There is also a special expression that can contain arbitrary statements-the function expression, which causes all strict pattern-specific parsing errors to be handled specially, such as with statements, strict pattern-specific reserved words as identifiers, such as:
function function () { with// syntaxerror:strict mode code could not include a with statement}) { "Use Strict"}
And that function expression can also contain more nested sub-functions of the layer, which can cause these errors in the record function head to become very complex. SpiderMonkey used two ways to solve the problem:
1. Similar to the old way of implementation, according to strict mode rules parsing function head, but do not immediately error, but the error message down, and so on to parse the entire function, know that this function is not strict mode, and then see the use of real error.
2. According to the rules of non-strict mode resolution, if you really encounter the "use strict" directive, the parser back to the beginning of the function, re-follow the strict pattern of the rules to parse again, encountered errors directly error, that is, two times resolution (reparse).
SpiderMonkey first implementation of the first way, the core idea is to use a Queuedstrictmodeerror attribute record in the parsing function header encountered the first strict mode error, if the subsequent resolution to "use strict", the error thrown out:
// A Strict mode error found in this scope or one of the its children. Itis// used if Strictmodestate is UNKNOWN. If The scope turnsout to being// Strict and this are non-null, it is thrown. Compileerror *queuedstrictmodeerror;
Then after half a year, the first in accordance with the 1th way to achieve the person, out of their own regret, said the previous implementation of the way is very complex and fragile, and then the second kind of reparse way to re-implement again, the following is the second way of implementation of the code in a key note, said very clearly:
// If The context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict"// directive, we backup and reparse it as strict.
SpiderMonkey said, and then say V8, if there is no V8 lead, there will not be this article. V8 in 2011 to achieve a strict mode, for the above mentioned in the ES5 4 kinds of error implementation, in general and SpiderMonkey 09 years to achieve similar, is to record the relevant information, delay decision whether to error. However, V8 in 2015 to achieve the default parameter values, but also encountered and SpiderMonkey in 12, the same problem, in V8 feasible method is the two, or delay the error, or realize reparse. However, V8 do not want to do, V8 developers dedicated to do a slides, at the TC39 meeting proposed that the use of the new parameter syntax introduced by ES6 should be prohibited while using "use strict", there are meeting records.
With regard to the implementation of delayed error, V8 people say it is cumbersome to implement and may affect performance. In addition to "more error types than ES5", the V8 point out that the arrow functions in the ES6 also make this implementation difficult:
// when parsing here, do you want to log the error message? // If the complete line of code is just an assignment statement, the error message is white // If the full line of code is an arrow function function() {/**///// followed by = = {"Use strict"}
That is, because the arrow function does not have a function keyword that indicates the starting position of the functions, and causes parsing of any assignment expression and comma expression that is enclosed by parentheses, it should be treated as an argument list of the arrow function, and all the strict pattern errors encountered are recorded, V8 There's a note in the source code. This difficulty of parsing the arrow functions is clearly pointed out:
// When the This function was used to read a formal parameter, we don ' t always // know whether the function is going to be strict or sloppy. Indeedfor// arrow Functions Wedon't always have know that the identifier we is reading// is actually a formal parameter. Therefore Besides the errors, we// must detect because we know we ' re in strict mode, we also reco RDany// error, we might make in the once we know the language mode.
In addition to all of the above-mentioned difficulties caused by the strict mode of error, V8 's people also pointed out another implementation difficulty, that is, the block-level scope function declaration appears in the default parameter value of the situation:
(function f (foo = (function(bar) { { function Bar () {} }} return Bar}) (1)) { "use strict" // })()
ES6 in the introduction of block-level function declarations, in order to ensure backward compatibility, the code block in the non-strict mode of the function will still be promoted to the function scope (appendix B 3.3), which leads to parsing block-level functions, if the current is strict mode, you should put the function in that block-level scope, Otherwise put it in the upper function scope. This information how to record, and the above example is only the simplest case, the actual situation may have any number of bars at different nesting levels, how to delay the determination of their scope, but also a realization of the difficulties.
Overall, for this matter, with reparse way to achieve more than the record information, delay error way to achieve more simple, but V8 do not want to implement reparse, and did not explain why.
In that slides, V8 's people have a page summary:
1. This thing is too complicated to achieve.
2. Impact performance, parser is the bottleneck of engine performance
3. Later TC39 in the development of new norms may also be troubled by this problem, to kill off
4. This will become increasingly rare (class and module default strict mode), this thing is not cost-effective to achieve
So V8 proposed at that meeting that, in ES7, it was forbidden to use the "use strict" while using the new parameter syntax introduced by ES6, that is, the trouble of using the function-level ' use strict ' to reverse the parsing would remain at the ES5 level.
At present, the mainstream engine has been implemented in the ES7 of this change:
V8 last August https://crrev.com/77394fa05a63a539ac4e6858d99cc85ec6867512
Chakracore in January this year https://github.com/Microsoft/ChakraCore/commit/d8bef2e941de27e7d666e0450a14013764565020
JavaScriptCore in July this year https://bugs.webkit.org/show_bug.cgi?id=159790
SpiderMonkey this October (last week) https://bugzilla.mozilla.org/show_bug.cgi?id=1272784
One of the SpiderMonkey, when implementing this change, has deleted the reparse logic of the original implementation: part 2:don ' t reparse functions with ' use strict ' directives. Since Chakracore and JavaScriptCore did not remove additional code (including the test code) when implementing this change, I guess they were like V8 and never implemented the "default parameter value should also follow the strict mode of function" in ES6.
Do those parsers written with JS have implemented this rule of ES6 and how are they implemented? I see Esprima is not implemented, shift Parser implemented (now according to the ES7 rule error), and the original shift Parser implementation, but also from the two implementation of the choice of reparse.
As stated above, when the external scope is already strict mode, the engine does not have to tangle when parsing the function head, is it possible not to enforce the ban?
function f () { // is already in strict mode function/ / parse this line without tangle // There's no need to get an error . } }
Chakracore did realize this "experience optimization", but because the final specification did not so stipulated, and rolled back, the specification is not so stipulated for reasons I feel very simple, is not necessary to complicate things, originally this error is to reduce the complexity of the engine implementation.
All of the complexity in this case is actually caused by the default parameter values, but why the remaining parameters are also implicated:
function f (... rest) { // will also error }
I think the reason is still to reduce complexity, because ES6 's specification already has the concept of simple parameter list (easy parameter list), and there is an abstract method called Issimpleparameterlist (), which has two usage scenes in ES6. The differences are: 1. Suppresses arguments object and formal parameter binding (even if it is non-strict mode) when the function contains a non-simple parameter 2. When a function contains a non-simple parameter, the argument is forbidden with the same name (even if it is non-strict mode). ES7 This change also use this method to judge, it is very convenient, do you want to write an abstract method, such as called Isparameterlistwhichcontainsinitializer (), In other words, the remaining parameters and the destructor parameters that do not contain the default parameter values are excluded from this prohibition, but there is no need to make such trouble, the concept of the norm is less, the rule is unified a little, also convenient memory.
If you want a function that contains a non-trivial parameter to enter strict mode, wrap a function outside of it with no parameters and write "use strict" in that outer function:
(function// outer functions do not take parameters "Use strict" function f (foo = "Bar") { // }})()
Of course, as mentioned earlier, for the future, class and module are the default strict mode, do not need you to write "use strict".
Why can't a function with "non-simple Arguments" contain "use strict" directives