feat: update

上级 72b932b3
......@@ -2,7 +2,7 @@
[中文版本](README.md) | [English Version](./README-en.md)
# IT 知识图谱与技术路线
# ITCS 技术体系与知识图谱
`Copyright 2016 © 王下邀月熊`
......
......@@ -14,7 +14,7 @@
所谓编程能力,其外在表现之一即是能够随时随地用合适的语言无阻塞地实现某些功能需求,对于常见的语法,接口,算法,设计模式等能够做到心随意动,信手拈来。编程能力是提升研发效能的重要保障,于笔者而言也是毕生应该追求的目标与爱好之一。笔者的编程能力较弱,日常开发,特别是在多语言多框架并用的场景下,往往会需要不断地中断,查找以继续工作,也是令我颇为苦恼。笔者目前是致力于完善 [Awesome Cheatsheet: 语法速览, 实践备忘,从零到一,上手即用](https://github.com/wxyyxc1992/Awesome-CheatSheet),对于日常开发中用到的相关知识的备忘录/清单进行总结, 快速掌握或者回顾某个语言/框架/工具的语法或使用要点。同时将常用的,有借鉴或者锻炼价值的代码片,沉淀到 [coding-snippets: Code Snippets, DataStructure, Algorithm and DesignPattern Implementations for Several Programming Language, Web, etc.](https://github.com/wxyyxc1992/coding-snippets) 中。
所谓知识深度,即是能够对某个方面做到深入了解,并且达到融会贯通,洞若观火,可以随心所欲地加以扩展、优化、创新等改造或变换。这方面则更加的见仁见智,不同的领域与方向对于深度的定义与挖掘方向也是千差万别。笔者自知能力有限,也不敢妄加评判,若有兴趣可以前往笔者的 [Github 笔记仓库](https://github.com/wxyyxc1992?tab=repositories)浏览指正某些领域的浅薄探究。
所谓知识深度,即是能够对某个方面做到深入了解,并且达到融会贯通,洞若观火,可以随心所欲地加以扩展、优化、创新等改造或变换。这方面则更加的见仁见智,不同的领域与方向对于深度的定义与挖掘方向也是千差万别。笔者自知能力有限,也不敢妄加评判,根据自己浅薄的认知将常见的工程师成长路径总结在了 [RoadMap](./RoadMap) 中,此外若有兴趣可以前往笔者的 [Github 笔记仓库](https://github.com/wxyyxc1992?tab=repositories)浏览指正某些领域的浅薄探究。
知识深度则依赖于以下 ✨ 友情链接:
......
[![返回目录](https://parg.co/USw)](https://parg.co/bxN)
> [JavaScript 面试中常见算法问题详解](https://zhuanlan.zhihu.com/p/25308541) 翻译自 [Interview Algorithm Questions in Javascript() {...}](https://github.com/kennymkchan/interview-questions-in-javascript)  从属于笔者的  [Web 前端入门与工程实践](https://github.com/wxyyxc1992/Web-Frontend-Introduction-And-Engineering-Practices)。下文提到的很多问题从算法角度并不一定要么困难,不过用 JavaScript 内置的 API 来完成还是需要一番考量的。
# JavaScript Specification
## 阐述下 JavaScript 中的变量提升
所谓提升,顾名思义即是 JavaScript 会将所有的声明提升到当前作用域的顶部。这也就意味着我们可以在某个变量声明前就使用该变量,不过虽然 JavaScript 会将声明提升到顶部,但是并不会执行真的初始化过程。
## 阐述下 `use strict;` 的作用
`use strict;` 顾名思义也就是 JavaScript 会在所谓严格模式下执行,其一个主要的优势在于能够强制开发者避免使用未声明的变量。对于老版本的浏览器或者执行引擎则会自动忽略该指令。
```js
// Example of strict mode
'use strict';
catchThemAll();
function catchThemAll() {
x = 3.14; // Error will be thrown
return x * x;
}
```
## 解释下什么是 Event Bubbling 以及如何避免
Event Bubbling 即指某个事件不仅会触发当前元素,还会以嵌套顺序传递到父元素中。直观而言就是对于某个子元素的点击事件同样会被父元素的点击事件处理器捕获。避免 Event Bubbling 的方式可以使用`event.stopPropagation()` 或者 IE 9 以下使用`event.cancelBubble`
## == 与 === 的区别是什么
`===` 也就是所谓的严格比较,关键的区别在于`===` 会同时比较类型与值,而不是仅比较值。
```js
// Example of comparators
0 == false; // true
0 === false; // false
2 == '2'; // true
2 === '2'; // false
```
## 解释下 null 与 undefined 的区别
JavaScript 中,null 是一个可以被分配的值,设置为 null 的变量意味着其无值。而 undefined 则代表着某个变量虽然声明了但是尚未进行过任何赋值。
## 解释下 Prototypal Inheritance 与 Classical Inheritance 的区别
在类继承中,类是不可变的,不同的语言中对于多继承的支持也不一样,有些语言中还支持接口、final、abstract 的概念。而原型继承则更为灵活,原型本身是可以可变的,并且对象可能继承自多个原型。
# 数组
## 找出整型数组中乘积最大的三个数
给定一个包含整数的无序数组,要求找出乘积最大的三个数。
```
var unsorted_array = [-10, 7, 29, 30, 5, -10, -70];
computeProduct(unsorted_array); // 21000
function sortIntegers(a, b) {
  return a - b;
}
// greatest product is either (min1 * min2 * max1 || max1 * max2 * max3)
function computeProduct(unsorted) {
  var sorted_array = unsorted.sort(sortIntegers),
    product1 = 1,
    product2 = 1,
    array_n_element = sorted_array.length - 1;
  // Get the product of three largest integers in sorted array
  for (var x = array_n_element; x > array_n_element - 3; x--) {
      product1 = product1 * sorted_array[x];
  }
  product2 = sorted_array[0] * sorted_array[1] * sorted_array[array_n_element];
  if (product1 > product2) return product1;
  return product2
};
```
## 寻找连续数组中的缺失数
给定某无序数组,其包含了 n 个连续数字中的 n - 1 个,已知上下边界,要求以`O(n)`的复杂度找出缺失的数字。
```
// The output of the function should be 8
var array_of_integers = [2, 5, 1, 4, 9, 6, 3, 7];
var upper_bound = 9;
var lower_bound = 1;
findMissingNumber(array_of_integers, upper_bound, lower_bound); //8
function findMissingNumber(array_of_integers, upper_bound, lower_bound) {
  // Iterate through array to find the sum of the numbers
  var sum_of_integers = 0;
  for (var i = 0; i < array_of_integers.length; i++) {
    sum_of_integers += array_of_integers[i];
  }
  // 以高斯求和公式计算理论上的数组和
  // Formula: [(N * (N + 1)) / 2]
- [(M * (M - 1)) / 2];
  // N is the upper bound and M is the lower bound
  upper_limit_sum = (upper_bound * (upper_bound + 1)) / 2;
  lower_limit_sum = (lower_bound * (lower_bound - 1)) / 2;
  theoretical_sum = upper_limit_sum - lower_limit_sum;
  //
  return (theoretical_sum - sum_of_integers)
}
```
## 数组去重
给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。
```js
// ES6 Implementation
var array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
// ES5 Implementation
var array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
uniqueArray(array); // [1, 2, 3, 5, 9, 8]
function uniqueArray(array) {
var hashmap = {};
var unique = [];
for (var i = 0; i < array.length; i++) {
// If key returns null (unique), it is evaluated as false.
if (!hashmap.hasOwnProperty([array[i]])) {
hashmap[array[i]] = 1;
unique.push(array[i]);
}
}
return unique;
}
```
## 数组中元素最大差值计算
给定某无序数组,求取任意两个元素之间的最大差值,注意,这里要求差值计算中较小的元素下标必须小于较大元素的下标。譬如`[7, 8, 4, 9, 9, 15, 3, 1, 10]`这个数组的计算值是 11( 15 - 4 ) 而不是 14(15 - 1),因为 15 的下标小于 1。
```js
var array = [7, 8, 4, 9, 9, 15, 3, 1, 10];
// [7, 8, 4, 9, 9, 15, 3, 1, 10] would return `11` based on the difference between `4` and `15`
// Notice: It is not `14` from the difference between `15` and `1` because 15 comes before 1.
findLargestDifference(array);
function findLargestDifference(array) {
// 如果数组仅有一个元素,则直接返回 -1
if (array.length <= 1) return -1; // current_min 指向当前的最小值
var current_min = array[0];
var current_max_difference = 0; // 遍历整个数组以求取当前最大差值,如果发现某个最大差值,则将新的值覆盖 current_max_difference // 同时也会追踪当前数组中的最小值,从而保证 `largest value in future` - `smallest value before it`
for (var i = 1; i < array.length; i++) {
if (
array[i] > current_min &&
array[i] - current_min > current_max_difference
) {
current_max_difference = array[i] - current_min;
} else if (array[i] <= current_min) {
current_min = array[i];
}
} // If negative or 0, there is no largest difference
if (current_max_difference <= 0) return -1;
return current_max_difference;
}
```
## 数组中元素乘积
给定某无序数组,要求返回新数组 output ,其中 output[i] 为原数组中除了下标为 i 的元素之外的元素乘积,要求以 O(n) 复杂度实现:
```js
var firstArray = [2, 2, 4, 1];
var secondArray = [0, 0, 0, 2];
var thirdArray = [-2, -2, -3, 2];
productExceptSelf(firstArray); // [8, 8, 4, 16]
productExceptSelf(secondArray); // [0, 0, 0, 0]
productExceptSelf(thirdArray); // [12, 12, 8, -12]
function productExceptSelf(numArray) {
var product = 1;
var size = numArray.length;
var output = []; // From first array: [1, 2, 4, 16] // The last number in this case is already in the right spot (allows for us) // to just multiply by 1 in the next step. // This step essentially gets the product to the left of the index at index + 1
for (var x = 0; x < size; x++) {
output.push(product);
product = product * numArray[x];
} // From the back, we multiply the current output element (which represents the product // on the left of the index, and multiplies it by the product on the right of the element)
var product = 1;
for (var i = size - 1; i > -1; i--) {
output[i] = output[i] * product;
product = product * numArray[i];
}
return output;
}
```
## 数组交集
给定两个数组,要求求出两个数组的交集,注意,交集中的元素应该是唯一的。
```js
var firstArray = [2, 2, 4, 1];
var secondArray = [1, 2, 0, 2];
intersection(firstArray, secondArray); // [2, 1]
function intersection(firstArray, secondArray) {
// The logic here is to create a hashmap with the elements of the firstArray as the keys.
// After that, you can use the hashmap's O(1) look up time to check if the element exists in the hash
// If it does exist, add that element to the new array.
var hashmap = {};
var intersectionArray = [];
firstArray.forEach(function(element) {
hashmap[element] = 1;
}); // Since we only want to push unique elements in our case... we can implement a counter to keep track of what we already added
secondArray.forEach(function(element) {
if (hashmap[element] === 1) {
intersectionArray.push(element);
hashmap[element]++;
}
});
return intersectionArray; // Time complexity O(n), Space complexity O(n)
}
```
# 字符串
## 颠倒字符串
给定某个字符串,要求将其中单词倒转之后然后输出,譬如"Welcome to this Javascript Guide!" 应该输出为 "emocleW ot siht tpircsavaJ !ediuG"。
```js
var string = 'Welcome to this Javascript Guide!';
// Output becomes !ediuG tpircsavaJ siht ot emocleW
var reverseEntireSentence = reverseBySeparator(string, '');
// Output becomes emocleW ot siht tpircsavaJ !ediuG
var reverseEachWord = reverseBySeparator(reverseEntireSentence, ' ');
function reverseBySeparator(string, separator) {
return string
.split(separator)
.reverse()
.join(separator);
}
```
## 乱序同字母字符串
给定两个字符串,判断是否颠倒字母而成的字符串,譬如`Mary``Army`就是同字母而顺序颠倒:
```js
var firstWord = 'Mary';
var secondWord = 'Army';
isAnagram(firstWord, secondWord); // true
function isAnagram(first, second) {
// For case insensitivity, change both words to lowercase.
var a = first.toLowerCase();
var b = second.toLowerCase(); // Sort the strings, and join the resulting array to a string. Compare the results
a = a
.split('')
.sort()
.join('');
b = b
.split('')
.sort()
.join('');
return a === b;
}
```
## 会问字符串
判断某个字符串是否为回文字符串,譬如`racecar``race car`都是回文字符串:
```
isPalindrome("racecar"); // true
isPalindrome("race Car"); // true
function isPalindrome(word) {
  // Replace all non-letter chars with "" and change to lowercase
  var lettersOnly = word.toLowerCase().replace(/\s/g, "");
  // Compare the string with the reversed version of the string
  return lettersOnly === lettersOnly.split("").reverse().join("");
}
```
# 栈与队列
## 使用两个栈实现入队与出队
```
var inputStack = []; // First stack
var outputStack = []; // Second stack
// For enqueue, just push the item into the first stack
function enqueue(stackInput, item) {
  return stackInput.push(item);
}
function dequeue(stackInput, stackOutput) {
  // Reverse the stack such that the first element of the output stack is the
  // last element of the input stack. After that, pop the top of the output to
  // get the first element that was ever pushed into the input stack
  if (stackOutput.length <= 0) {
    while(stackInput.length > 0) {
      var elementToOutput = stackInput.pop();
      stackOutput.push(elementToOutput);
    }
  }
  return stackOutput.pop();
}
```
## 判断大括号是否闭合
创建一个函数来判断给定的表达式中的大括号是否闭合:
```
var expression = "{{}}{}{}"
var expressionFalse = "{}{{}";
isBalanced(expression); // true
isBalanced(expressionFalse); // false
isBalanced(""); // true
function isBalanced(expression) {
  var checkString = expression;
  var stack = [];
  // If empty, parentheses are technically balanced
  if (checkString.length <= 0) return true;
  for (var i = 0; i < checkString.length; i++) {
    if(checkString[i] === '{') {
      stack.push(checkString[i]);
    } else if (checkString[i] === '}') {
      // Pop on an empty array is undefined
      if (stack.length > 0) {
        stack.pop();
      } else {
        return false;
      }
    }
  }
  // If the array is not empty, it is not balanced
  if (stack.pop()) return false;
  return true;
}
```
# 递归
## 二进制转换
通过某个递归函数将输入的数字转化为二进制字符串:
```
decimalToBinary(3); // 11
decimalToBinary(8); // 1000
decimalToBinary(1000); // 1111101000
function decimalToBinary(digit) {
  if(digit >= 1) {
    // If digit is not divisible by 2 then recursively return proceeding
    // binary of the digit minus 1, 1 is added for the leftover 1 digit
    if (digit % 2) {
      return decimalToBinary((digit - 1) / 2) + 1;
    } else {
      // Recursively return proceeding binary digits
      return decimalToBinary(digit / 2) + 0;
    }
  } else {
    // Exit condition
    return '';
  }
}
```
## 二分搜索
```
function recursiveBinarySearch(array, value, leftPosition, rightPosition) {
  // Value DNE
  if (leftPosition > rightPosition) return -1;
  var middlePivot = Math.floor((leftPosition + rightPosition) / 2);
  if (array[middlePivot] === value) {
    return middlePivot;
  } else if (array[middlePivot] > value) {
    return recursiveBinarySearch(array, value, leftPosition, middlePivot - 1);
  } else {
    return recursiveBinarySearch(array, value, middlePivot + 1, rightPosition);
  }
}
```
# 数字
## 判断是否为 2 的指数值
```
isPowerOfTwo(4); // true
isPowerOfTwo(64); // true
isPowerOfTwo(1); // true
isPowerOfTwo(0); // false
isPowerOfTwo(-1); // false
// For the non-zero case:
function isPowerOfTwo(number) {
  // `&` uses the bitwise n.
  // In the case of number = 4; the expression would be identical to:
  // `return (4 & 3 === 0)`
  // In bitwise, 4 is 100, and 3 is 011. Using &, if two values at the same
  // spot is 1, then result is 1, else 0. In this case, it would return 000,
  // and thus, 4 satisfies are expression.
  // In turn, if the expression is `return (5 & 4 === 0)`, it would be false
  // since it returns 101 & 100 = 100 (NOT === 0)
  return number & (number - 1) === 0;
}
// For zero-case:
function isPowerOfTwoZeroCase(number) {
  return (number !== 0) && ((number & (number - 1)) === 0);
}
```
# 调用 setState 之后发生了什么?
在代码中调用`setState`函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
# React 中 Element 与 Component 的区别是?
简单而言,React Element 是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 就是利用 JSX 构建的声明式代码片然后被转化为`createElement`的调用组合。而 React Component 则是可以接收参数输入并且返回某个 React Element 的函数或者类。更多介绍可以参考[React Elements vs React Components](https://tylermcginnis.com/react-elements-vs-react-components/)
# 在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?
在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。
# React 中 refs 的作用是什么?
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加`ref`属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回:
```
class CustomForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          ref={(input) => this.input = input} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}
```
上述代码中的`input`域包含了一个`ref`属性,该属性声明的回调函数会接收`input`对应的 DOM 元素,我们将其绑定到`this`指针以便在其他的类函数中使用。另外值得一提的是,refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值:
```
function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    <form onSubmit={() => handleSubmit(inputElement.value)}>
      <input
        type='text'
        ref={(input) => inputElement = input} />
      <button type='submit'>Submit</button>
    </form>
  )
}
```
# React 中 keys 的作用是什么?
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
```
render () {
  return (
    <ul>
      {this.state.todoItems.map(({task, uid}) => {
        return <li key={uid}>{task}</li>
      })}
    </ul>
  )
}
```
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
# 如果你创建了类似于下面的`Twitter`元素,那么它相关的类定义是啥样子的?
```
<Twitter username='tylermcginnis33'>
  {(user) => user === null
    ? <Loading />
    : <Badge info={user} />}
</Twitter>
```
```
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
// fetchUser take in a username returns a promise
// which will resolve with that username's data.
class Twitter extends Component {
  // finish this
}
```
如果你还不熟悉回调渲染模式(Render Callback Pattern),这个代码可能看起来有点怪。这种模式中,组件会接收某个函数作为其子组件,然后在渲染函数中以`props.children`进行调用:
```
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
class Twitter extends Component {
  state = {
    user: null,
  }
  static propTypes = {
    username: PropTypes.string.isRequired,
  }
  componentDidMount () {
    fetchUser(this.props.username)
      .then((user) => this.setState({user}))
  }
  render () {
    return this.props.children(this.state.user)
  }
}
```
这种模式的优势在于将父组件与子组件解耦和,父组件可以直接访问子组件的内部状态而不需要再通过 Props 传递,这样父组件能够更为方便地控制子组件展示的 UI 界面。譬如产品经理让我们将原本展示的`Badge`替换为`Profile`,我们可以轻易地修改下回调函数即可:
```
<Twitter username='tylermcginnis33'>
  {(user) => user === null
    ? <Loading />
    : <Profile info={user} />}
</Twitter>
```
# Controlled Component 与 Uncontrolled Component 之间的区别是什么?
React 的核心组成之一就是能够维持内部状态的自治组件,不过当我们引入原生的 HTML 表单元素时(input,select,textarea 等),我们是否应该将所有的数据托管到 React 组件中还是将其仍然保留在 DOM 元素中呢?这个问题的答案就是受控组件与非受控组件的定义分割。受控组件(Controlled Component)代指那些交由 React 控制并且所有的表单数据统一存放的组件。譬如下面这段代码中`username`变量值并没有存放到 DOM 元素中,而是存放在组件状态数据中。任何时候我们需要改变`username`变量值时,我们应当调用`setState`函数进行修改。
```
class ControlledForm extends Component {
  state = {
    username: ''
  }
  updateUsername = (e) => {
    this.setState({
      username: e.target.value,
    })
  }
  handleSubmit = () => {}
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          value={this.state.username}
          onChange={this.updateUsername} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}
```
而非受控组件(Uncontrolled Component)则是由 DOM 存放表单数据,并非存放在 React 组件中。我们可以使用 refs 来操控 DOM 元素:
```
class UnControlledForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          ref={(input) => this.input = input} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}
```
竟然非受控组件看上去更好实现,我们可以直接从 DOM 中抓取数据,而不需要添加额外的代码。不过实际开发中我们并不提倡使用非受控组件,因为实际情况下我们需要更多的考虑表单验证、选择性的开启或者关闭按钮点击、强制输入格式等功能支持,而此时我们将数据托管到 React 中有助于我们更好地以声明式的方式完成这些功能。引入 React 或者其他 MVVM 框架最初的原因就是为了将我们从繁重的直接操作 DOM 中解放出来。
# 在生命周期中的哪一步你应该发起 AJAX 请求?
我们应当将 AJAX 请求放到 componentDidMount 函数中执行,主要原因有下:
* React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于  componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用  componentWillMount。如果我们将 AJAX 请求放到  componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。
* 如果我们将 AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了`setState`函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在  componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题。
# shouldComponentUpdate 的作用是啥以及为何它这么重要?
shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新。
# 如何告诉 React 它应该编译生产环境版本?
通常情况下我们会使用 Webpack 的 DefinePlugin 方法来将 NODE_ENV 变量值设置为 production。编译版本中 React 会忽略 propType 验证以及其他的告警信息,同时还会降低代码库的大小,React 使用了 Uglify 插件来移除生产环境下不必要的注释等信息。
# 为什么我们需要使用 React 提供的 Children API 而不是 JavaScript 的 map?
`props.children`并不一定是数组类型,譬如下面这个元素:
```
<Parent>
  <h1>Welcome.</h1>
</Parent>
```
如果我们使用`props.children.map`函数来遍历时会受到异常提示,因为在这种情况下`props.children`是对象(object)而不是数组(array)。React 当且仅当超过一个子元素的情况下会将`props.children`设置为数组,就像下面这个代码片:
```
<Parent>
  <h1>Welcome.</h1>
  <h2>props.children will now be an array</h2>
</Parent>
```
这也就是我们优先选择使用`React.Children.map`函数的原因,其已经将`props.children`不同类型的情况考虑在内了。
# 概述下 React 中的事件处理逻辑
为了解决跨浏览器兼容性问题,React 会将浏览器原生事件(Browser Native Event)封装为合成事件(SyntheticEvent)传入设置的事件处理器中。这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外有意思的是,React 并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理。这样 React 在更新 DOM 的时候就不需要考虑如何去处理附着在 DOM 上的事件监听器,最终达到优化性能的目的。
# createElement 与 cloneElement 的区别是什么?
createElement 函数是 JSX 编译之后使用的创建 React Element 的函数,而 cloneElement 则是用于复制某个元素并传入新的 Props。
# 传入 setState 函数的第二个参数的作用是什么?
该函数会在`setState`函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成:
```
this.setState(
  { username: 'tylermcginnis33' },
  () => console.log('setState has finished and the component has re-rendered.')
)
```
# 下述代码有错吗?
```
this.setState((prevState, props) => {
  return {
    streak: prevState.streak + props.count
  }
})
```
这段代码没啥问题,不过只是不太常用罢了,详细可以参考[React 中 setState 同步更新策略](https://zhuanlan.zhihu.com/p/24781259?refer=wxyyxc1992)
# RoadMap to be the one
# RoadMap to be ITCS Engineer
# 工程师的成长之路
* [Web 前端工程师之路](./Frontend)
* [服务端工程师之路](./Backend)
* [测试工程师之路](./Test)
* [运维工程师之路](./DevOps)
* [架构师之路](./SoftwareArchitecture)
* [人工智能与机器学习工程师之路](./DataScienceAI)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册