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

相关推荐

ProMonkey2024:5个oc?厉害! 但是有一个小问题:谁问你了?😡我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了(别的帖子偷来的,现学现卖😋)
点赞 评论 收藏
分享
点赞 评论 收藏
分享
60 233 评论
分享
牛客网
牛客企业服务