乐趣区

JavaScript专项算法题3闭包

@冒泡的马树

题库原地址:http://csbin.io/closures

闭包、作用域和运行上下文

挑战 1

问题:

构建 createFunction 函数,用于创立和返回函数。当被创立的函数被调用时,它会打印“hello”。

题解:

// CHALLENGE 1
function createFunction() {const innerFunction = () => {console.log("hello");
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const function1 = createFunction();
function1(); // => should console.log('hello');

挑战 2

问题:

构建承受一个输出值作为参数的 createFunctionPrinter 函数,用于创立和返回一个特定函数。当特定函数被调用时,其应该打印特定函数被创立时输出 createFunctionPrinter 中的值。

题解:

// CHALLENGE 2
function createFunctionPrinter(input) {
    const innerValue = input;
  const innerFunction = () => {console.log(innerValue);
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const printSample = createFunctionPrinter('sample');
printSample(); // => should console.log('sample');
const printHello = createFunctionPrinter('hello');
printHello(); // => should console.log('hello');

挑战 3

问题:

察看上面 outer 函数的实现代码。留神其会返回一个函数而且那个函数应用了不在其作用域的变量。尝试推断一下运行 outer 函数失去的输入值,而后构建 addByX 函数,其会返回一个承受一个输出值作为参数并与 x 相加的函数。

代码:

// CHALLENGE 3
function outer() {
  let counter = 0; // this variable is outside incrementCounter's scope
  function incrementCounter () {
    counter ++;
    console.log('counter', counter);
  }
  return incrementCounter;
}

const willCounter = outer();
const jasCounter = outer();

// Uncomment each of these lines one by one.
// Before your do, guess what will be logged from each function call.

/*** Uncomment these to check your work! ***/
willCounter();
willCounter();
willCounter();

jasCounter();
willCounter();


function addByX(x) {
    let backpackValue = x ;
  const innerFunction = (el) => {return backpackValue + el;}
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const addByTwo = addByX(2);
console.log(addByTwo(1)); // => should return 3
console.log(addByTwo(2)); // => should return 4
console.log(addByTwo(3)); // => should return 5

const addByThree = addByX(3);
console.log(addByThree(1)); // => should return 4
console.log(addByThree(2)); // => should return 5

const addByFour = addByX(4);
console.log(addByFour(4)); // => should return 8
console.log(addByFour(5)); // => should return 9

挑战 4

问题:

构建 once 函数,承受参数为一个回调函数并返回一个特定函数。当特定函数被第一次调用时,其会调用回调函数并返回输入值。如果其不是被第一次调用,则特定函数仅仅返回第一次调用时失去的回调函数返回值,而不是再次运行回调函数。

题解:

// CHALLENGE 4
function once(func) {
    let counter = 0;
  let innerValue = 0; 
  const innerFunction = (el) => {
    counter++;
    if(counter == 1) {innerValue = func(el);
      return innerValue;
    } else {return innerValue;}
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const onceFunc = once(addByTwo);
console.log(onceFunc(4));  // => should log 6
console.log(onceFunc(10));  // => should log 6
console.log(onceFunc(9001));  // => should log 6

挑战 5

问题:

构建 after 函数,承受一个数字 n 和一个回调函数作为参数。回调函数须要在通过 after 构建的函数运行第 n 次时才被运行。

题解:

// CHALLENGE 5
function after(count, func) {
    let counter = count;
  const innerFunction = () => {
    counter--;
    if(counter == 0){func();
    }
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const called = function() { console.log('hello') };
const afterCalled = after(3, called);
afterCalled(); // => nothing is printed
afterCalled(); // => nothing is printed
afterCalled(); // => 'hello' is printed

挑战 6

问题:

构建 delay 函数,承受一个回调函数作为第一个参数,一个数值 n(单位为毫秒)作为第二个参数。delay 函数被调用后,须经 n 毫秒后才运行回调函数。任何其余赋给特定函数的参数会在 n 毫秒后被回调函数应用。提醒:钻研 setTimeout();

题解:

// CHALLENGE 6
function delay(func, wait, ...args) {setTimeout(() => func(...args), wait);
}

delay(called, 2000);  // "hello" after 2 seconds.

挑战 7

问题:

构建 rollCall 函数,承受一个由名字组成的数组,返回一个特定函数。第一次调用特定函数时,其会打印数组的第一个名字。第二次调用时,打印第二个名字,始终上来,直到所有名字都被打印。当所有名字都被打印后,其会打印“Everyone accounted for”。

题解:

// CHALLENGE 7
function rollCall(names) {
  const innerArray = new Array;
  for(let i=0; i<names.length; i++){innerArray[i] = names[i];
  }
    const innerFunction = () => {if(innerArray.length == 0){console.log("Everyone accounted for");
    } else {console.log(innerArray.shift());
    }
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const rollCaller = rollCall(['Victoria', 'Juan', 'Ruth'])
rollCaller() // => should log 'Victoria'
rollCaller() // => should log 'Juan'
rollCaller() // => should log 'Ruth'
rollCaller() // => should log 'Everyone accounted for'

挑战 8

问题:

构建 saveOutput 函数,承受一个回调函数(其承受一个参数)和一个字符串(充当明码)作为参数。saveOutput 会返回一个特定函数,其作用与回调函数相似,只是当它的字符串参数与 saveOutput 函数的明码字符串参数雷同时,特定函数会利用先前所有调用特定函数时的输出值作为对象的键,对应回调函数的输入值作为值,构建一个对象,并返回之。

题解:

// CHALLENGE 8
function saveOutput(func, magicWord) {
    const storedMagicWord = magicWord;
  const storedInputArray = new Array;
  const storedOutputArray = new Array;
  const exitObject = new Object;
  let tempOutput = 0;
  const innerFunction = (el) => {if(el !== storedMagicWord) {storedInputArray.push(el);
      tempOutput = func(el);
      storedOutputArray.push(tempOutput);
      return tempOutput;
    } else {if(storedInputArray.length == storedOutputArray.length) {for(let i=0; i<storedInputArray.length; i++){exitObject[storedInputArray[i]]=storedOutputArray[i];
        }
        return exitObject;
      } else{return 'error';}
    }
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const multiplyBy2 = function(num) {return num * 2;};
const multBy2AndLog = saveOutput(multiplyBy2, 'boo');
console.log(multBy2AndLog(2)); // => should log 4
console.log(multBy2AndLog(9)); // => should log 18
console.log(multBy2AndLog('boo')); // => should log {2: 4, 9: 18}

挑战 9

问题:

构建 cycleIterator 函数,承受参数为一个数组并返回一个特定函数。特定函数不承受参数。当第一次调用时,特定函数返回数组的第一个元素。第二次调用时,返回第二个元素,始终上来。当特定函数已返回数组的最初一个元素后,下一次调用此特定函数时其返回数组的第一个元素,而后第二个元素,始终上来。

题解:

// CHALLENGE 9
function cycleIterator(array) {
    let counter = 0;
  const innerFunction = () => {
        counter++;
    if(counter > array.length){
      counter = 0;
      return array[counter];
    } else{return array[counter-1];
    }
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const threeDayWeekend = ['Fri', 'Sat', 'Sun'];
const getDay = cycleIterator(threeDayWeekend);
console.log(getDay()); // => should log 'Fri'
console.log(getDay()); // => should log 'Sat'
console.log(getDay()); // => should log 'Sun'
console.log(getDay()); // => should log 'Fri'

挑战 10

问题:

构建 defineFirstArg 函数,承受参数为一个回调函数和一个数值。回调函数承受至多一个参数。defineFirstArg 会返回一个特定函数。此特定函数会将 defineFirstArg 的数值参数作为回调函数的第一个参数,输出到特定函数的参数作为回调函数的第二个参数,并调用回调函数。

题解:

// CHALLENGE 10
function defineFirstArg(func, arg) {const innerFunction = (el) =>{return func(arg, el);
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const subtract = function(big, small) {return big - small;};
const subFrom20 = defineFirstArg(subtract, 20);
console.log(subFrom20(5)); // => should log 15

挑战 11

问题:

构建 dateStamp 函数,承受参数为一个回调函数并返回一个特定函数。特定函数会承受回调函数所须要的参数并传给回调函数,返回一个对象记录特定函数被调用时的日期信息(date 键)以及回调函数的输入(output 键)。提醒:你可能须要钻研一下如何获取 Date 对象中的信息。

题解:

// CHALLENGE 11
function dateStamp(func) {
    let dateInfo;
  const innerObj = new Object;
  const innerFunction = (el) => {dateInfo = new Date();
    innerObj['date'] = dateInfo.toString();
    innerObj['output'] = func(el);
    return innerObj;
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const stampedMultBy2 = dateStamp(n => n * 2);
console.log(stampedMultBy2(4)); // => should log {date: (today's date), output: 8 }
console.log(stampedMultBy2(6)); // => should log {date: (today's date), output: 12 }

挑战 12

问题:

构建 censor 函数,不承受参数。censor 会返回一个承受两个字符串或单个字符串参数的特定函数。当失去两个字符串参数时,特定函数会存储两个字符串作为一对,以备后续应用。当失去一个字符串参数时,特定函数会返回同样的字符串,只是此字符串中与存储字符串对的第一个字符串雷同的字符串会被替换成存储字符串对中的第二个字符串。

题解:

// CHALLENGE 12
function censor() {
  let parameterInfoObj = new Object;
  let oneParameterArray = new Array;
  let punctuationArray = new Array;
    const innerFunc = (el1, el2) => {if(el1 && el2) {oneParameterArray = [];
      punctuationArray = [];
      parameterInfoObj[el1] = el2;
      return parameterInfoObj;
    }
    else if(el1 && !el2){oneParameterArray = el1.match(/\b(\w+)\b/g);
      punctuationArray = el1.match(/([,.]+)/g);
      for(let i = 0; i<oneParameterArray.length; i++){for(let key in parameterInfoObj){if(key === oneParameterArray[i]){oneParameterArray[i] = parameterInfoObj[key];
          }
        }
      }
        for(let i =0; i<oneParameterArray.length; i++){oneParameterArray[i] += punctuationArray[i]; 
      }
      return oneParameterArray.join("");
    }
  }
  return innerFunc;
}

/*** Uncomment these to check your work! ***/
const changeScene = censor();
changeScene('dogs', 'cats');
changeScene('quick', 'slow');
console.log(changeScene('The quick, brown fox jumps over the lazy dogs.')); // => should log 'The slow, brown fox jumps over the lazy cats.'

挑战 13

问题:

JavaScript 对象中并没有公有属性的定义,然而或者能够自定义一个?构建 createSecretHolder(secret) 函数,承受任何值作为 secret 参数,并且仅返回两个办法,返回 secret 值的 getSecret() 和设置 secret 值的 setSecret()。

题解:

// CHALLENGE 13
function createSecretHolder(secret) {
    let secretInfo = secret;
  const innerObj = new Object;
  innerObj.getSecret = ()=>{return secretInfo;}
  innerObj.setSecret = (el)=>{secretInfo = el;}
  return innerObj;
  
}

/*** Uncomment these to check your work! ***/
const obj = createSecretHolder(5)
console.log(obj.getSecret()) // => returns 5
console.log(obj.setSecret(2)) // => undefined
console.log(obj.getSecret()) // => returns 2

挑战 14

问题:

构建 callTimes 函数,返回一个特定函数。特定函数应返回它被调用的次数信息。

题解:

// CHALLENGE 14
function callTimes() {
    let counter = 0;
  const innerFunction = () => {return ++counter;}
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
let myNewFunc1 = callTimes();
let myNewFunc2 = callTimes();
console.log(myNewFunc1()); // => 1
console.log(myNewFunc1()); // => 2
console.log(myNewFunc2()); // => 1
console.log(myNewFunc2()); // => 2

挑战 15

问题:

构建 russianRoulette 函数,作用为承受一个数值参数(假如为 n)并返回一个特定函数。此特定函数不承受输出参数,而且会在前 n - 1 次调用时返回字符串“click“,在第 n 次调用时则返回字符串”bang“,n 次当前再调用皆返回字符串”reload to play again”。

题解:

// CHALLENGE 15
function russianRoulette(num) {
  let counter = num;
    const innerFunction = () => {
      counter--;
      if(counter>0) {return "click";} else if(counter==0){return "bang";} else{return "reload to play again";}
    }
    return innerFunction; 
}

/*** Uncomment these to check your work! ***/
const play = russianRoulette(3);
console.log(play()); // => should log 'click'
console.log(play()); // => should log 'click'
console.log(play()); // => should log 'bang'
console.log(play()); // => should log 'reload to play again'
console.log(play()); // => should log 'reload to play again'

挑战 16

问题:

构建 average 函数,不承受参数,并返回一个特定函数(承受一个数值作为参数或无参数)。当 average 创立的特定函数被输出数值参数的形式调用时,返回值为所有已经被输出到特定函数的数值参数的平均值(反复数值视为别离的数值)。当特定函数被无参数的形式调用时,返回以后的平均值。如果特定函数在无参数形式调用时未曾被输出数值参数的形式调用过,则返回 0。

题解:

// CHALLENGE 16
function average() {
  let averageValue = 0;
    let argumentCounter = 0;
    let argumentSum = 0;
    const innerFunction = (el) => {if(el == undefined) {return averageValue;} else{
        argumentCounter++;
        argumentSum += el; 
        averageValue = argumentSum / argumentCounter;
        return averageValue;
      }
    }
    return innerFunction;
}

/*** Uncomment these to check your work! ***/
const avgSoFar = average();
console.log(avgSoFar()); // => should log 0
console.log(avgSoFar(4)); // => should log 4
console.log(avgSoFar(8)); // => should log 6
console.log(avgSoFar()); // => should log 6
console.log(avgSoFar(12)); // => should log 8
console.log(avgSoFar()); // => should log 8

挑战 17

问题:

构建 makeFuncTester 函数,承受参数为一个二维数组(其中第二维数组仅含两个元素),返回一个接管回调函数作为参数的特定函数。当二维数组的每一个子数组的第一个元素输出到回调函数时都产生与第二个元素雷同的返回值时,特定函数返回 ture,否则特定函数返回 false。

题解:

// CHALLENGE 17
function makeFuncTester(arrOfTests) {const firstLayerArray = [];
  for(let i = 0; i<arrOfTests.length; i++){let secondLayerArray = [];
    for(let j = 0; j<arrOfTests[i].length; j++){secondLayerArray.push(arrOfTests[i][j]);
    }
    firstLayerArray.push(secondLayerArray);
  }
  const innerFunction = (callback) => {for(let i=0; i<firstLayerArray.length; i++){if(callback(firstLayerArray[i][0]) !== firstLayerArray[i][1]){return false;}
    }
    return true;
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const capLastTestCases = [];
capLastTestCases.push(['hello', 'hellO']);
capLastTestCases.push(['goodbye', 'goodbyE']);
capLastTestCases.push(['howdy', 'howdY']);
const shouldCapitalizeLast = makeFuncTester(capLastTestCases);
const capLastAttempt1 = str => str.toUpperCase();
const capLastAttempt2 = str => str.slice(0, -1) + str.slice(-1).toUpperCase();
console.log(shouldCapitalizeLast(capLastAttempt1)); // => should log false
console.log(shouldCapitalizeLast(capLastAttempt2)); // => should log true

挑战 18

问题:

构建 makeHistory 函数,承受一个数值参数 n(充当限定值)并返回一个特定函数(承受字符串作为参数)。特定函数会存储限定个数为 n 的最近输出到此特定函数中的字符串参数历史(每次调用存储一次)。每次一个字符串被输出到特定函数中,特定函数会返回拼接“done”于此字符串后的新字符串(空格作为距离)。然而,如果输出字符串为“undo”,特定函数会删除字符串参数历史中的最近字符串值,返回拼接“undone”于此最近字符串后的新字符串(空格作为距离)。如果输出字符串“undo”时特定函数中的字符串参数历史已为空,那么特定函数会返回字符串“nothing to undo”。

题解:

// CHALLENGE 18
function makeHistory(limit) {
    const limitNumber = limit; 
  let parameterStack = [];
  const innerFunction = (stringElement) => {if(stringElement === "undo"){if(parameterStack.length == 0) {return "nothing to undo";} else{return `${parameterStack.pop()} undone`;
      }
    } else{parameterStack.push(stringElement);
      if(parameterStack.length > limit){parameterStack = parameterStack.slice(-limit);
      }
      return `${stringElement} done`;
    }
  }
  return innerFunction;
}

/*** Uncomment these to check your work! ***/
const myActions = makeHistory(2);
console.log(myActions('jump')); // => should log 'jump done'
console.log(myActions('undo')); // => should log 'jump undone'
console.log(myActions('walk')); // => should log 'walk done'
console.log(myActions('code')); // => should log 'code done'
console.log(myActions('pose')); // => should log 'pose done'
console.log(myActions('undo')); // => should log 'pose undone'
console.log(myActions('undo')); // => should log 'code undone'
console.log(myActions('undo')); // => should log 'nothing to undo'

挑战 19

问题:

仔细观察测试代码如果你须要帮忙来了解上面的算法形容。

构建 blackjack 函数,承受参数为一个数组(元素皆为从 1 到 11 的数值),返回一个 DEALER 函数。DEALER 函数会承受两个参数(皆为数值),而后返回一个另外的 PLAYER 函数。

在第一次调用 PLAYER 函数时,它会返回输出 DEALER 函数中的两个数值参数之和。

在第二次调用 PLAYER 函数时,它会返回下列两种状况中的一种:

  1. 输出 blackjack 函数的数值型数组的第一个数值加上输出 DEALER 函数中的两个数值参数之和失去的和,如果和小于等于 21,返回此和;
  2. 如果和大于 21,返回字符串“bust”。

如果第二次调用 PLAYER 函数时已返回 ”bust”,则接下来 PLAYER 函数的每次调用皆会返回字符串“you are done!”(不过不同于“bust”,输入”you are done!“时不会应用数值型数组中的数值 )。如果第二次调用 PLAYER 函数时并未返回”bust“,则接下来调用 PLAYER 函数时会返回下列两种状况中的一种:

  1. 最近一次的和值加上数值型数组的下一个数值元素,如果这个求和后果小于等于 21 的话,返回此和;
  2. 返回“bust”如果求和后果大于 21。

再次申明,如果其返回 ”bust”,则接下来 PLAYER 函数的每次调用皆会返回字符串“you are done!”,否则,PLAYER 函数会持续应用最近一次和值与数值型数组的下一个数值元素求和,始终上来。

你能够假如给定的数值型数组有足够多的数值元素从而会在用完数组元素之前失去“bust”。

题解:


// CHALLENGE 19
function blackjack(array) {const dealer = (num1, num2) => {
    let first = true; 
    let bust = false; 
    let sum = num1 + num2; 
    const player = () => {if(first) {
        first = false; 
        return sum;
      }
      if(bust) {return "you are done!";}
      if(sum + array[0] <= 21){sum += array.shift();
        return sum;
      } else {array.shift();
        bust = true;
        return "bust";
      }
    }
    return player;
  }
  return dealer;
}

/*** Uncomment these to check your work! ***/

/*** DEALER ***/
const deal = blackjack([2, 6, 1, 7, 11, 4, 6, 3, 9, 8, 9, 3, 10, 4, 5, 3, 7, 4, 9, 6, 10, 11]);

/*** PLAYER 1 ***/
const i_like_to_live_dangerously = deal(4, 5);
console.log(i_like_to_live_dangerously()); // => should log 9
console.log(i_like_to_live_dangerously()); // => should log 11
console.log(i_like_to_live_dangerously()); // => should log 17
console.log(i_like_to_live_dangerously()); // => should log 18
console.log(i_like_to_live_dangerously()); // => should log 'bust'
console.log(i_like_to_live_dangerously()); // => should log 'you are done!'
console.log(i_like_to_live_dangerously()); // => should log 'you are done!'

/*** BELOW LINES ARE FOR THE BONUS ***/

/*** PLAYER 2 ***/
const i_TOO_like_to_live_dangerously = deal(2, 2);
console.log(i_TOO_like_to_live_dangerously()); // => should log 4
console.log(i_TOO_like_to_live_dangerously()); // => should log 15
console.log(i_TOO_like_to_live_dangerously()); // => should log 19
console.log(i_TOO_like_to_live_dangerously()); // => should log 'bust'
console.log(i_TOO_like_to_live_dangerously()); // => should log 'you are done!
console.log(i_TOO_like_to_live_dangerously()); // => should log 'you are done!

/*** PLAYER 3 ***/
const i_ALSO_like_to_live_dangerously = deal(3, 7);
console.log(i_ALSO_like_to_live_dangerously()); // => should log 10
console.log(i_ALSO_like_to_live_dangerously()); // => should log 13
console.log(i_ALSO_like_to_live_dangerously()); // => should log 'bust'
console.log(i_ALSO_like_to_live_dangerously()); // => should log 'you are done!
console.log(i_ALSO_like_to_live_dangerously()); // => should log 'you are done!'

退出移动版