Web前端面经 - 详细整理
超级详细的前端面经,整理了很多优质面经,因为自己也要面试,所以整理的很用心,而且持续更新。
要看的去我的github 收藏,因为这里只贴出一部分内容。因为看你们就只是收藏,都不点赞,还不到github去给我打星星⭐⭐⭐ 嘤嘤嘤~
地址: https://github.com/huyaocode/webKnowledge
建议在 github 上克隆到本地看,因为github访问还是稍微有点慢的。VScode里面有Markdown的浏览插件,用起来很棒。
我的秋招还是非常顺利的,到目前为止:阿里、腾讯、头条、拼多多、360、依图都过了。面了的公司都过了。 哈哈, 这里面还是要讲究方法的,我的核心就是我整理了这个GitHub项目,这对我的基础有很大帮助,而且很多面试题都有看过。 我上岸了,哈哈,开心
o( ̄▽ ̄)ブ
求⭐Star!
下面是部分内容,完整内容在gitHub上
防抖 debounce
函数防抖就是在函数需要频繁触发的情况下,只有足够的空闲时间,才执行一次。
典型应用
- 百度搜索框在输入稍有停顿时才更新推荐热词。
- 拖拽
function debounce(handler, delay){ delay = delay || 300; var timer = null; return function(){ var _self = this, _args = arguments; clearTimeout(timer); timer = setTimeout(function(){ handler.apply(_self, _args); }, delay); } }
防抖函数为什么要记录this
和参数的例子: debounce-demo.js
节流 throttle
一个函数只有在大于执行周期时才执行,周期内调用不执行。好像水滴积攒到一定程度才会触发一次下落一样。
典型应用:
抢券时疯狂点击,既要限制次数,又要保证先点先发出请求
窗口调整
页面滚动
function throttle(handler, wait){ wait = wait || 300; var lastTime = 0; return function(){ var _self = this, _args = arguments; var nowTime = new Date().getTime(); if((nowTime - lastTime) > wait){ handler.apply(_self, _args); lastTime = nowTime; } } }
闭包问题
循环中赋值为引用的问题
for (var i = 1; i < 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) }
解决方法有3种
第一种,使用立即执行函数
方式
for (var i = 1; i < 5; i++) { (fuction(j){ setTimeout(function timer() { console.log(j) }, j * 1000) })(i) }
第二种,使用ES6的let
for (let i = 1; i < 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) }
第三种,使用setTimeout的第三个参数
for (var i = 1; i < 5; i++) { setTimeout(function timer(j) { console.log(j) }, i * 1000, i) }
计数器
实现一个foo函数 可以这么使用:
a = foo();b = foo();c = foo(); // a === 1;b === 2;c === 3; foo.clear();d = foo(); //d === 1;
function myIndex() { var index = 1; function foo(){ return index++; } foo.clear = function() { index = 1; } return foo; } var foo = myIndex();
自封***ind方法
- 因为bind的使用方法是 某函数.bind(某对象,...剩余参数)
- 所以需要在Function.prototype 上进行编程
- 将传递的参数中的某对象和剩余参数使用apply的方式在一个回调函数中执行即可
- 要在第一层获取到被绑定函数的this,因为要拿到那个函数用apply
/** * 简单版本 */ Function.prototype.myBind = (that, ...args) => { const funcThis = this; return function(..._args) { return funcThis.apply(that, args.concat(_args)); } } Function.prototype.mybind = function(ctx) { var _this = this; var args = Array.prototype.slice.call(arguments, 1); return function() { return _this.apply(ctx, args.concat(args, Array.prototype.slice.call(arguments))) } }
自封装一个apply
- 首先要先原型上即 Function.prototype上编程
- 需要拿到函数的引用, 在这里是 this
- 让 传入对象.fn = this
- 执行 传入对象.fn(传入参数)
- 返回执行结果
Function.prototype.myApply = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this let result // 处理参数和 call 有区别 if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result }
React
React 的工作原理
React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会通过 "diffing" 算法来标记虚拟 DOM 中的改变,第二步是调节(reconciliation),会用 diff 的结果来更新 DOM。
使用 React 有何优点
- JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的
- 支持服务端渲染,可改进SEO和性能
- 易于测试
- React 只关注 View 层,所以可以和其它任何框架(如Backbone.js, Angular.js)一起使用
react生命周期函数
一、初始化阶段:
- getDefaultProps
- 获取实例的默认属性
- getInitialState
- 获取每个实例的初始化状态
- componentWillMount
- 组件即将被装载、渲染到页面上
- 多用于根组件中的应用程序配置
- render
- 组件在这里生成虚拟的DOM节点
- componentDidMount
- 组件真正在被装载之后
- 在这可以完成所有没有 DOM 就不能做的所有配置,并开始获取所有你需要的数据(发送请求);如果需要设置事件监听,也可以在这完成
二、运行中状态:
- componentWillReceiveProps
- 组件将要接收到属性的时候调用
- shouldComponentUpdate
- 是一个改善性能的地方,组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
- componentWillUpdate
- 组件即将更新不能修改属性和状态
- render
- 组件重新描绘
- componentDidUpdate
- 组件已经更新
- 响应 prop 或 state 的改变
三、销毁阶段:
- componentWillUnmount
- 组件即将销毁
- 在这你可以取消网络请求,或者移除所有与组件相关的事件***
当你调用setState的时候,发生了什么事?
- 将传递给 setState 的对象合并到组件的当前状态,触发所谓的调和过程(Reconciliation)
- 然后生成新的DOM树并和旧的DOM树使用Diff算法对比
- 根据对比差异对界面进行最小化重渲染
setState第二个参数的作用
因为setState是一个异步的过程,所以说执行完setState之后不能立刻更改state里面的值。如果需要对state数据更改监听,setState提供第二个参数,就是用来监听state里面数据的更改,当数据更改完成,调用回调函数。
为什么建议传递给 setState 的参数是一个 callback 而不是一个对象
setState它是一个异步函数,他会合并多次修改,降低diff算法的比对频率。这样也会提升性能。
因为 this.props 和 this.state 的更新是异步的,不能依赖它们的值去计算下一个 state。
react中key的作用
key是React中用于追踪哪些列表中元素被修改、删除或者被添加的辅助标识。在diff算法中,key用来判断该元素节点是被移动过来的还是新创建的元素,减少不必要的元素重复渲染。
sass和less的区别
定义变量的符号不同,less是用@,sass使用$
变量的作用域不同,less在全局定义,就作用在全局,在代码块中定义,就作用于整哥代码块。而sass只作用域全局。
react生命周期中,最适合与服务端进行数据交互的是哪个函数
componentDidMount
:在这个阶段,实例和dom已经挂载完成,可以进行相关的dom操作。
react中组件传值
父传子(组件嵌套浅):父组件定义一个属性,子组件通过this.props接收。
子传父:父组件定义一个属性,并将一个回调函数赋值给定义的属性,然后子组件进行调用传过来的函数,并将参数传进去,在父组件的回调函数中即可获得子组件传过来的值。
在constructor中绑定事件函数的this指向
把一个对象的方法赋值给一个变量会造成this的丢失,所以需要绑定this,把绑定放在构造函数中可以保证只绑定一次函数,如果放在render函数中绑定this的话每次渲染都会去绑定一次this,那样是很耗费性能的。
shouldComponentUpdate(nextProps, nextState)
当父组件被重新渲染时即render函数执行时,子组件就会默认被重新渲染,但很多时候是不需要重新渲染每一个子组件的。这时就可以使用 shouldComponentUpdate 来判断是否真的需要重新渲染子组件。仅仅一个判断,就可以节约很多的消耗。
所以对于父组件发生变化而子组件不变的情况,使用shouldComponentUpdate会提升性能。
shouldComponentUpdate(nextProps, nextState) { if(nextProps.content === this.props.content) { return false; } else { return true; } }
使用PureComponent
PureComponent
内部帮我们实现了shouldComponentUpdate
的比较,其他和Component一样。但是在shouldComponentUpdate进行的是一个浅比较,看看官方文档是怎么说的。
浅比较只比较第一层的基本类型和引用类型值是否相同
如果数据结构比较复杂,那么可能会导致一些问题,要么当你知道改变的时候调用forceUpdate
,要么使用immutable
来包装你的state
无状态组件
无状态组件就是使用定义函数的方式来定义组件,这种组件相比于使用类的方式来定义的组件(有状态组件),少了很多初始化过程,更加精简,所以要是可以使用无状态组件应当尽可能的使用无状态组件,会大幅度提升效率
refs 作用
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的API。
(在构造函数中)调用 super(props) 的目的是什么
在 super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。
传递 props 给 super() 的原因则是让子类中能用 constructor 访问 this.props。
展示组件(Presentational component)和容器组件(Container component)之间有何不同
- 展示组件关心组件看起来是什么。
- 展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。
- 容器组件则更关心组件是如何运作的。
- 容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。
类组件(Class component)和函数式组件(Functional component)之间有何不同
- 类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态
- 当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 '无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件
状态(state)和属性(props)之间有何不同
State
是一种数据结构,用于组件挂载时所需的默认值。State可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。
props
是组件的配置。props由父组件传递给子组件,就子组件而言,props是不可变的。组件不能改变自身props,但是可以把其他子组件的props防止一起管理。
props也不仅仅是数据,回调函数也可以通过props传递。
客户端渲染与服务端渲染
客户端渲染即普通的React项目渲染方式。
客户端渲染流程:
- 浏览器发送请求
- 服务器返回HTML
- 浏览器发送bundle.js请求
- 服务器返回bundle.js
- 浏览器执行bundle.js中的React代码
CSR带来的问题:
- 首屏加载时间过长
- SEO 不友好
因为时间在往返的几次网络请求中就耽搁了,而且因为CSR返回到页面的HTML中没有内容,就只有一个root空元素,页面内容是靠js渲染出来的,爬虫在读取网页时就抓不到信息,所以SEO不友好
SSR带来的问题:
- React代码在服务器端执行,很大的消耗了服务器的性能
React 同构时页面加载流程
- 服务端运行React代码渲染出HTML
- 浏览器加载这个无交互的HTML代码
- 浏览器接收到内容展示
- 浏览器加载JS文件
- JS中React代码在浏览器中重新执行
应该在 React 组件的何处发起 Ajax 请求
在 React 组件中,应该在 componentDidMount 中发起网络请求。这个方***在组件第一次“挂载”(被添加到 DOM)时执行,在组件的生命周期中仅会执行一次。
更重要的是,你不能保证在组件挂载之前 Ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。
受控组件(controlled component)
一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为"受控元素"。
在 HTML 中,类似 <input>
, <textarea>
和 <select>
这样的表单元素会维护自身的状态,并基于用户的输入来更新。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值。
除了在构造函数中绑定 this,还有其它方式吗
你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。
怎么阻止组件的渲染
在组件的 render 方法中返回 null 并不会影响触发组件的生命周期方法
前端路由原理
前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新页面。目前前端使用的路由就只有两种实现方式。
- Hash 模式
- History 模式
Hash 模式
www.test.com/#/
就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener('hashchange', () => { // ... 具体逻辑 })
History 模式
History 模式是 HTML5 新推出的功能,主要使用 history.pushState
和 history.replaceState
改变 URL。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录 history.pushState(stateObject, title, URL) // 替换当前历史记录 history.replaceState(stateObject, title, URL)
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener('popstate', e => { // e.state 就是 pushState(stateObject) 中的 stateObject console.log(e.state) })
两种模式对比
- Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL
- History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
- Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资源的时候
Vue 和 React区别
改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。
Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。
React 需要使用 JSX,Vue 使用了模板语法
高阶组件 HOC (higher order component)
高阶组件是一个以组件为参数并返回一个新组件的函数。
HOC 允许你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
function add(a, b) { return a + b }
现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:
function withLog (fn) { function wrapper(a, b) { const result = fn(a, b) console.log(result) return result } return wrapper } const withLogAdd = withLog(add) withLogAdd(1, 2)
这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。
事件机制
React 其实自己实现了一套事件机制,首先我们考虑一下以下代码:
const Test = ({ list, handleClick }) => ({ list.map((item, index) => ( <span onClick={handleClick} key={index}>{index}</span> )) })
事实当然不是,JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件***的方式,将所有的事件都统一绑定在了document
上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。
另外冒泡到 document 上的事件也不是原生浏览器事件,而是React自己实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault。
那么实现合成事件的目的好处有两点,分别是:
- 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力
- 对于原生浏览器事件来说,浏览器会给***创建一个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,当事件需要被使用时,就会从池子中复用对象,事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象。
创建对象的方法
- 字面量创建
- 构造函数创建
- Object.create()
var o1 = {name: 'value'}; var o2 = new Object({name: 'value'}); var M = function() {this.name = 'o3'}; var o3 = new M(); var P = {name: 'o4'}; var o4 = Object.create(P)
this
this的指向有哪几种情况?
this代表函数调用相关联的对象,通常页称之为执行上下文。
- 作为函数直接调用,非严格模式下,this指向window,严格模式下,this指向undefined;
- 作为某对象的方法调用,this通常指向调用的对象。
- 使用apply、call、bind 可以绑定this的指向。
- 在构造函数中,this指向新创建的对象
- 箭头函数没有单独的this值,this在箭头函数创建时确定,它与声明所在的上下文相同。
如果对一个函数进行多次 bind,那么上下文会是什么呢?
let a = {} let fn = function () { console.log(this) } fn.bind().bind(a)() // => ?
不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window。
// fn.bind().bind(a) 等于 let fn2 = function fn1() { return function() { return fn.apply() }.apply(a) } fn2()
多个this规则出现时,this最终指向哪里?
首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。
原型
- JavaScript 的所有对象中都包含了一个
__proto__
内部属性,这个属性所对应的就是该对象的原型- JavaScript 的函数对象,除了原型
__proto__
之外,还预置了 prototype 属性 - 当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型
__proto__
。
- JavaScript 的函数对象,除了原型
原型链
任何一个实例对象通过原型链可以找到它对应的原型对象,原型对象上面的实例和方法都是实例所共享的。
一个对象在查找以一个方法或属性时,他会先在自己的对象上去找,找不到时,他会沿着原型链依次向上查找。
注意: 函数才有prototype,实例对象只有有proto, 而函数有的proto是因为函数是Function的实例对象
instanceof原理
判断实例对象的proto属性与构造函数的prototype是不是用一个引用。如果不是,他会沿着对象的proto向上查找的,直到顶端Object。
判断对象是哪个类的直接实例
使用对象.construcor
直接可判断
构造函数,new时发生了什么?
var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj);
- 创建一个新的对象 obj;
- 将这个空对象的proto成员指向了Base函数对象prototype成员对象
- Base函数对象的this指针替换成obj, 相当于执行了Base.call(obj);
- 如果构造函数显示的返回一个对象,那么则这个实例为这个返回的对象。 否则返回这个新创建的对象
类
类的声明
// 普通写法 function Animal() { this.name = 'name' } // ES6 class Animal2 { constructor () { this.name = 'name'; } }
继承
借用构造函数法
在构造函数中 使用Parent.call(this)
的方法继承父类属性。
原理: 将子类的this使用父类的构造函数跑一遍
缺点: Parent原型链上的属性和方法并不会被子类继承
function Parent() { this.name = 'parent' } function Child() { Parent.call(this); this.type = 'child' }
原型链实现继承
原理:把子类的prototype(原型对象)直接设置为父类的实例
缺点:因为子类只进行一次原型更改,所以子类的所有实例保存的是同一个父类的值。
当子类对象上进行值修改时,如果是修改的原始类型的值,那么会在实例上新建这样一个值;
但如果是引用类型的话,他就会去修改子类上唯一一个父类实例里面的这个引用类型,这会影响所有子类实例
function Parent() { this.name = 'parent' this.arr = [1,2,3] } function Child() { this.type = 'child' } Child.prototype = new Parent(); var c1 = new Child(); var c2 = new Child(); c1.__proto__ === c2.__proto__
组合继承方式
组合构造函数中使用call继承和原型链继承。
原理: 子类构造函数中使用Parent.call(this);
的方式可以继承写在父类构造函数中this上绑定的各属性和方法;
使用Child.prototype = new Parent()
的方式可以继承挂在在父类原型上的各属性和方法
缺点: 父类构造函数在子类构造函数中执行了一次,在子类绑定原型时又执行了一次
function Parent() { this.name = 'parent' this.arr = [1,2,3] } function Child() { Parent.call(this); this.type = 'child' } Child.prototype = new Parent();
组合继承方式 优化1:
因为这时父类构造函数的方法已经被执行过了,只需要关心原型链上的属性和方法了
Child.prototype = Parent.prototype;
缺点:
- 因为原型上有一个属性为
constructor
,此时直接使用父类的prototype的话那么会导致 实例的constructor为Parent,即不能区分这个实例对象是Child的实例还是父类的实例对象。 - 子类不可直接在prototype上添加属性和方法,因为会影响父类的原型
注意:这个时候instanseof是可以判断出实例为Child的实例的,因为instanceof的原理是沿着对象的proto判断是否有一个原型是等于该构造函数的原型的。这里把Child的原型直接设置为了父类的原型,那么: 实例.proto === Child.prototype === Child.prototype
组合继承方式 优化2 - 添加中间对象【最通用版本】:
function Parent() { this.name = 'parent' this.arr = [1,2,3] } function Child() { Parent.call(this); this.type = 'child' } Child.prototype = Object.create(Parent.prototype); //提供__proto__ Child.prototype.constrctor = Child;
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto
封装一个原生的继承方法
/** * 继承 * @param Parent * @param Child */ function extendsClass(Parent, Child) { function F() {} F.prototype = Parent.prototype Child.prototype = new F() Child.prototype.constrctor = Child return Child }
ES5/ES6 的继承除了写法以外还有什么区别?
- class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
- class 声明内部会启用严格模式。
- class 的所有方法(包括静态方法和实例方法)都是不可枚举的。
- class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
- 必须使用 new 调用 class。
- class 内部无法重写类名。
以下是目录,详细内容都在github上
地址: https://github.com/huyaocode/webKnowledge
│ README.md
│ 前端成长.md
│
├─CSS
│ │ CSS选择器.md
│ │ README.md
│ │ 其他题目.md
│ │
│ ├─动画
│ │ animal.html
│ │ animal.png
│ │ animation.html
│ │ README.md
│ │ transition.html
│ │
│ ├─居中元素
│ │ │ README.md
│ │ │
│ │ ├─垂直居中
│ │ │ center-in-body.html
│ │ │ center1.html
│ │ │ center2.html
│ │ │ center3.html
│ │ │ center4.html
│ │ │ center5.html
│ │ │
│ │ └─水平居中
│ │ center1.html
│ │ center2.html
│ │ center3.html
│ │ center4.html
│ │
│ └─布局
│ BFC两栏布局.html
│ README.md
│ 三栏-flex.html
│ 三栏-浮动方案.html
│ 三栏-绝对定位.html
│ 三栏-网格布局.html
│ 三栏-表格布局.html
│ 双飞翼布局.html
│ 圣杯布局.html
│
├─Git
│ README.md
│ 分支.md
│
├─HTML
│ example.html
│ README.md
│ 其他题目.md
│
│
├─JS基础
│ BOM.md
│ DOM.md
│ ES6.md
│ node事件轮询.md
│ README.md
│ ServiceWorker.md
│ this.md
│ 事件.md
│ 事件轮询机制.md
│ 事件队列.md
│ 全局内置对象.md
│ 其他题目.md
│ 函数.md
│ 原型链与继承.md
│ 变量类型和类型转换.md
│ 垃圾回收与内存泄露和优化.md
│ 正则.md
│
├─NodeJS
│ │ npm.md
│ │ README.md
│ │ 异步IO.md
│ │ 模块机制.md
│ │
│ └─核心模块
│ path.md
│ process.md
│
├─React
│ PureComponent与Component区别.md
│ react-router.md
│ React与Vue区别.md
│ React中性能优化.md
│ README.md
│ Redux.md
│ setState.md
│ VDOM.md
│ 高阶组件.md
│
├─TypeScript
│ interface与type.md
│ README.md
│
├─Vue
│ README.md
│
├─webpack
│ loader.md
│ package-lock.json.md
│ plugins.md
│ README.md
│ webpack配置文件.md
│ 安装.md
│ 模块化.md
│
├─Web安全
│ CSRF.md
│ README.md
│ SQL注入.md
│ XSS.md
│
├─其他
│ hybird.md
│ 错误监控.md
│
├─性能优化
│ │ CDN.md
│ │ README.md
│ │ SEO.md
│ │ webpack中优化.md
│ │ 性能测试.md
│ │ 浏览器渲染.md
│ │ 编写高性能的Javascript.md
│ │ 网络优化.md
│ │ 雅虎军规.md
│ │
│ └─testDemo
│ │ opentime-1.html
│ │ opentime-2.html
│ │
│ └─slowServer
│ index.js
│ js-bottom.html
│ js-header.html
│ slow.js
│
├─算法
│ │ 排序算法.md
│ │
│ └─树的遍历
│ 二分搜索树中求和.js
│ 克隆对象.js
│ 广度优先遍历.js
│ 深度优先遍历.js
│
├─编程题与分析题
│ bind、apply实现.md
│ compose.md
│ debounce-demo.js
│ deepCopy.js
│ name的值是多少.md
│ promise.js
│ README.md
│ reduce实现map.md
│ reduce案例.js
│ this指向.md
│ 两任务并行.md
│ 作用域.md
│ 使用Promise封装一个AJAX.md
│ 异步编程.md
│ 手写Promise.md
│ 柯里化.md
│ 深浅拷贝.md
│ 类型判断.md
│ 观察者模式.md
│ 闭包.md
│ 防抖节流.md
│
├─网络
│ Ajax.md
│ CDN.md
│ cookie和session.md
│ HTTP.md
│ HTTPS.md
│ nginx.md
│ README.md
│ RESTful.md
│ TCP.md
│ UDP.md
│ 从输入URL到页面加载完成的过程.md
│ 缓存.md
│ 跨域.md
│
└─面试
README.md
自我介绍.md