函式
函式定義
- 函式語句和表示式兩種方式定義
//函式語句——宣告
function printprops(o) {
for(var p in o)
console.log(p + ": " + o[p] + "\n");
}
//函式表示式
var square = function(x) { return x*x; }
複製程式碼
- 表示式定義的函式,函式的名稱是可選的
- 函式宣告定義的函式可以在定義之前出現的程式碼呼叫
- 以表示式定義的函式在定義之前無法呼叫(變數的宣告提前,但是賦值不會提前)
- 函式宣告語句不能出現在迴圈,條件判斷,或者try/catch/finally以及with語句中
函式呼叫
- 4種方式呼叫:作為函式,作為方法,作為建構函式,通過它們的call()和apply()方法間接呼叫
- ES 3和非嚴格的ES 5,呼叫上下文(this)是全域性物件,嚴格模式下,則是undefined
- 函式表示式作為物件的屬性訪問表示式,此時是一個方法,物件成為函式的呼叫上下文
var calculator = { // An object literal
operand1: 1,
operand2: 1,
add: function() {
// Note the use of the this keyword to refer to this object.
this.result = this.operand1 + this.operand2;
}
};
calculator.add(); // A method invocation to compute 1+1.
calculator.result // => 2
複製程式碼
- 任何函式的方法呼叫會傳入一個隱式的實參——這個實參是物件,方法呼叫的母體。
- 方法鏈——方法的返回值是一個物件,這個物件還可以再呼叫它的方法
- this是關鍵字,非變數,所以this沒有作用域的限制——巢狀的函式不會從呼叫它的函式中繼承this,它的this指向呼叫它的物件
var o = { // An object o.
m: function() { // Method m of the object.
var self = this; // Save the this value in a variable.
console.log(this === o); // Prints "true": this is the object o.
f(); // Now call the helper function f().
function f() { // A nested function f
console.log(this === o); // "false": this is global or undefined
console.log(self === o); // "true": self is the outer this value.
}
}
};
o.m(); // Invoke the method m on the object o.
複製程式碼
- 如果函式或者方法呼叫之前帶有關鍵字new,就構成建構函式呼叫
- 建構函式中的this指向這個新建立的物件
- call和apply可以顯示地指定呼叫所需的this值,即使該函式不是那個物件的方法
函式的實參和形參
- 呼叫函式時傳入的實參比函式宣告時指定的形參個數要少,則剩下的形參設定為undefined,所以需要給省略的引數賦一個預設值
// Append the names of the enumerable properties of object o to the
// array a, and return a. If a is omitted, create and return a new array.
function getPropertyNames(o, /* optional */ a) {
if (a === undefined) a = []; // If undefined, use a new array
for(var property in o) a.push(property);
return a;
}
// This function can be invoked with 1 or 2 arguments:
var a = getPropertyNames(o); // Get o's properties into a new array
getPropertyNames(p,a); // append p's properties to that array
a = a || [];
複製程式碼
- 呼叫函式時傳入的實參個數超過形參個數時,不能直接獲得未命名值的引用——在函式體內,識別符號arguments是指向實參物件的引用,是一個類陣列物件
function f(x, y, z)
{
// First, verify that the right number of arguments was passed
if (arguments.length != 3) {
throw new Error("function f called with " + arguments.length +
"arguments, but it expects 3 arguments.");
}
// Now do the actual function...
}
複製程式碼
- 不定實參函式——接受任意個數的實參
function max(/* ... */) {
var max = Number.NEGATIVE_INFINITY;
// Loop through the arguments, looking for, and remembering, the biggest.
for(var i = 0; i < arguments.length; i++)
if (arguments[i] > max) max = arguments[i];
// Return the biggest
return max;
}
var largest = max(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6); // => 10000
複製程式碼
- 通過實參名字來修改實參值的話,通過arguments[]陣列可以獲取到更改後的值
function f(x) {
console.log(x); // Displays the initial value of the argument
arguments[0] = null; // Changing the array element also changes x!
console.log(x); // Now displays "null"
}
x和arguments[0]指代同一個值
複製程式碼
- ES 5中移除了上述的特殊特性
- ES 5的嚴格模式下,讀寫callee和caller屬性會產生型別錯誤。callee指代當前正在執行的函式。caller指代呼叫當前正在執行的函式的函式。
var factorial = function(x) {
if (x <= 1) return 1;
return x * arguments.callee(x-1);
};
複製程式碼
- 形參過多時,可以將傳入的實參寫入一個單獨的物件
// Copy length elements of the array from to the array to.
// Begin copying with element from_start in the from array
// and copy that element to to_start in the to array.
// It is hard to remember the order of the arguments.
function arraycopy(/* array */ from, /* index */ from_start,
/* array */ to, /* index */ to_start,
/* integer */ length)
{
// code goes here
}
// This version is a little less efficient, but you don't have to
// remember the order of the arguments, and from_start and to_start
// default to 0.
function easycopy(args) {
arraycopy(args.from,
args.from_start || 0, // Note default value provided
args.to,
args.to_start || 0,
args.length);
}
// Here is how you might invoke easycopy():
var a = [1,2,3,4], b = [];
easycopy({from: a, to: b, length: 4});
複製程式碼
- 實參型別做型別檢查
function sum(a) {
if (isArrayLike(a)) {
var total = 0;
for(var i = 0; i < a.length; i++) { // Loop though all elements
var element = a[i];
if (element == null) continue; // Skip null and undefined
if (isFinite(element)) total += element;
else throw new Error("sum(): elements must be finite numbers");
}
return total;
}
else throw new Error("sum(): argument must be array-like");
}
複製程式碼
作為值的函式
- 函式可以賦值給變數
function square(x) {
return x*x;
}
複製程式碼
- 函式除了賦值給變數,還可以賦值給物件的屬性,此時函式就稱為方法
// We define some simple functions here
function add(x,y) { return x + y; }
function subtract(x,y) { return x - y; }
function multiply(x,y) { return x * y; }
function divide(x,y) { return x / y; }
// Here's a function that takes one of the above functions
// as an argument and invokes it on two operands
function operate(operator, operand1, operand2) {
return operator(operand1, operand2);
}
// We could invoke this function like this to compute the value (2+3) + (4*5):
var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));
// For the sake of the example, we implement the simple functions again,
// this time using function literals within an object literal;
var operators = {
add: function(x,y) { return x+y; },
subtract: function(x,y) { return x-y; },
multiply: function(x,y) { return x*y; },
divide: function(x,y) { return x/y; },
pow: Math.pow // Works for predefined functions too
};
// This function takes the name of an operator, looks up that operator
// in the object, and then invokes it on the supplied operands. Note
// the syntax used to invoke the operator function.
function operate2(operation, operand1, operand2) {
if (typeof operators[operation] === "function")
return operators[operation](operand1, operand2);
else throw "unknown operator";
}
// Compute the value ("hello" + " " + "world") like this:
var j = operate2("add", "hello", operate2("add", " ", "world"));
// Using the predefined Math.pow() function:
var k = operate2("pow", 10, 2);
複製程式碼
- 函式可以擁有屬性,使資訊在函式呼叫過程中持久化——不需要放到全域性變數中
// Initialize the counter property of the function object.
// Function declarations are hoisted so we really can
// do this assignment before the function declaration.
uniqueInteger.counter = 0;
// This function returns a different integer each time it is called.
// It uses a property of itself to remember the next value to be returned.
function uniqueInteger() {
return uniqueInteger.counter++; // Increment and return counter property
}
複製程式碼
- 函式使用自身的屬性快取上一次的計算結果
// Compute factorials and cache results as properties of the function itself.
function factorial(n) {
if (isFinite(n) && n>0 && n==Math.round(n)) { // Finite, positive ints only
if (!(n in factorial)) // If no cached result
factorial[n] = n * factorial(n-1); // Compute and cache it
return factorial[n]; // Return the cached result
}
else return NaN; // If input was bad
}
factorial[1] = 1; // Initialize the cache to hold this base case.
複製程式碼
作為名稱空間的函式
- 在函式中宣告的變數在整個函式體內是可見的,在函式外是不可見的。不在任何函式內宣告的變數是全域性變數。
- 通過將程式碼放入一個函式內,然後呼叫該函式,使全域性變數變成函式內的區域性變數。
function mymodule() {
// Module code goes here.
// Any variables used by the module are local to this function
// instead of cluttering up the global namespace.
}
mymodule(); // But don't forget to invoke the function!
(function() { // mymodule function rewritten as an unnamed expression
// Module code goes here.
}()); // end the function literal and invoke it now.
複製程式碼
- 使用圓括號,JavaScript直譯器將其解析為函式定義表示式,否則解析為函式宣告語句
- 特定場景下返回帶補丁的extend()版本——for/in迴圈是否會列舉測試物件的toString屬性
// Define an extend function that copies the properties of its second and
// subsequent arguments onto its first argument.
// We work around an IE bug here: in many versions of IE, the for/in loop
// won't enumerate an enumerable property of o if the prototype of o has
// a nonenumerable property by the same name. This means that properties
// like toString are not handled correctly unless we explicitly check for them.
var extend = (function() { // Assign the return value of this function
// First check for the presence of the bug before patching it.
for(var p in {toString:null}) {
// If we get here, then the for/in loop works correctly and we return
// a simple version of the extend() function
return function extend(o) {
for(var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for(var prop in source) o[prop] = source[prop];
}
return o;
};
}
// If we get here, it means that the for/in loop did not enumerate
// the toString property of the test object. So return a version
// of the extend() function that explicitly tests for the nonenumerable
// properties of Object.prototype.
return function patched_extend(o) {
for(var i = 1; i < arguments.length; i++) {
var source = arguments[i];
// Copy all the enumerable properties
for(var prop in source) o[prop] = source[prop];
// And now check the special-case properties
for(var j = 0; j < protoprops.length; j++) {
prop = protoprops[j];
if (source.hasOwnProperty(prop)) o[prop] = source[prop];
}
}
return o;
};
// This is the list of special-case properties we check for
var protoprops = ["toString", "valueOf", "constructor", "hasOwnProperty",
"isPrototypeOf", "propertyIsEnumerable","toLocaleString"];
}());
複製程式碼
閉包
- JavaScript採用詞法作用域,函式的執行依賴於變數作用域,是在函式定義時決定,而不是呼叫時決定的
- 閉包——函式物件可以通過作用域鏈相互關聯起來,函式體內部的變數都可以儲存在函式作用域內
- 詞法作用域規則——作用域鏈是函式定義的時候建立的
var scope = "global scope"; // A global variable
function checkscope() {
var scope = "local scope"; // A local variable
function f() { return scope; } // Return the value in scope here
return f();
}
checkscope() // => "local scope"
var scope = "global scope"; // A global variable
function checkscope() {
var scope = "local scope"; // A local variable
function f() { return scope; } // Return the value in scope here
return f;
}
checkscope()() // What does this return?
複製程式碼
- 改造uniqueInteger()函式,使區域性變數變成私有狀態
var uniqueInteger = (function() { // Define and invoke
var counter = 0; // Private state of function below
return function() { return counter++; };
}());
複製程式碼
- 類似counter的私有變數可以被多個巢狀函式訪問
function counter() {
var n = 0;
return {
count: function() { return n++; },
reset: function() { n = 0; }
};
}
var c = counter(), d = counter(); // Create two counters
c.count() // => 0
d.count() // => 0: they count independently
c.reset() // reset() and count() methods share state
c.count() // => 0: because we reset c
d.count() // => 1: d was not reset
複製程式碼
- 將閉包合併為存取器方法getter和setter
function counter(n) { // Function argument n is the private variable
return {
// Property getter method returns and increments private counter var.
get count() { return n++; },
// Property setter doesn't allow the value of n to decrease
set count(m) {
if (m >= n) n = m;
else throw Error("count can only be set to a larger value");
}
};
}
var c = counter(1000);
c.count // => 1000
c.count // => 1001
c.count = 2000
c.count // => 2000
c.count = 2000 // => Error!
複製程式碼
- 使用閉包技術共享私有狀態
// This function adds property accessor methods for a property with
// the specified name to the object o. The methods are named get<name>
// and set<name>. If a predicate function is supplied, the setter
// method uses it to test its argument for validity before storing it.
// If the predicate returns false, the setter method throws an exception.
//
// The unusual thing about this function is that the property value
// that is manipulated by the getter and setter methods is not stored in
// the object o. Instead, the value is stored only in a local variable
// in this function. The getter and setter methods are also defined
// locally to this function and therefore have access to this local variable.
// This means that the value is private to the two accessor methods, and it
// cannot be set or modified except through the setter method.
function addPrivateProperty(o, name, predicate) {
var value; // This is the property value
// The getter method simply returns the value.
o["get" + name] = function() { return value; };
// The setter method stores the value or throws an exception if
// the predicate rejects the value.
o["set" + name] = function(v) {
if (predicate && !predicate(v))
throw Error("set" + name + ": invalid value " + v);
else
value = v;
};
}
// The following code demonstrates the addPrivateProperty() method.
var o = {}; // Here is an empty object
// Add property accessor methods getName and setName()
// Ensure that only string values are allowed
addPrivateProperty(o, "Name", function(x) { return typeof x == "string"; });
o.setName("Frank"); // Set the property value
console.log(o.getName()); // Get the property value
o.setName(0); // Try to set a value of the wrong type
複製程式碼
- 閉包使用時,注意將不希望共享的變數共享給其他的閉包
// This function returns a function that always returns v
function constfunc(v) { return function() { return v; }; }
// Create an array of constant functions:
var funcs = [];
for(var i = 0; i < 10; i++) funcs[i] = constfunc(i);
// The function at array element 5 returns the value 5.
funcs[5]() // => 5
// Return an array of functions that return the values 0-9
function constfuncs() {
var funcs = [];
for(var i = 0; i < 10; i++)
funcs[i] = function() { return i; };
return funcs;
}
var funcs = constfuncs();
funcs[5]() // What does this return? -— 10
複製程式碼
- 閉包在外部函式內是無法訪問this的,除非外部函式將this轉存為一個變數
函式屬性、方法和建構函式
- 函式的length代表形參的個數
// This function uses arguments.callee, so it won't work in strict mode.
function check(args) {
var actual = args.length; // The actual number of arguments
var expected = args.callee.length; // The expected number of arguments
if (actual !== expected) // Throw an exception if they differ.
throw Error("Expected " + expected + "args; got " + actual);
}
function f(x, y, z) {
check(arguments); // Check that the actual # of args matches expected #.
return x + y + z; // Now do the rest of the function normally.
}
複製程式碼
- prototype屬性
- call()和apply()
f.call(o, 1, 2);
f.apply(o, [1,2]);
複製程式碼
ES 5嚴格模式中,call和apply的第一個實參都會變為this的值(即使是null和undefined),在ES 3和非嚴格模式中,傳入的null和undefined會被全域性變數代替 4. 動態修改已有方法-monkey-patching
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
複製程式碼
- bind()方法——將函式繫結到某個物件 實現bind方法
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
複製程式碼
- 柯里化
var sum = function(x,y) { return x + y }; // Return the sum of 2 args
// Create a new function like sum, but with the this value bound to null
// and the 1st argument bound to 1. This new function expects just one arg.
var succ = sum.bind(null, 1);
succ(2) // => 3: x is bound to 1, and we pass 2 for the y argument
function f(y,z) { return this.x + y + z }; // Another function that adds
var g = f.bind({x:1}, 2); // Bind this and y
g(3) // => 6: this.x is bound to 1, y is bound to 2 and z is 3
``
7. ES 3的Function.bind方法
```JavaScript
if (!Function.prototype.bind) {
Function.prototype.bind = function(o /*, args */) {
// Save the this and arguments values into variables so we can
// use them in the nested function below.
var self = this, boundArgs = arguments;
// The return value of the bind() method is a function
return function() {
// Build up an argument list, starting with any args passed
// to bind after the first one, and follow those with all args
// passed to this function.
var args = [], i;
for(i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]);
for(i = 0; i < arguments.length; i++) args.push(arguments[i]);
// Now invoke self as a method of o, with those arguments
return self.apply(o, args);
};
};
}
複製程式碼
- toString()方法——返回原始碼,內建函式返回[native code]
- Function()建構函式
var f = new Function("x", "y", "return x*y;");
複製程式碼
- Function()建構函式允許JavaScript在執行時動態地建立並編譯函式
- 每次呼叫Function()建構函式都會解析函式體,並建立新的函式物件
- 它所建立的函式並不是用詞法作用域,總是在頂層函式執行——無法捕獲區域性作用域
var scope = "global";
function constructFunction() {
var scope = "local";
return new Function("return scope"); // Does not capture the local scope!
}
// This line returns "global" because the function returned by the
// Function() constructor does not use the local scope.
constructFunction()(); // => "global
複製程式碼
- 可呼叫的物件——callable object
檢測一個物件是否是真正的函式物件
function isFunction(x) {
return Object.prototype.toString.call(x) === "[object Function]";
}
```
## 函數語言程式設計
1. 使用函式處理陣列——計算平均值和標準差
```JavaScript
// First, define two simple functions
var sum = function(x,y) { return x+y; };
var square = function(x) { return x*x; };
// Then use those functions with Array methods to compute mean and stddev
var data = [1,1,3,5,5];
var mean = data.reduce(sum)/data.length;
var deviations = data.map(function(x) {return x-mean;});
var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1));
複製程式碼
//ES 3 實現自定義的map()和reduce()函式
// Call the function f for each element of array a and return
// an array of the results. Use Array.prototype.map if it is defined.
var map = Array.prototype.map
? function(a, f) { return a.map(f); } // Use map method if it exists
: function(a, f) { // Otherwise, implement our own
var results = [];
for(var i = 0, len = a.length; i < len; i++) {
if (i in a) results[i] = f.call(null, a[i], i, a);
}
return results;
};
// Reduce the array a to a single value using the function f and
// optional initial value. Use Array.prototype.reduce if it is defined.
var reduce = Array.prototype.reduce
? function(a, f, initial) { // If the reduce() method exists.
if (arguments.length > 2)
return a.reduce(f, initial); // If an initial value was passed.
else
return a.reduce(f); // Otherwise, no initial value.
}
: function(a, f, initial) { // This algorithm from the ES5 specification
var i = 0, len = a.length, accumulator;
// Start with the specified initial value, or the first value in a
if (arguments.length > 2) accumulator = initial;
else { // Find the first defined index in the array
if (len == 0) throw TypeError();
while(i < len) {
if (i in a) {
accumulator = a[i++];
break;
}
else i++;
}
if (i == len) throw TypeError();
}
// Now call f for each remaining element in the array
while(i < len) {
if (i in a)
accumulator = f.call(undefined, accumulator, a[i], i, a);
i++;
}
return accumulator;
}
複製程式碼
var data = [1,1,3,5,5];
var sum = function(x,y) { return x+y; };
var square = function(x) { return x*x; };
var mean = reduce(data, sum)/data.length;
var deviations = map(data, function(x) {return x-mean;});
var stddev = Math.sqrt(reduce(map(deviations, square), sum)/(data.length-1));
複製程式碼
- 高階函式——操作函式的函式,接收一個或多個函式作為引數,返回一個新的函式
//not()函式時一個高階函式
function not(f) {
return function() { // Return a new function
var result = f.apply(this, arguments); // that calls f
return !result; // and negates its result.
};
}
var even = function(x) { // A function to determine if a number is even
return x % 2 === 0;
};
var odd = not(even); // A new function that does the opposite
[1,1,3,5,5].every(odd); // => true: every element of the array is odd
複製程式碼
function mapper(f) {
return function(a) { return map(a, f); };
}
var increment = function(x) { return x+1; };
var incrementer = mapper(increment);
incrementer([1,2,3]) // => [2,3,4]
複製程式碼
function compose(f,g) {
return function() {
// We use call for f because we're passing a single value and
// apply for g because we're passing an array of values.
return f.call(this, g.apply(this, arguments));
};
}
var square = function(x) { return x*x; };
var sum = function(x,y) { return x+y; };
var squareofsum = compose(square, sum);
squareofsum(2,3) // => 25
複製程式碼
- 不完全函式——把一次完整的函式呼叫拆成多次函式呼叫,每次傳入的實參都是完整實參的一部分,每次拆分開的函式叫做不完全函式,每次函式呼叫叫做不完全呼叫
//將類陣列物件轉換為真正的陣列
function array(a, n) { return Array.prototype.slice.call(a, n || 0); }
//這個函式的實參傳遞至左側
function partialLeft(f /*, ...*/) {
var args = arguments; // Save the outer arguments array
return function() { // And return this function
var a = array(args, 1); // Start with the outer args from 1 on.
a = a.concat(array(arguments)); // Then add all the inner arguments.
return f.apply(this, a); // Then invoke f on that argument list.
};
}
//這個函式的實參傳遞至右側
function partialRight(f /*, ...*/) {
var args = arguments; // Save the outer arguments array
return function() { // And return this function
var a = array(arguments); // Start with the inner arguments.
a = a.concat(array(args,1)); // Then add the outer args from 1 on.
return f.apply(this, a); // Then invoke f on that argument list.
};
}
//這個函式的實參被用作模板
//實參列表中的undefined值都被填充
function partial(f /*, ... */) {
var args = arguments; // Save the outer arguments array
return function() {
var a = array(args, 1); // Start with an array of outer args
var i=0, j=0;
// Loop through those args, filling in undefined values from inner
for(; i < a.length; i++)
if (a[i] === undefined)
a[i] = arguments[j++];
// Now append any remaining inner arguments
a = a.concat(array(arguments, j))
return f.apply(this, a);
};
}
複製程式碼
var f = function(x,y,z) { return x * (y - z); };
// Notice how these three partial applications differ
partialLeft(f, 2)(3,4) // => -2: Bind first argument: 2 * (3 - 4)
partialRight(f, 2)(3,4) // => 6: Bind last argument: 3 * (4 - 2)
partial(f, undefined, 2)(3,4) // => -6: Bind middle argument: 3 * (2 - 4)
複製程式碼
利用已有函式來定義新的函式:
var increment = partialLeft(sum, 1);
var cuberoot = partialRight(Math.pow, 1/3);
String.prototype.first = partial(String.prototype.charAt, 0);
String.prototype.last = partial(String.prototype.substr, -1, 1)
複製程式碼
當將不完全呼叫和其他高階函式整合在一起
var not = partialLeft(compose, function(x) { return !x; });
var even = function(x) { return x % 2 === 0; };
var odd = not(even);
var isNumber = not(isNaN)
複製程式碼
使用不完全呼叫的組合來重新組織求平均數和標準差的程式碼——函數語言程式設計!!
var data = [1,1,3,5,5]; // Our data
var sum = function(x,y) { return x+y; }; // Two elementary functions
var product = function(x,y) { return x*y; };
var neg = partial(product, -1); // Define some others
var square = partial(Math.pow, undefined, 2);
var sqrt = partial(Math.pow, undefined, .5);
var reciprocal = partial(Math.pow, undefined, -1);
// Now compute the mean and standard deviation. This is all function
// invocations with no operators, and it starts to look like Lisp code!
var mean = product(reduce(data, sum), reciprocal(data.length));
var stddev = sqrt(product(reduce(map(data,
compose(square,
partial(sum, neg(mean)))),
sum),
reciprocal(sum(data.length,-1))));
複製程式碼
-
記憶函式——將上次的計算結果快取起來,在函數語言程式設計中,這種快取技巧叫做“記憶”。
memorize快取資料到區域性變數
// Return a memoized version of f.
// It only works if arguments to f all have distinct string representations.
function memoize(f) {
var cache = {}; // Value cache stored in the closure.
return function() {
// Create a string version of the arguments to use as a cache key.
var key = arguments.length + Array.prototype.join.call(arguments,",");
if (key in cache) return cache[key];
else return cache[key] = f.apply(this, arguments);
};
}
複製程式碼
//歐幾里得演算法-求兩個整數的最大公約數
function gcd(a,b) { // Type checking for a and b has been omitted
var t; // Temporary variable for swapping values
if (a < b) t=b, b=a, a=t; // Ensure that a >= b
while(b != 0) t=b, b = a%b, a=t; // This is Euclid's algorithm for GCD
return a;
}
var gcdmemo = memoize(gcd);
gcdmemo(85, 187) // => 17
// Note that when we write a recursive function that we will be memoizing,
// we typically want to recurse to the memoized version, not the original.
var factorial = memoize(function(n) {
return (n <= 1) ? 1 : n * factorial(n-1);
});
factorial(5) // => 120. Also caches values for 4, 3, 2 and 1.
複製程式碼