@冒泡的马树

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

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

挑战1

问题:

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

题解:

// CHALLENGE 1function 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 2function 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 3function 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 3console.log(addByTwo(2)); // => should return 4console.log(addByTwo(3)); // => should return 5const addByThree = addByX(3);console.log(addByThree(1)); // => should return 4console.log(addByThree(2)); // => should return 5const addByFour = addByX(4);console.log(addByFour(4)); // => should return 8console.log(addByFour(5)); // => should return 9

挑战4

问题:

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

题解:

// CHALLENGE 4function 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 6console.log(onceFunc(10));  // => should log 6console.log(onceFunc(9001));  // => should log 6

挑战5

问题:

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

题解:

// CHALLENGE 5function 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 printedafterCalled(); // => nothing is printedafterCalled(); // => 'hello' is printed

挑战6

问题:

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

题解:

// CHALLENGE 6function delay(func, wait, ...args) {  setTimeout(() => func(...args), wait);}delay(called, 2000);  // "hello" after 2 seconds.

挑战7

问题:

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

题解:

// CHALLENGE 7function 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 8function 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 4console.log(multBy2AndLog(9)); // => should log 18console.log(multBy2AndLog('boo')); // => should log { 2: 4, 9: 18 }

挑战9

问题:

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

题解:

// CHALLENGE 9function 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 10function 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 11function 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 12function 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 13function 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 5console.log(obj.setSecret(2)) // => undefinedconsole.log(obj.getSecret()) // => returns 2

挑战14

问题:

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

题解:

// CHALLENGE 14function 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()); // => 1console.log(myNewFunc1()); // => 2console.log(myNewFunc2()); // => 1console.log(myNewFunc2()); // => 2

挑战15

问题:

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

题解:

// CHALLENGE 15function 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 16function 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 0console.log(avgSoFar(4)); // => should log 4console.log(avgSoFar(8)); // => should log 6console.log(avgSoFar()); // => should log 6console.log(avgSoFar(12)); // => should log 8console.log(avgSoFar()); // => should log 8

挑战17

问题:

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

题解:

// CHALLENGE 17function 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 falseconsole.log(shouldCapitalizeLast(capLastAttempt2)); // => should log true

挑战18

问题:

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

题解:

// CHALLENGE 18function 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 19function 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 9console.log(i_like_to_live_dangerously()); // => should log 11console.log(i_like_to_live_dangerously()); // => should log 17console.log(i_like_to_live_dangerously()); // => should log 18console.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 4console.log(i_TOO_like_to_live_dangerously()); // => should log 15console.log(i_TOO_like_to_live_dangerously()); // => should log 19console.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 10console.log(i_ALSO_like_to_live_dangerously()); // => should log 13console.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!'