1、阐述下 JavaScript 中的变量提升

所谓提升,顾名思义即是 JavaScript 会将所有的声明提升到当前作用域的顶部。这也就意味着我们可以在某个变量声明前就使用该变量,不过虽然 JavaScript 会将声明提升到顶部,但是并不会执行真的初始化过程。

2、阐述下 use strict; 的作用

use strict; 顾名思义也就是 JavaScript 会在所谓严格模式下执行,其一个主要的优势在于能够强制开发者避免使用未声明的变量。对于老版本的浏览器或者执行引擎则会自动忽略该指令。

  1. // Example of strict mode
  2. "use strict";
  3. catchThemAll();
  4. function catchThemAll() {
  5. x = 3.14; // Error will be thrown
  6. return x * x;
  7. }

3、解释下什么是 Event Bubbling 以及如何避免

Event Bubbling 即指某个事件不仅会触发当前元素,还会以嵌套顺序传递到父元素中。直观而言就是对于某个子元素的点击事件同样会被父元素的点击事件处理器捕获。避免 Event Bubbling 的方式可以使用event.stopPropagation() 或者 IE 9 以下使用event.cancelBubble。

4、== 与 === 的区别是什么

=== 也就是所谓的严格比较,关键的区别在于=== 会同时比较类型与值,而不是仅比较值。

  1. // Example of comparators
  2. 0 == false; // true
  3. 0 === false; // false
  4. 2 == '2'; // true
  5. 2 === '2'; // false

5、解释下 null 与 undefined 的区别

JavaScript 中,null 是一个可以被分配的值,设置为 null 的变量意味着其无值。而 undefined 则代表着某个变量虽然声明了但是尚未进行过任何赋值。

6、解释下 Prototypal Inheritance 与 Classical Inheritance 的区别

在类继承中,类是不可变的,不同的语言中对于多继承的支持也不一样,有些语言中还支持接口、final、abstract 的概念。而原型继承则更为灵活,原型本身是可以可变的,并且对象可能继承自多个原型。

数组

7、找出整型数组中乘积最大的三个数

给定一个包含整数的无序数组,要求找出乘积最大的三个数。

  1. let unsorted_array = [-10, 7, 29, 30, 5, -10, -70];
  2. computeProduct(unsorted_array); // 21000
  3. function sortIntegers(a, b) {
  4. return a - b;
  5. }
  6. // greatest product is either (min1 * min2 * max1 || max1 * max2 * max3)
  7. function computeProduct(unsorted) {
  8. let sorted_array = unsorted.sort(sortIntegers),
  9. product1 = 1,
  10. product2 = 1,
  11. array_n_element = sorted_array.length - 1;
  12. // Get the product of three largest integers in sorted array
  13. for (let x = array_n_element; x > array_n_element - 3; x--) {
  14. product1 = product1 * sorted_array[x];
  15. }
  16. product2 = sorted_array[0] * sorted_array[1] * sorted_array[array_n_element];
  17. if (product1 > product2) return product1;
  18. return product2
  19. };

8、寻找连续数组中的缺失数

给定某无序数组,其包含了 n 个连续数字中的 n - 1 个,已知上下边界,要求以O(n)的复杂度找出缺失的数字。

  1. // The output of the function should be 8
  2. let array_of_integers = [2, 5, 1, 4, 9, 6, 3, 7];
  3. let upper_bound = 9;
  4. let lower_bound = 1;
  5. findMissingNumber(array_of_integers, upper_bound, lower_bound); //8
  6. function findMissingNumber(array_of_integers, upper_bound, lower_bound) {
  7. // Iterate through array to find the sum of the numbers
  8. let sum_of_integers = 0;
  9. for (let i = 0; i < array_of_integers.length; i++) {
  10. sum_of_integers += array_of_integers[i];
  11. }
  12. // 以高斯求和公式计算理论上的数组和
  13. // Formula: [(N * (N + 1)) / 2] - [(M * (M - 1)) / 2];
  14. // N is the upper bound and M is the lower bound
  15. upper_limit_sum = (upper_bound * (upper_bound + 1)) / 2;
  16. lower_limit_sum = (lower_bound * (lower_bound - 1)) / 2;
  17. theoretical_sum = upper_limit_sum - lower_limit_sum;
  18. //
  19. return (theoretical_sum - sum_of_integers)
  20. }

9、数组去重

给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。

  1. // ES6 Implementation
  2. let array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
  3. Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
  4. // ES5 Implementation
  5. let array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
  6. uniqueArray(array); // [1, 2, 3, 5, 9, 8]
  7. function uniqueArray(array) {
  8. let hashmap = {};
  9. let unique = [];
  10. for(let i = 0; i < array.length; i++) {
  11. // If key returns null (unique), it is evaluated as false.
  12. if(!hashmap.hasOwnProperty([array[i]])) {
  13. hashmap[array[i]] = 1;
  14. unique.push(array[i]);
  15. }
  16. }
  17. return unique;
  18. }

11、数组中元素最大差值计算

