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项目渲染方式。
客户端渲染流程:

  1. 浏览器发送请求
  2. 服务器返回HTML
  3. 浏览器发送bundle.js请求
  4. 服务器返回bundle.js
  5. 浏览器执行bundle.js中的React代码

CSR带来的问题:

  1. 首屏加载时间过长
  2. SEO 不友好

因为时间在往返的几次网络请求中就耽搁了,而且因为CSR返回到页面的HTML中没有内容,就只有一个root空元素,页面内容是靠js渲染出来的,爬虫在读取网页时就抓不到信息,所以SEO不友好

SSR带来的问题:

  1. React代码在服务器端执行,很大的消耗了服务器的性能

React 同构时页面加载流程

  1. 服务端运行React代码渲染出HTML
  2. 浏览器加载这个无交互的HTML代码
  3. 浏览器接收到内容展示
  4. 浏览器加载JS文件
  5. 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.pushStatehistory.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代表函数调用相关联的对象,通常页称之为执行上下文。

  1. 作为函数直接调用,非严格模式下,this指向window,严格模式下,this指向undefined;
  2. 作为某对象的方法调用,this通常指向调用的对象。
  3. 使用apply、call、bind 可以绑定this的指向。
  4. 在构造函数中,this指向新创建的对象
  5. 箭头函数没有单独的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__

原型链

任何一个实例对象通过原型链可以找到它对应的原型对象,原型对象上面的实例和方法都是实例所共享的。

一个对象在查找以一个方法或属性时,他会先在自己的对象上去找,找不到时,他会沿着原型链依次向上查找。

注意: 函数才有prototype,实例对象只有有proto, 而函数有的proto是因为函数是Function的实例对象

instanceof原理

判断实例对象的proto属性与构造函数的prototype是不是用一个引用。如果不是,他会沿着对象的proto向上查找的,直到顶端Object。

判断对象是哪个类的直接实例

使用对象.construcor直接可判断

构造函数,new时发生了什么?

   var obj  = {}; 
   obj.__proto__ = Base.prototype;
   Base.call(obj);  
  1. 创建一个新的对象 obj;
  2. 将这个空对象的proto成员指向了Base函数对象prototype成员对象
  3. Base函数对象的this指针替换成obj, 相当于执行了Base.call(obj);
  4. 如果构造函数显示的返回一个对象,那么则这个实例为这个返回的对象。 否则返回这个新创建的对象

类的声明

// 普通写法
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

地址: https://github.com/huyaocode/webKnowledge

去给我点个 ⭐star 吧!

#阿里巴巴##腾讯##字节跳动##秋招##前端工程师##面经#
全部评论
怎么只收藏不点赞,去star 啊!
点赞 回复 分享
发布于 2019-10-08 13:06
论    我与大佬的差距🙂
点赞 回复 分享
发布于 2019-10-08 17:05
沾沾喜气
点赞 回复 分享
发布于 2019-10-08 13:23
上去吧,楼主的面经
点赞 回复 分享
发布于 2019-10-08 13:33
神仙大佬
点赞 回复 分享
发布于 2019-10-08 17:14
给你star!总结很棒
点赞 回复 分享
发布于 2019-10-08 18:33
楼主太棒啦 点赞收藏投币三连
点赞 回复 分享
发布于 2019-10-08 21:42
上去吧,的面经
点赞 回复 分享
发布于 2019-10-10 09:39
总结的很好,收藏学习。
点赞 回复 分享
发布于 2019-10-10 18:05
时期北魏的面积
点赞 回复 分享
发布于 2019-10-11 21:45
帮顶
点赞 回复 分享
发布于 2019-10-14 12:56
UPUP 鼓包
点赞 回复 分享
发布于 2019-10-15 23:44

相关推荐

sunroof:是只有第一批需要空白三方吗,我的对接人说不需要,华为可以等解约之类的。
点赞 评论 收藏
分享
60 233 评论
分享
牛客网
牛客企业服务