Professional JavaScript for Web Developers 第四版學習筆記 CHAPTER 10: FUNCTIONS
Arrow Functions 350
Function Names 351
Understanding Arguments 353
Arguments in Arrow Functions 355
No Overloading 355
Default Parameter Values 356
Default Parameter Scope and Temporal Dead Zone 358
Spread Arguments and Rest Parameters 359
Spread Arguments 359
Rest Parameter 360
Function Declarations versus Function Expressions 361
Functions as Values 362
Function Internals 364
arguments 364
this 365
caller 366
new.target 367
Function Properties and Methods 367
Function Expressions 370
Recursion 372
Tail Call Optimization 373
Tail Call Optimization Requirements 374
Coding for Tail Call Optimization 375
Closures 376
The this Object 379
Memory Leaks 381
Immediately Invoked Function Expressions 382
Private Variables 384
Static Private Variables 385
The Module Pattern 387
The Module-Augmentation Pattern 388
Summary 389
-------------------------------------------------
ARROW FUNCTIONS
// Both are valid
let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };
// Zero parameters require an empty pair of parentheses
let getRandom = () => { return Math.random(); };
// Multiple parameters require parentheses
let sum = (a, b) => { return a + b; };
// Invalid syntax:
let multiply = a, b => { return a * b; };
----------------
// Both are valid and will return the value
let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;
// Assignment is allowed
let value = {};
let setName = (x) => x.name = "Matt";
setName(value);
console.log(value.name); // "Matt"
// Invalid syntax:
let multiply = (a, b) => return a * b;
Arrow functions, although syntactically succinct, are not suited in several situations. They do not allow the use of arguments, super, or new.target, and cannot be used as a constructor. Additionally, function objects created with the arrow syntax do not have a prototype defined.
-------------------------------------------------
When a function is defined using the arrow notation, the arguments passed to the function cannot be accessed using the arguments keyword; they can only be accessed using their named token in the function definition.
All arguments in ECMAScript are passed by value. It is not possible to pass arguments by reference. If an object is passed as an argument, the value is just a reference to the object.
-------------------------------------------------
let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI'];
let ordinality = 0;
function getNumerals() {
// Increment the ordinality after using it to index into the numerals array
return romanNumerals[ordinality++];
}
function makeKing(name = 'Henry', numerals = getNumerals()) {
return `King ${name} ${numerals}`;
}
console.log(makeKing()); // 'King Henry I'
console.log(makeKing('Louis', 'XVI')); // 'King Louis XVI'
console.log(makeKing()); // 'King Henry II'
console.log(makeKing()); // 'King Henry III'
-------------------------------------------------
Spread Arguments
The arguments object is only one way to consume spread arguments. Spread arguments can be used as named parameters in both standard functions and arrow functions, as well as alongside default arguments.
function getProduct(a, b, c = 1) {
return a * b * c;
}
console.log(getProduct(...[1,2])); // 2
console.log(getProduct(...[1,2,3])); // 6
console.log(getProduct(...[1,2,3,4])); // 6
Rest Parameter
// Error
function getProduct(...values, lastValue) {}
// OK
function ignoreFirst(firstValue, ...values) {
console.log(values);
}
ignoreFirst(); // []
ignoreFirst(1); // []
ignoreFirst(1,2); // [2]
ignoreFirst(1,2,3); // [2, 3]
Spread Arguments 和 Rest Parameter 是一體兩面。
-------------------------------------------------
arguments.callee
the arguments object has a property named callee, which is a pointer to the function that owns the arguments object.
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
-------------------------------------------------
Inside an arrow function, this references the context in which the arrow function expression is defined.
This is especially useful in situations where events or timeouts will invoke a function inside a callback where the invoking object is not the intended object.
function King() {
this.royaltyName = 'Henry';
// 'this' will be the King instance
setTimeout(() => console.log(this.royaltyName), 1000);
}
function Queen() {
this.royaltyName = 'Elizabeth';
// 'this' will be the window object
setTimeout(function() { console.log(this.royaltyName); }, 1000);
}
new King(); // Henry
new Queen(); // undefined
-------------------------------------------------
caller
function outer() {
inner();
}
function inner() {
console.log(inner.caller);
}
outer();
-------------------------------------------------
bind()
window.color = 'red';
var o = {
color: 'blue'
};
function sayColor() {
console.log(this.color);
}
let objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
-------------------------------------------------
window.identity = 'The Window';
let object = {
identity: 'My Object',
getIdentityFunc() {
return function() {
return this.identity;
};
}
};
console.log(object.getIdentityFunc()()); // 'The Window'
Remember that each function automatically gets two special variables as soon as the function is called: this and arguments. An inner function can never access these variables directly from an outer function. It is possible to allow a closure access to a different this object by storing it in another variable that the closure can access, as in this example:
window.identity = 'The Window';
let object = {
identity: 'My Object',
getIdentityFunc() {
let that = this;
return function() {
return that.identity;
};
}
};
console.log(object.getIdentityFunc()()); // 'My Object'
Remember that the closure has a reference to the containing function’s entire activation object.
-------------------------------------------------
singleton
let singleton = function() {
// private variables and functions
let privateVariable = 10;
function privateFunction() {
return false;
}
// privileged/public methods and properties
return {
publicProperty: true,
publicMethod() {
privateVariable++;
return privateFunction();
}
};
}();