给定某无序数组,求取任意两个元素之间的最大差值,注意,这里要求差值计算中较小的元素下标必须小于较大元素的下标。
譬如[7, 8, 4, 9, 9, 15, 3, 1, 10]这个数组的计算值是 11( 15 - 4 ) 而不是 14(15 - 1),因为 15 的下标小于 1。

  1. let array = [7, 8, 4, 9, 9, 15, 3, 1, 10];
  2. // [7, 8, 4, 9, 9, 15, 3, 1, 10] would return `11` based on the difference between `4` and `15`
  3. // Notice: It is not `14` from the difference between `15` and `1` because 15 comes before 1.
  4. findLargestDifference(array);
  5. function findLargestDifference(array) {
  6. // 如果数组仅有一个元素,则直接返回 -1
  7. if (array.length <= 1) return -1;
  8. // current_min 指向当前的最小值
  9. let current_min = array[0];
  10. let current_max_difference = 0;
  11. // 遍历整个数组以求取当前最大差值,如果发现某个最大差值,则将新的值覆盖 current_max_difference
  12. // 同时也会追踪当前数组中的最小值,从而保证 `largest value in future` - `smallest value before it`
  13. for (let i = 1; i < array.length; i++) {
  14. if (array[i] > current_min && (array[i] - current_min > current_max_difference)) {
  15. current_max_difference = array[i] - current_min;
  16. } else if (array[i] <= current_min) {
  17. current_min = array[i];
  18. }
  19. }
  20. // If negative or 0, there is no largest difference
  21. if (current_max_difference <= 0) return -1;
  22. return current_max_difference;
  23. }

12、数组中元素乘积

给定某无序数组,要求返回新数组 output ,其中 output[i] 为原数组中除了下标为 i 的元素之外的元素乘积,要求以 O(n) 复杂度实现:

  1. let firstArray = [2, 2, 4, 1];
  2. let secondArray = [0, 0, 0, 2];
  3. let thirdArray = [-2, -2, -3, 2];
  4. productExceptSelf(firstArray); // [8, 8, 4, 16]
  5. productExceptSelf(secondArray); // [0, 0, 0, 0]
  6. productExceptSelf(thirdArray); // [12, 12, 8, -12]
  7. function productExceptSelf(numArray) {
  8. let product = 1;
  9. let size = numArray.length;
  10. let output = [];
  11. // From first array: [1, 2, 4, 16]
  12. // The last number in this case is already in the right spot (allows for us)
  13. // to just multiply by 1 in the next step.
  14. // This step essentially gets the product to the left of the index at index + 1
  15. for (let x = 0; x < size; x++) {
  16. output.push(product);
  17. product = product * numArray[x];
  18. }
  19. // From the back, we multiply the current output element (which represents the product
  20. // on the left of the index, and multiplies it by the product on the right of the element)
  21. let product = 1;
  22. for (let i = size - 1; i > -1; i--) {
  23. output[i] = output[i] * product;
  24. product = product * numArray[i];
  25. }
  26. return output;
  27. }

13、数组交集

给定两个数组,要求求出两个数组的交集,注意,交集中的元素应该是唯一的。

  1. let firstArray = [2, 2, 4, 1];
  2. let secondArray = [1, 2, 0, 2];
  3. intersection(firstArray, secondArray); // [2, 1]
  4. function intersection(firstArray, secondArray) {
  5. // The logic here is to create a hashmap with the elements of the firstArray as the keys.
  6. // After that, you can use the hashmap's O(1) look up time to check if the element exists in the hash
  7. // If it does exist, add that element to the new array.
  8. let hashmap = {};
  9. let intersectionArray = [];
  10. firstArray.forEach(function(element) {
  11. hashmap[element] = 1;
  12. });
  13. // Since we only want to push unique elements in our case... we can implement a counter to keep track of what we already added
  14. secondArray.forEach(function(element) {
  15. if (hashmap[element] === 1) {
  16. intersectionArray.push(element);
  17. hashmap[element]++;
  18. }
  19. });
  20. return intersectionArray;
  21. // Time complexity O(n), Space complexity O(n)
  22. }

字符串

14、颠倒字符串

给定某个字符串,要求将其中单词倒转之后然后输出,譬如”Welcome to this Javascript Guide!” 应该输出为 “emocleW ot siht tpircsavaJ !ediuG”。

  1. let string = "Welcome to this Javascript Guide!";
  2. // Output becomes !ediuG tpircsavaJ siht ot emocleW
  3. let reverseEntireSentence = reverseBySeparator(string, "");
  4. // Output becomes emocleW ot siht tpircsavaJ !ediuG
  5. let reverseEachWord = reverseBySeparator(reverseEntireSentence, " ");
  6. function reverseBySeparator(string, separator) {
  7. return string.split(separator).reverse().join(separator);
  8. }

15、乱序同字母字符串

