题目概览
- 使用 last-child 找到 ul 的最后一个 li 元素
- 闭包的概念 & 缺点 & 解决方法
- 作用域 & let var区别 & 变量提升
- 如何判断数组还是对象 & ‘’ instanceof String 结果 & 数组去重的方法
- 解决跨域问题的方案 & 前端可以做代理么
- …怎么使用 & 应用场景 & arguments
- 是否了解 promise & 如何改变promise的状态 & 解决异步问题的其他方案
- generator & async & 区别
- for…of & 机制
- 移动端解决方案 & vw 转换成px的配置
- 下拉加载更多的实现方法 & 节流 & 去抖
- 有没有自己封装过组件 & 高阶组件应用
- React DOM & React Router DOM 区别
(电面 施强)
具体分析
Promise
概念:通过链式调用的方式处理异步问题,避免了层层嵌套的回调对象。
状态:Promise的状态一旦确定就不能再更改了;两种:从pending到resolve和从pending到rejected
改变:可以直接声明一个resolve的promise:promise.resolve
延伸:还有哪些处理异步问题的方案
回调,事件监听,Promise,Generator,Async
Generator/yield
next 没有返回参数的例子(重点理解1、2、3、4步):
function* oddGenerator() {
// 1. function关键字后添加*标识的函数
yield 1; // 3. 遇到yield/return关键字就会暂停执行,直到下次触发 next() 方法
yield 3;
return 5;
}
let iterator = oddGenerator(); // 2. 这个函数并不会立即执行,而是返回一个Generator对象,通过调用对象的 next() 方法执行
// 4. next() 方法会返回一个带有 value 和 done 两个属性的对象,value 是返回值,done 是布尔类型,标识是否继续返回值。
let first = iterator.next(); // { value: 1, done: false }
let second = iterator.next(); // { value: 3, done: false }
let third = iterator.next(); // { value: 5, done: true }
next 返回了参数的例子:
function* outputGenerator() {
let ret1 = yield 1;
console.log(`got ret1: ${ret1}`);
let ret2 = yield 2;
console.log(`got ret2: ${ret2}`);
}
let iterator = outputGenerator();
iterator.next(1);
iterator.next(2); // got ret1: 2
iterator.next(3); // got ret2: 3
过程:
- 先执行 = 号右边,遇到 yield 关键字暂停执行
- 在下次触发 next 方法后继续向下执行,也就是 = 左边的部分 let ret1 = XXX;这也解释了为什么 第二次调用 next()的参数 会被 第一次 yield 赋值的变量 接收到。
Generator 对象其实是一个迭代器,可以直接用 for … of 循环迭代 generator 对象
延伸:如何处理异步问题
yield 表达式后面跟promise对象。手动调用next方法。
Async/await
概念:本身就是为了解决异步编程而产生的,Async 函数始终返回一个 Promise,使用 await 获取返回值。
区别 Generator:
- Async 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。
- 更好的语义。async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示后面的表达式需要等待结果。
- 更广的适用性。co 函数库约定,yield 后面只能是 Thunk 函数或 Promise 对象,而 await 后面,可以跟 Promise 对象或者原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)
迭代器
目的:为所有的数据结构提供统一的访问机制。比如 for…of 循环。
可迭代的数据结构:一种数据结构,只要部署了 Iterator 接口,这种数据结构就是可迭代的。比如:
- 原生的数据结构:Array String Map Set arguments TypedArray generator 对象 DOM NodeList 对象
- 手动部署了 Iterator 接口 的数据结构
const obj = { [Symbol.iterator]: function() { return { next: function() { return { value: 1, done: true }; } }; } };
迭代的过程:
- 通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置;
- 调用 next 方法,返回当前位置的对象 { value: 当前属性的值, done: 当前遍历是否结束}
- 当 done 为 true 的时候遍历结束
需要调用 Iterator 的场景
- 解构赋值
let [first, ...rest] = [1, 2, 3];
- 扩展运算符
var str = "hello"; [...str]; // ['h','e','l','l','o']
for…of
概念:新引入的循环方式;支持新的迭代协议;用于可以被迭代的数据结构;不能遍历普通对象,只能遍历可迭代对象。
原理:
- 当 for…of 循环某种数据结构时,这个循环会自动寻找 Iterator 接口,返回一个遍历器对象
- 调用对象的 next 方法,返回带有 value 和 done 两个属性的对象,value 是返回的值,done 是布尔类型,判断是够继续遍历
- 当 done 为 true 时结束遍历
与 for-in 的区别: for-in 为普通对象设计的。
for-in 的缺点:除了遍历数组元素以外,还会遍历自定义属性,甚至原型链上的属性。
扩展运算符
形式:...
扩展运算符可以看做是 rest 参数的逆运算
console.log(...[1, 2, 3]); // 1 2 3
应用:
普通函数的调用;合并数组;将字符串转为数组;将类数组转换成真正的数组;代替 apply 方法调用函数;
-
普通函数调用:
function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers); // 42
-
合并数组
var arr1 = ['a', 'b']; var arr2 = ['c', 'd']; // ES5的合并数组 arr1.concat(arr1, arr2) // [ 'a', 'b', 'c', 'd' ] // ES6的合并数组 [...arr1, ...arr2] // [ 'a', 'b', 'c', 'd' ]
-
将字符串转为数组
var str = "hello"; // ES5 var arr1 = str.split(""); // [ "h", "e", "l", "l", "o" ] // ES6 var arr2 = [...str]; // [ "h", "e", "l", "l", "o" ]
-
替代 apply 方法调用函数
// ES5 的写法 Math.max.apply(null, [14, 3, 77]); // ES6 的写法 Math.max(...[14, 3, 77]); // 等同于 Math.max(14, 3, 77);
-
将实现了 Iterator 接口的对象转换成真正的数组
var nodeList = document.querySelectorAll("div"); var array = [...nodeList];
rest 参数
形式:...变量名
作用:获取函数的多余参数
rest
参数和 arguments
对象的区别:
- rest 参数没有对应形参的实参;arguments 包含传给函数的所有实参
- rest 是真实的 array 实例;arguments 不是一个真正的数组;
- arguments 对象还有一些附加的属性,比如:
callee
(当前函数的引用)、length、Symbol(Symbol.iterator)
举例:
function add(first, ...values) {
console.log(values); // [2,3]
console.log(arguments); // [1, 2, 3, callee: 当前函数的引用, length: 3, Symbol(Symbol.iterator): 迭代接口]
}
add(1, 2, 3);
React DOM & React Router DOM 区别
React DOM 提供了针对 DOM 的方法,可以在应用的顶层调用。比如:
-
render:渲染一个 React 元素成为 DOM,放到所提供的 container 里。
ReactDOM.render(element, container[, callback])
当第一次被调用时,内部存在的任何 DOM 元素都会被替换掉。之后的调用会使用 React 的 DOM差分算法进行高效的更新。
-
unmountComponentAtNode():从 DOM 元素中移除已挂载的 React 组件,清除它的事件处理器和 state。
ReactDOM.unmountComponentAtNode(container);
下拉加载更多的实现
componentDidMount() {
this.props.monitorStore.getErrorFlowList();
// 监听内容滚动事件
this.refs.scroll.addEventListener('scroll', this.handleScroll.bind(this), false);
}
handleScroll(e){
if(e.target.scrollTop>=e.target.scrollHeight-e.target.clientHeight-100){
this.props.monitorStore.getErrorFlowList();
}
}
// 移除监听事件
componentWillUnmount() {
this.refs.scroll.removeEventListener('scroll', this.handleScroll.bind(this), false);
}
store文件:
// 1.判断是否正在加载中,若正在请求,则退出
if (this.fetchStatus == "ing") return;
// 2. 判断errorFlowList的长度与总条数是否相等,若相等(即全部加载完成),则退出
if (this.errorTotalCount === this.errorFlowList.length) {
this.fetchStatus = "finished";
return;
}
节流 Throttle & 去抖 Debounce
去抖:函数在一段时间之后再执行,如果这个时间段内有新的调用了,则取消上次调用,重新开始新的调用
- 窗口缩放
- 文本输入的验证
节流:函数在执行之后,在规定的时间内不能进行下一次执行
- DOM 元素的拖拽功能实现
- 监听滚动事件判断是否到页面底部自动加载更多内容
区别:节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。
react 封装组件的经历
比如:弹框、弹层提示tooltip、带分类的下拉组件、搜索项的提取封装
proxy 解决跨域问题
nodejs 代理:http-proxy-middleware
把请求代理转发到其他服务器的中间件
使用 webpack 配置代理代码devServer
:
module.exports = {
//...
// dev-server 使用了非常强大的 http-proxy-middleware 包
devServer: {
proxy: {
"/api": "http://localhost:3000"
}
}
};