Extension of function
Default values for function parameters
Basic Usage
Before ES6, it cannot be specified directly and needs to be modified.
function log(x, y) { y = y || 'World' console.log(x, y) } log('Hello', '');
The disadvantage of this writing method is that if y is assigned, but the corresponding Boolean value is false, the assignment does not work. As in the code above, y is a null character and the result is changed to the default value.
To avoid this problem, you usually need to determine whether y is assigned, and if not, then equal to the default value.
if (typeof y === 'undefined') { y = 'World'; }
ES6 allows you to set default values.
function log(x, y = 'World') { console.log(x, y); } log('Hello', '') // Hello
be careful:
- Parameter variables are declared by default and cannot be declared again with let or const.
function foo(x = 5) { let x = 1; // error const x = 2; // error }
- A function cannot have a function with the same name when using the default value of the parameter.
function foo(x, x, y) { // ... } function foo(x, x, y = 1) { // ... } // SyntaxError: Duplicate parameter name not allowed in this context
- The default value of the parameter is not passed, but is recalculated each time, that is, lazy evaluation.
let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x=100 foo() // 101
Used in conjunction with deconstruction assignment defaults
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined 5 foo() // TypeError: Cannot read property 'x' of undefined foo({x: 1}) // 1 5
The above code only uses object deconstruction assignment, so when foo call does not provide parameters, x and y will not be generated, resulting in an error. This can be avoided through the default value of function parameters.
function foo({x, y = 5} = {}) { console.log(x, y); } foo() // undefined 5
The above code specifies that if no parameter is provided, the parameter of function foo defaults to an empty object. (personal ps: from the result (undefined 5), the default is not an empty object, but an object with a default value.)
As an exercise, what is the difference between the following two writing methods?
// Writing method I function m1({x = 0, y = 0} = {}) { return [x, y]; } // Writing method 2 function m2({x, y} = { x: 0, y: 0 }) { return [x, y]; }
m1(); // [0, 0] m2(); // [0, 0] m1({x = 1}); // [1, 0] m2({x = 1}); // [1, undefined] m1({}) // [0, 0]; m2({}) // [undefined, undefined]
Location of parameter defaults
The parameter defining the default value should be a tail parameter. If it is a non tail parameter, set the default value. In fact, this parameter cannot be omitted.
function f(x, y = 5, z) { return [x, y, z]; } f() // [undefined, 5, undefined] f(1) // [1, 5, undefined] f(1, ,2) // report errors f(1, undefined, 2) // [1, 5, 2] f(1, null, 2) // [1, null, 2]
The length attribute of the function
The meaning of the length attribute is: the number of parameters expected to be passed in by the function.
After specifying the default value, the expected number of parameters passed in will not include this parameter. The length property of the function will return the number of parameters without specifying the default value, and the length property will be distorted.
If the parameter with the default value set is not the tail parameter, the length attribute will not be included in the following parameters.
(function (a, b, c = 5) {}).length // 2 (function (a, b = 1, c) {}).length // 1
Similarly, the rest parameter later will not be included in the length attribute.
(function(...args) {}).length // 0
Scope (to be understood)
Once the default value of the parameter is set, the parameter will form a separate scope (context) when the function is declared and initialized. This scope will disappear after initialization. This syntax behavior will not appear when the default value of the parameter is not set.
var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2
let x = 1; function f(y = x) { let x = 2; console.log(y); } f() // 1
var x = 1; function foo(x = x) { // ... } foo() // ReferenceError: Cannot access 'x' before initialization
If the default value of the parameter is a function, the scope of the function also complies with this rule. Look at the example below.
let foo = 'outer'; function bar(func = () => foo) { let foo = 'inner'; console.log(func()); } bar(); // outer
function bar(func = () => foo) { let foo = 'inner'; console.log(func()); } bar() // ReferenceError: foo is not defined
Here is a more complex example.
var x = 1; function foo(x, y = function() { x = 2; }) { var x = 3; y(); console.log(x); } foo() // 3 x // 1
In the above code, the parameters of the function foo form a scope. In this scope, variables X and y are declared. The default value of Y is an anonymous function. The variable x inside this anonymous function points to the first parameter X of the same scope.
The function foo also declares an internal variable x, which is not in the same scope as the first parameter x, so it is not the same variable. Therefore, after y is executed, the values of the internal variable x and the external global variable x do not change.
If the var with var x = 3 is removed, the internal variable X of the function foo points to the first parameter x, which is consistent with the internal X of the anonymous function, so the final output is 2, and the external global variable x is still unaffected.
var x = 1; function foo(x, y = function() { x = 2; }) { x = 3; y(); console.log(x); } foo() // 2 x // 1
application
Using the parameter default value, you can specify that a parameter must not be omitted. If omitted, an error will be thrown.
function throwIfMissing() { throw new Error('Missing parameter'); } function foo(mustBeProvided = throwIfMissing()) { return mustBeProvided; } foo() // Error: Missing parameter
In addition, the default value of the parameter can be set to undefined, indicating that this parameter can be omitted.
function foo(optional = undefined) { ··· }
rest parameter
ES6 introduces the rest parameter (in the form of... Variable name) to obtain the redundant parameters of the function, so there is no need to use the arguments object. The variable matched with the rest parameter is an array, which puts the redundant parameters into the array.
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
// How to write the arguments variable function sortNumbers() { return Array.prototype.slice.call(arguments).sort(); } // Writing method of rest parameter const sortNumbers = (...numbers) => numbers.sort();
The arguments object is not an array, but an array like object. Therefore, in order to use the array method, you must use array prototype. slice. Call first converts it to an array. The rest parameter does not have this problem. It is a real array, and array specific methods can be used.
The rest parameter cannot be followed by other parameters (that is, it can only be the last parameter), otherwise an error will be reported.
// report errors function f(a, ...b, c) { // ... }
The length attribute of the function, excluding the rest parameter.
(function(a) {}).length // 1 (function(...a) {}).length // 0
Strict mode
Starting from ES5, the internal function can be set to strict mode.
ES2016 has made some modifications. As long as the default value, deconstruction assignment and extension operator are used, the strict mode cannot be explicitly set inside the function.
// report errors function doSomething(a, b = a) { 'use strict'; // code } // report errors const doSomething = function ({a, b}) { 'use strict'; // code }; // report errors const doSomething = (...a) => { 'use strict'; // code }; const obj = { // report errors doSomething({a, b}) { 'use strict'; // code } };
The reason for this is that the strict mode inside the function applies to both the function body and the function parameters. However, when a function is executed, the function parameters are executed first, and then the function body is executed. This is unreasonable. Only from the function body can we know whether the parameters are executed in strict mode, but the parameters are executed before the function body.
// report errors function doSomething(value = 070) { 'use strict'; return value; }
In the above code, the default value of the parameter value is the octal number 070, but in strict mode, the prefix 0 cannot be used to represent octal, so an error should be reported. However, in fact, the JavaScript engine will successfully execute value = 070 first, then enter the function body and find that it needs to be executed in strict mode, and then an error will be reported.
Therefore, the standard simply prohibits this usage. As long as the parameter uses the default value, deconstruction assignment, or extension operator, the strict mode cannot be explicitly specified.
Two methods can circumvent this limitation. The first is to set the global strict mode, which is legal.
'use strict'; function doSomething(a, b = a) { // code }
The second is to wrap the function in an immediate execution function without parameters.
const doSomething = (function () { 'use strict'; return function(value = 42) { return value; }; }());
name attribute
The name property of the function returns the function name of the function.
function foo() {} foo.name // "foo"
This attribute has long been widely supported by browsers, but it was not written to the standard until ES6.
It should be noted that ES6 has made some changes to the behavior of this attribute. If an anonymous function is assigned to a variable, the name attribute of ES5 will return an empty string, while the name attribute of ES6 will return the actual function name.
var f = function () {}; // ES5 f.name // "" // ES6 f.name // "f"
If a named function is assigned to a variable, the name properties of ES5 and ES6 return the original name of the named function.
const bar = function baz() {}; // ES5 bar.name // "baz" // ES6 bar.name // "baz"
The Function instance returned by the Function constructor. The value of the name attribute is anonymous.
(new Function).name // "anonymous"
For the function returned by bind, the value of the name attribute will be prefixed with bound.
function foo() {}; foo.bind({}).name // "bound foo" (function(){}).bind({}).name // "bound "
Arrow function
Basic Usage
ES6 allows functions to be defined using the "arrow" (= >).
var f = v => v; // Equivalent to var f = function (v) { return v; }; var f = () => 5; // Equivalent to var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // Equivalent to var sum = function(num1, num2) { return num1 + num2; };
If there is more than one statement in the arrow function code block, curly braces are used and the return statement is used to return.
If the arrow function directly returns an object, parentheses must be added to the object, otherwise an error will be reported.
var sum = (num1, num2) => { return num1 + num2; } // report errors let getTempItem = id => { id: id, name: "Temp" }; // No error reporting let getTempItem = id => ({ id: id, name: "Temp" }); let foo = () => { a: 1 }; foo() // undefined
The last case can run, but it will get wrong results. The engine thinks that braces are code blocks, so it executes a line of statement a: 1. At this point, a can be interpreted as the label of the statement, so the actual execution statement is 1, and then the function ends without a return value.
Use of arrow function:
- Can be used in conjunction with variable deconstruction
const full = ({ first, last }) => first + ' ' + last;
- Make the expression more concise
const isEven = n => n % 2 === 0; const square = n => n * n;
- Simplified callback function
[1,2,3].map(x => x * x);
- Combined with rest parameter
const numbers = (...nums) => nums; numbers(1, 2, 3, 4, 5) // [1,2,3,4,5] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]
Precautions for use (to be remembered)
- The arrow function does not have its own this object (the internal this is this in the upper scope at the time of definition)
- It cannot be used as a constructor, that is, you cannot use the new command on it, otherwise an error will be thrown.
- You cannot use the arguments object, which does not exist in the function body. If you want to use, you can use the rest parameter instead.
- You cannot use the yield command, so the arrow function cannot be used as a Generator function.
function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
In the above code, this points to the object where the function definition takes effect ({id:42}).
function Timer() { this.s1 = 0; this.s2 = 0; // Arrow function setInterval(() => this.s1++, 1000); // Ordinary function setInterval(function () { this.s2++; }, 1000); } var timer = new Timer(); setTimeout(() => console.log('s1: ', timer.s1), 3100); setTimeout(() => console.log('s2: ', timer.s2), 3100); // s1: 3 // s2: 0
In the above code, two timers are set inside the Timer function. The scope of this binding definition of the arrow function (i.e. Timer function), and this of the ordinary function points to the scope of the runtime (i.e. global object). Therefore, after 3100ms, timer.s1 is updated three times, while timer.s2 is not updated in detail once.
The arrow function can actually make this point fixed. Binding this makes it no longer changeable. This feature is very helpful to encapsulate the callback function. The following is an example. The callback function of DOM events is encapsulated in an object.
var handler = { id: '123456', init: function() { document.addEventListener('click', event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log('Handling ' + type + ' for ' + this.id); } };
In the init() method of the above code, the arrow function is used, so this always points to the handler object. If the callback function is a normal function, run this The line dosomething () will report an error because this points to the document object.
The following is the ES5 code generated by Babel to arrow function, which can clearly explain the direction of this.
// ES6 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log('id:', _this.id); }, 100); }
In the above code, the converted ES5 version clearly shows that the arrow function does not have its own this at all, but refers to the outer this.
How many points does this point to in the following code?
function foo() { return () => { return () => { return () => { console.log('id:', this.id); }; }; }; } var f = foo.call({id: 1}); var t1 = f.call({id: 2})()(); // id: 1 var t2 = f().call({id: 3})(); // id: 1 var t3 = f()().call({id: 4}); // id: 1
Answer: there is only one point of this, that is, this of the function foo, because all inner functions are arrow functions without their own this, and all point to this of the outermost foo function. If all the inner functions in this example are written as ordinary functions, the this of each function points to a different object where the runtime is located.
In addition to this, the following three variables do not exist in the arrow function, pointing to the corresponding variables of the outer function: arguments, super and new target.
function foo() { setTimeout(() => { console.log('args:', arguments); }, 100); } foo(2, 4, 6, 8) // args: [2, 4, 6, 8]
In the above code, the variable arguments inside the arrow function is actually the arguments variable of the function foo.
In addition, since the arrow function does not have its own this, of course, you can't use call(), apply(), bind() to change the direction of this.
(function() { return [ (() => this.x).bind({ x: 'inner' })() ]; }).call({ x: 'outer' }); // ['outer']
In the above code, the arrow function does not have its own this, so the bind method is invalid. The internal this points to the external this.
Not applicable
Because the arrow function will change this from "dynamic" to "static", the arrow function should not be used in the following two occasions.
- Defines the method of the object, and the method includes this internally.
const cat = { lives: 9, jumps: () => { this.lives--; } }
In the above code, cat The jumps () method is an arrow function, which is wrong. Call cat When jumps(), if it is an ordinary function, this in the method points to cat; If you write an arrow function like the above to make this point to the global object, you will not get the expected result. This is because the object does not constitute a separate scope, so the scope when the jumps arrow function is defined is the global scope.
Look at another example.
globalThis.s = 21; const obj = { s: 42, m: () => console.log(this.s) }; obj.m() // 21
In the above example, obj M () is defined using the arrow function. The processing method of the JavaScript engine is to first generate the arrow function in the global space and then assign it to obj m. This causes this inside the arrow function to point to the global object, so obj M () outputs 21 in the global space, not 42 inside the object. The code above is actually equivalent to the code below.
globalThis.s = 21; globalThis.m = () => console.log(this.s); const obj = { s: 42, m: globalThis.m }; obj.m() // 21
For the above reasons, it is recommended to use the traditional writing method to define the properties of the object, rather than the arrow function.
2. When the occasion requires dynamic this.
var button = document.getElementById('press'); button.addEventListener('click', () => { this.classList.toggle('on'); });
When the above code is running, clicking the button will report an error, because the listening function of button is an arrow function, resulting in this being the global object. If you change it to a normal function, this will dynamically point to the clicked button object.
In addition, if the function body is very complex, there are many lines, or there are a large number of read and write operations inside the function, it is not simply to calculate the value. At this time, the arrow function should not be used, but the ordinary function should be used, which can improve the readability of the code.
Nested arrow functions (to be understood)
Arrow functions can also be used internally.
The following is a multi nested function of ES5 syntax.
function insert(value) { return {into: function (array) { return {after: function (afterValue) { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }}; }}; } insert(2).into([1, 3]).after(1); //[1, 2, 3]
Write the above function with an arrow function instead.
let insert = (value) => ({into: (array) => ({after: (afterValue) => { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }})}); insert(2).into([1, 3]).after(1); //[1, 2, 3]
The following is an example of deploying a pipeline mechanism, that is, the output of the previous function is the input of the latter function.
const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val); const plus1 = a => a + 1; const mult2 = a => a * 2; const addThenMult = pipeline(plus1, mult2); addThenMult(5) // 12
If you think the above writing method is poor in readability, you can also use the following writing method.
const plus1 = a => a + 1; const mult2 = a => a * 2; mult2(plus1(5)) // 12
Here's the rewrite λ Examples of calculus.
// λ Writing method of calculus fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v))) // Writing method of ES6 var fix = f => (x => f(v => x(x)(v))) (x => f(v => x(x)(v)));
The above two ways of writing are almost one-to-one correspondence. because λ Calculus is very important for computer science, which enables us to use ES6 as an alternative tool to explore computer science.
Tail call optimization
What is a tail call?
Tail call: the last step of a function is to call another function.
function f(x){ return g(x); }
None of the following three cases are tail calls.
// Situation 1 function f(x){ let y = g(x); return y; } // Assignment operation after call // Situation II function f(x){ return g(x) + 1; } // Calculation operation after call // Situation III function f(x){ g(x); } // Equivalent to returning undefined
Tail call optimization
The reason why the tail call is different from other calls is its special call location.
Function calls will form a "call record" in memory, Also known as "call frame", it saves the call location, internal variables and other information. If function B is called inside function a, a call frame of B will be formed above the call frame of A. when B finishes running and returns the result to a, the call frame of B will disappear. If B also calls C, there will be a call frame of C. and so on, all call frames will form one call stack.
Since the tail call is the last operation of the function, it is not necessary to retain the call frame of the outer function, because the call location, internal variables and other information will not be used again. As long as the call frame of the inner function is directly used to replace the call frame of the outer function.
function f() { let m = 1; let n = 2; return g(m + n); } f(); // Equivalent to function f() { return g(3); } f(); // Equivalent to g(3);
In the above code, if function g is not the last call, function f needs to save the values of m and n, the call location of G and other information. However, because f ends after G is called, the call frame of f(x) can be deleted and only the call frame of g(3) can be retained.
This is called "tail call optimization", that is, only the call frame of the inner function is reserved. If all functions are tail calls, it can be achieved that there is only one call frame each time, which will greatly save memory.
Note that only when the internal variables of the outer function are no longer used, the call frame of the inner function will replace the call frame of the outer function, otherwise "tail call optimization" cannot be carried out.
Note that at present, only Safari supports tail call optimization, which is not supported by Chrome and Firefox.
Tail recursion
Tail recursion: tail calls itself.
Recursion is very memory intensive and prone to "stack overflow" errors. But for tail recursion, because there is only one call frame, the "stack overflow" error will never occur.
function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120
The above code is a factorial function, which needs to save up to n call records, with a complexity of O(n).
If tail recursion is used, only one call record is retained, with a complexity of O(1).
function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120
Another famous example is the calculation of Fibonacci sequence, which can also fully illustrate the importance of tail recursive optimization.
The Fibonacci sequence of non tail recursion is implemented as follows.
function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10) // 89 Fibonacci(100) // overtime Fibonacci(500) // overtime
The tail recursively optimized Dibonacci sequence is implemented as follows:
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity
It can be seen that "tail call optimization" is of great significance to recursive operations, so some functional programming languages have written it into the language specification. The same is true for ES6. It is clearly stipulated for the first time that all ECMAScript implementations must deploy "tail call optimization". That is to say, as long as tail recursion is used in ES6, stack overflow (timeout) will not occur and memory will be saved.
Rewriting of recursive functions
Rewrite method: rewrite all internal variables used into function parameters. For example, the factorial example above has the disadvantage that it is not intuitive. At first glance, it is difficult to see why it is necessary to pass in two parameters 5 and 1 to calculate the factorial of 5.
Two methods can solve this problem.
- In addition to the recursive function, a normal form function is provided.
function tailFactorial(n, total) { if (n === 1) return total; return tailFactorial(n - 1, n * total); } function factorial(n) { return tailFactorial(n, 1); } factorial(5) // 120
The above code calls the tail recursive function tailFactorial through a factorial function of normal form, which looks much more normal.
Or use coriolism to convert multi parameter functions into single parameter forms.
function currying(fn, n) { return function (m) { return fn.call(this, m, n); }; } function tailFactorial(n, total) { if (n === 1) return total; return tailFactorial(n - 1, n * total); } const factorial = currying(tailFactorial, 1); factorial(5) // 120
- Use the function default value of ES6.
function factorial(n, total = 1) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5) // 120
Conclusion: the essence of recursion is a loop operation. Pure functional programming languages have no loop operation commands, and all loops are implemented by recursion, which is why tail recursion is extremely important to these languages. For other languages that support tail call optimization (such as Lua and ES6), you only need to know that loops can be replaced by recursion. Once recursion is used, tail recursion is best used.
Strict mode
ES6 tail call optimization is only enabled in strict mode, and normal mode is invalid.
This is because in normal mode, there are two variables inside the function, which can track the call stack of the function.
- func.arguments: returns the arguments of the function at the time of the call.
- func.caller: returns the function that calls the current function.
- When tail call optimization occurs, the call stack of the function will be overwritten, so the above two variables will be distorted. Strict mode disables these two variables, so the tail call mode takes effect only in strict mode.
function restricted() { 'use strict'; restricted.caller; // report errors restricted.arguments; // report errors } restricted();
Implementation of tail recursive optimization (to be understood)
Tail recursive optimization only works in strict mode, so there are ways to use tail recursive optimization in normal mode or in an environment that does not support this function.
Principle: tail recursion needs to be optimized because there are too many call stacks, resulting in overflow. As long as the call stack is reduced, there will be no overflow. How to reduce? Use "loop" to replace "recursion".
The following is a normal recursive function.
function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else { return x; } } sum(1, 100000) // Uncaught RangeError: Maximum call stack size exceeded(...)
In the above code, sum is a recursive function, parameter x is the value to be accumulated, and parameter y controls the number of recursions. Once 100000 times of recursion, an error will be reported, indicating that the maximum number of calls to the stack has been exceeded.
trampoline function can turn recursive execution into circular execution.
function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; }
The above is an implementation of the trampoline function, which accepts a function f as a parameter. As long as f returns a function after execution, and then executes the function instead of calling the function in the function, recursive execution is avoided, and the problem of too large call stack is eliminated.
Then you have to rewrite the original recursive function to return one function at each step.
function sum(x, y) { if (y > 0) { return sum.bind(null, x + 1, y - 1); } else { return x; } }
In the above code, each execution of sum will return another version of itself.
Now if you use trampoline function to execute sum, there will be no stack overflow.
trampoline(sum(1, 100000)) // 100001
Trampoline function is not a real tail recursive optimization. The following implementation is.
function tco(f) { var value; var active = false; var accumulated = []; return function accumulator() { accumulated.push(arguments); if (!active) { active = true; while (accumulated.length) { value = f.apply(this, accumulated.shift()); } active = false; return value; } }; } var sum = tco(function(x, y) { if (y > 0) { return sum(x + 1, y - 1) } else { return x } }); sum(1, 100000) // 100001
In the above code, tco function is the implementation of tail recursive optimization, and its secret lies in the state variable active. By default, this variable is inactive. Once you enter the tail recursive optimization process, this variable is activated. Then, each round of recursive sum returns undefined, so recursive execution is avoided; The calculated array stores the parameters of each round of sum execution and always has values, which ensures that the while loop inside the accumulator function will always be executed. In this way, it is very clever to change "recursion" into "loop", and the parameters of the next round will replace the parameters of the previous round, ensuring that there is only one layer of the call stack.
Trailing comma of function argument
ES2017 The last argument of a function is allowed to have a trailing comma.
Previously, commas were not allowed after the last parameter during function definition and call.
function clownsEverywhere( param1, param2 ) { /* ... */ }
In the above code, if you add a comma after param2, an error will be reported.
However, if you want to add parameters or call order in the future, it is necessary to add a comma after the original last parameter. For the version management system, it will show that the line where the comma is added has also changed, which seems a little redundant. Therefore, the new syntax allows you to define and call directly with a comma at the end.
function clownsEverywhere( param1, param2, ) { /* ... */ }
Function.prototype.toString()
ES2019 The toString() method of the function instance has been modified.
The toString method returns the function code itself. Previously, comments and spaces were omitted. As like as two peas, the modified toString() method explicitly requires the return of the same original code.
function /* foo comment */ foo () {} foo.toString() // function foo() {} //After modification foo.toString() // "function /* foo comment */ foo () {}"
The parameters of the catch command are omitted
JavaScript try The catch structure previously explicitly required that cathc must be followed by parameters to accept the error object thrown by the try code block. (even if this parameter may not be used)
ES2019 Allow catch statements to omit parameters.
try { // ... } catch (err) { // Processing error } //ES2019 try { // ... } catch { // ... }