给定两个字符串,判断是否颠倒字母而成的字符串,譬如Mary与Army就是同字母而顺序颠倒:

  1. let firstWord = "Mary";
  2. let secondWord = "Army";
  3. isAnagram(firstWord, secondWord); // true
  4. function isAnagram(first, second) {
  5. // For case insensitivity, change both words to lowercase.
  6. let a = first.toLowerCase();
  7. let b = second.toLowerCase();
  8. // Sort the strings, and join the resulting array to a string. Compare the results
  9. a = a.split("").sort().join("");
  10. b = b.split("").sort().join("");
  11. return a === b;
  12. }

16、回文字符串

判断某个字符串是否为回文字符串,譬如racecar与race car都是回文字符串:

  1. isPalindrome("racecar"); // true
  2. isPalindrome("race Car"); // true
  3. function isPalindrome(word) {
  4. // Replace all non-letter chars with "" and change to lowercase
  5. let lettersOnly = word.toLowerCase().replace(/\s/g, "");
  6. // Compare the string with the reversed version of the string
  7. return lettersOnly === lettersOnly.split("").reverse().join("");
  8. }

栈与队列

17、使用两个栈实现入队与出队

  1. let inputStack = []; // First stack
  2. let outputStack = []; // Second stack
  3. // For enqueue, just push the item into the first stack
  4. function enqueue(stackInput, item) {
  5. return stackInput.push(item);
  6. }
  7. function dequeue(stackInput, stackOutput) {
  8. // Reverse the stack such that the first element of the output stack is the
  9. // last element of the input stack. After that, pop the top of the output to
  10. // get the first element that was ever pushed into the input stack
  11. if (stackOutput.length <= 0) {
  12. while(stackInput.length > 0) {
  13. let elementToOutput = stackInput.pop();
  14. stackOutput.push(elementToOutput);
  15. }
  16. }
  17. return stackOutput.pop();
  18. }

18、判断大括号是否闭合

创建一个函数来判断给定的表达式中的大括号是否闭合:

  1. let expression = "{{}}{}{}"
  2. let expressionFalse = "{}{{}";
  3. isBalanced(expression); // true
  4. isBalanced(expressionFalse); // false
  5. isBalanced(""); // true
  6. function isBalanced(expression) {
  7. let checkString = expression;
  8. let stack = [];
  9. // If empty, parentheses are technically balanced
  10. if (checkString.length <= 0) return true;
  11. for (let i = 0; i < checkString.length; i++) {
  12. if(checkString[i] === '{') {
  13. stack.push(checkString[i]);
  14. } else if (checkString[i] === '}') {
  15. // Pop on an empty array is undefined
  16. if (stack.length > 0) {
  17. stack.pop();
  18. } else {
  19. return false;
  20. }
  21. }
  22. }
  23. // If the array is not empty, it is not balanced
  24. if (stack.pop()) return false;
  25. return true;
  26. }

递归

19、二进制转换

通过某个递归函数将输入的数字转化为二进制字符串:

  1. decimalToBinary(3); // 11
  2. decimalToBinary(8); // 1000
  3. decimalToBinary(1000); // 1111101000
  4. function decimalToBinary(digit) {
  5. if(digit >= 1) {
  6. // If digit is not divisible by 2 then recursively return proceeding
  7. // binary of the digit minus 1, 1 is added for the leftover 1 digit
  8. if (digit % 2) {
  9. return decimalToBinary((digit - 1) / 2) + 1;
  10. } else {
  11. // Recursively return proceeding binary digits
  12. return decimalToBinary(digit / 2) + 0;
  13. }
  14. } else {
  15. // Exit condition
  16. return '';
  17. }
  18. }

20、二分搜索

  1. function recursiveBinarySearch(array, value, leftPosition, rightPosition) {
  2. // Value DNE
  3. if (leftPosition > rightPosition) return -1;
  4. let middlePivot = Math.floor((leftPosition + rightPosition) / 2);
  5. if (array[middlePivot] === value) {
  6. return middlePivot;
  7. } else if (array[middlePivot] > value) {
  8. return recursiveBinarySearch(array, value, leftPosition, middlePivot - 1);
  9. } else {
  10. return recursiveBinarySearch(array, value, middlePivot + 1, rightPosition);
  11. }
  12. }

数字

21、判断是否为 2 的指数值

  1. isPowerOfTwo(4); // true
  2. isPowerOfTwo(64); // true
  3. isPowerOfTwo(1); // true
  4. isPowerOfTwo(0); // false
  5. isPowerOfTwo(-1); // false
  6. // For the non-zero case:
  7. function isPowerOfTwo(number) {
  8. // `&` uses the bitwise n.
  9. // In the case of number = 4; the expression would be identical to:
  10. // `return (4 & 3 === 0)`
  11. // In bitwise, 4 is 100, and 3 is 011. Using &, if two values at the same
  12. // spot is 1, then result is 1, else 0. In this case, it would return 000,
  13. // and thus, 4 satisfies are expression.
  14. // In turn, if the expression is `return (5 & 4 === 0)`, it would be false
  15. // since it returns 101 & 100 = 100 (NOT === 0)
  16. return number & (number - 1) === 0;
  17. }
  18. // For zero-case:
  19. function isPowerOfTwoZeroCase(number) {
  20. return (number !== 0) && ((number & (number - 1)) === 0);
  21. }