前端工程师自检清单及答案整理

前言

根据大神们整理的前端自检清单,本身整理一下答案,也方便本身学习。javascript

image.png

1、JavaScript基础

变量和类型

1.JavaScript规定了几种语言类型

js目前共定义了8种语言类型,其中包括:Undefined,Null,Boolean,Number,String,Object, Symbol,BigIntcss

2.JavaScript对象的底层数据结构是什么

JavaScript基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是肯定的,并由系统自动分配和自动释放。这样带来的好处就是,内存能够及时获得回收,相对于堆来讲 ,更加容易管理内存空间。html

JavaScript引用类型数据被存储于堆中 (如对象、数组、函数等,它们是经过拷贝和new出来的)。其实,说存储于堆中,也不太准确,由于,引用类型的数据的地址指针是存储于栈中的,当咱们想要访问引用类型的值的时候,须要先从栈中得到对象的地址指针,而后,在经过地址指针找到堆中的所须要的数据。前端

3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol

1.使用Symbol来替代常量vue

const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
复制代码

2.使用Symbol来做为对象属性名(key)java

const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
  [PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18
复制代码

3.使用Symbol定义类的私有属性/方法node

// a.js
const PASSWORD = Symbol()
class Login {
  constructor(username, password) {
    this.username = username
    this[PASSWORD] = password
  }

  checkPassword(pwd) {
      return this[PASSWORD] === pwd
  }
}
export default Login
// b.js
import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456')  // true
login.PASSWORD  // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!
复制代码

因为Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再建立一个如出一辙的Symbol出来(由于Symbol是惟一的),所以这个PASSWORD的Symbol只能被限制在a.js内部使用,因此使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。react

4.注册和获取全局Symbol window中建立的Symbol实例老是惟一的,而咱们须要的是在全部这些window环境下保持一个共享的Symbol。这种状况下,咱们就须要使用另外一个API来建立或获取Symbol,那就是Symbol.for(),它能够注册或获取一个window间全局的Symbol实例:webpack

let gs1 = Symbol.for('global_symbol_1')  //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1')  //获取全局Symbol
gs1 === gs2  // true
复制代码

4.JavaScript中的变量在内存中的具体存储形式

基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,经过按值访问 引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不容许直接访问堆内存中的位置,所以操做对象时,实际操做对象的引用nginx

let a1 = 0; // 栈内存
let a2 = "this is string" // 栈内存
let a3 = null; // 栈内存
let b = { x: 10 }; // 变量b存在于栈中,{ x: 10 }做为对象存在于堆中
let c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3]做为对象存在于堆中
复制代码

image.png

5.基本类型对应的内置对象,以及他们之间的装箱拆箱操做

内置对象: Object是 JavaScript 中全部对象的父对象 数据封装类对象:Object、Array、Boolean、Number 和 String 其余对象:Function、Math、Date、RegExp、Error。 特殊的基本包装类型(String、Number、Boolean) arguments: 只存在于函数内部的一个类数组对象 装箱: 把基本数据类型转化为对应的引用数据类型的操做,装箱分为隐式装箱和显示装箱 隐式装箱

let a = 'sun'
let b = a.indexof('s') // 0 // 返回下标
复制代码

上面代码在后台实际的步骤为:

let a = new String('sun')
let b = a.indexof('s')
a = null
复制代码

实现机制:

1.建立String类型的一个实例; 2.在实例上调用指定的方法; 3.销毁这个实例;

显示装箱 经过内置对象能够对Boolean、Object、String等能够对基本类型显示装箱 let a = new String('sun')

拆箱: 拆箱和装箱相反,就是把引用类型转化为基本类型的数据,一般经过引用类型的valueof()和toString()方法实现

let name = new String('sun')
let age = new Number(24)
console.log(typeof name) // object
console.log(typeof age) //  object
// 拆箱操做
console.log(typeof age.valueOf()); // number // 24  基本的数字类型
console.log(typeof name.valueOf()); // string  // 'sun' 基本的字符类型
console.log(typeof age.toString()); // string  // '24' 基本的字符类型
console.log(typeof name.toString()); // string  // 'sun' 基本的字符类型
复制代码

6.理解值类型和引用类型

image.png

7.null和undefined的区别

null表示"没有对象",即该处不该该有值。典型用法是:

(1) 做为函数的参数,表示该函数的参数不是对象。
(2) 做为对象原型链的终点。

undefined表示"缺乏值",就是此处应该有一个值,可是尚未定义。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。

8.至少能够说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

一、typeof:(能够对基本类型作出准确的判断,但对于引用类型,用它就有点力不从心了)

typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。

二、instanceof

instanceof是用来判断A是否为B的实例时,表达式为:A instanceof B,若是 A是B的实例,则返回true; 不然返回false 在这里特别注意的是 instanceof检测的是原型

3 Object.prototype.toString

toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上全部对象的类型均可以经过这个方法获取到。 4 constructor 查看对象对应的构造函数 construvtor在对应对象的原型下面,是自动生成的,当咱们写一个构造函数的时候,程序自动添加,构造函数名.prototype.constructor = 构造函数名

image.png

9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

image.png

10.出现小数精度丢失的缘由,JavaScript能够存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法

0.1+0.2不等于0.3,是由于计算机进行计算时先转化成二进制,二浮点数用二进制表示时是无穷位的,IEEE754标准中用64位表示(1位用来表示符号位,11用来表示指数,52位表示尾数)会截断后面的位数,再转化成十进制,就有了偏差。

最大数字:

对于整数,前端出现问题的概率可能比较低,毕竟不多有业务须要须要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。

对于小数,前端出现问题的概率仍是不少的,尤为在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)

最安全的数字-Math.pow(2, 53)-1,到+Math.pow(2, 53)-1

原型和原型链

1.instanceof的底层实现原理,手动实现一个instanceof

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    }
}
复制代码

2.实现继承的几种方式以及他们的优缺点

// 原型继承
function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
}
function SubType(){
    this.subProty =false;
}
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperValue())

// 借用构造函数
function SuperType(name) {
    this.name = name;
}
function SubType(){
    SuperType.call(this, 'demo');
    this.age = 18;
}
var instance = new SubType();
console.log(instance.name);
console.log(instance.age);

// 组合继承
function SuperType(name){
    this.name = name;
    this.colors = ['red'];
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}
function SubType(name,age) {
    SuperType.call(this,name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    console.log(this.age);
}
var instance = new SubType('demo',18);
instance.sayAge();
instance.sayName();

// 原型式继承
function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}
var person = {
    name: 'tom'
}
var anotherPerson = object(person)
console.log(anotherPerson.name)

// 寄生式继承
function createAnother(original){
    var clone =Object.create(original);
    clone.sayHi = function () {
        console.log('hi');
    }
    return clone;
}
var person = {
    name: 'tom'
}
var anotherPerson = createAnother(person);
console.log(anotherPerson.name)
anotherPerson.sayHi();

// 寄生组合式继承
function SuperType(name) {
    this.name = name;
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
function inheritPrototype(subType,superType){
    var prototype = Object.create(superType.prototype);
    prototype.constructor =subType;
    subType.prototype = prototype;
}
inheritPrototype(SubType,SuperType);
var person = new SubType('zhangsan',18);
person.sayName()
复制代码

3.能够描述new一个对象的详细过程,手动实现一个new操做符

function Person (name,age){
    this.name = name;
    this.age = age;
    this.say = function () {
        console.log("I am " + this.name)
    }
}
function realizeNew(){
    let obj = {};
    let Con = [].shift.call(arguments);
    obj.__proto__ = Con.prototype;
    let result = Con.apply(obj,arguments);
    return typeof result === 'object'? result : obj
}
var person1 =realizeNew(Person,'张三')
复制代码

4.理解es6 class构造以及继承的底层实现原理

做用域和闭包

1.理解词法做用域和动态做用域

词法做用域,函数的做用域在函数定义的时候就决定了(取决于函数定义的位置)
动态做用域,函数的做用域在函数调用的时候就决定了(取决于函数的调用) js采用的是词法做用域

2.理解JavaScript的做用域和做用域链

做用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突。 ES6 以前 JavaScript 没有块级做用域,只有全局做用域和函数做用域。ES6的到来,为咱们提供了‘块级做用域’,可经过新增命令let和const来体现。

function outFun2() {
    var inVariable = "内层变量2";
}
outFun2();//要先执行这个函数,不然根本不知道里面是啥
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
复制代码

做用域链 在 JavaScript 中使用变量时,JavaScript 引擎将尝试在当前做用域中查找变量的值。若是找不到变量,它将查找外部做用域并继续这样作,直到找到变量或到达全局做用域为止。

若是仍然找不到变量,它将在全局做用域内隐式声明变量(若是不是在严格模式下)或返回错误。 ####3.理解JavaScript的执行上下文栈,能够应用堆栈信息快速定位问题 执行上下文是当前 JavaScript 代码被解析和执行时所在环境的抽象概念。

执行上下文的类型 执行上下文总共有三种类型
全局执行上下文:只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象。
函数执行上下文:存在无数个,只有在函数被调用的时候才会被建立,每次调用函数都会建立一个新的执行上下文。
Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,不多用并且不建议使用。

执行栈 执行栈,也叫调用栈,具备 LIFO(后进先出)结构,用于存储在代码执行期间建立的全部执行上下文。 首次运行JS代码时,会建立一个全局执行上下文并Push到当前的执行栈中。每当发生函数调用,引擎都会为该函数建立一个新的函数执行上下文并Push到当前执行栈的栈顶。 根据执行栈LIFO规则,当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的下一个执行上下文。

执行上下文的建立 执行上下文分两个阶段建立:
1)建立阶段;
2)执行阶段
建立阶段
一、肯定 this 的值,也被称为 This Binding。
二、LexicalEnvironment(词法环境) 组件被建立。
三、VariableEnvironment(变量环境) 组件被建立。
This Binding:
在全局执行上下文中,this 的值指向全局对象,在浏览器中,this 的值指向 window 对象。 在函数执行上下文中,this 的值取决于函数的调用方式。若是它被一个对象引用调用,那么 this 的值被设置为该对象,不然 this 的值被设置为全局对象或 undefined(严格模式下)
词法环境
在词法环境中,有两个组成部分:
(1)环境记录(environment record)
(2)对外部环境的引用 环境记录是存储变量和函数声明的实际位置。 对外部环境的引用意味着它能够访问其外部词法环境。 变量环境:
它也是一个词法环境,其 EnvironmentRecord 包含了由 VariableStatements 在此执行上下文建立的绑定。 如上所述,变量环境也是一个词法环境,所以它具备上面定义的词法环境的全部属性。 在 ES6 中,LexicalEnvironment 组件和 VariableEnvironment 组件的区别在于前者用于存储函数声明和变量( let 和 const )绑定,然后者仅用于存储变量( var )绑定。 在建立阶段,代码会被扫描并解析变量和函数声明,其中函数声明存储在环境中,而变量会被设置为 undefined(在 var 的状况下)或保持未初始化(在 let 和 const 的状况下) 这就是为何你能够在声明以前访问 var 定义的变量(尽管是 undefined ),但若是在声明以前访问 let 和 const 定义的变量就会提示引用错误的缘由。

这就是咱们所谓的变量提高。

4.this的原理以及几种不一样使用场景的取值

1、this原理

this 既不指向函数自身,也不指函数的词法做用域,而是调用函数时的对象!

2、使用场景

一)普通函数的调用,this指向的是Window

var name = '卡卡';
function cat(){
    var name = '有鱼';
    console.log(this.name);//卡卡
    console.log(this);//Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
}
cat();
复制代码

(二)对象的方法,this指的是该对象

一、一层做用域链时,this指的该对象

var name = '卡卡';
var cat = {
    name:'有鱼',
    eat:function(){
        console.log(this.name);//有鱼
    }
}
cat.eat();
复制代码

二、多层做用域链时,this指的是距离方法最近的一层对象

var name = '卡卡';
var cat = {
    name:'有鱼',
    eat1:{
        name:'年年',
        eat2:function(){
            console.log(this.name);//年年
        }
    }
}
cat.eat1.eat2();
复制代码

这里须要注意一个状况,若是cat.eat1.eat2这个结果赋值给一个变量eat3,则eat3()的值是多少呢?

var eat3 = cat.eat1.eat2;
eat3(); // 卡卡
复制代码

答案是[卡卡],这个是由于通过赋值操做时,并未发起函数调用,eat3()这个才是真正的调用,而发起这个调用的是根对象window,因此this指的就是window,this.name=卡卡

(三)构造函数的调用,this指的是实例化的新对象

var name = '卡卡';
function Cat(){
    this.name = '有鱼';
    this.type = '英短蓝猫';
}
var cat1 = new Cat();
console.log(cat1);// 实例化新对象 Cat {name: "有鱼", type: "英短蓝猫"}
console.log(cat1.name);// 有鱼
复制代码

(四)apply和call调用时,this指向参数中的对象

var name = '有鱼';
function eat(){
    console.log(this.name);
}
var cat = {
    name:'年年',
}
var dog = {
    name:'高飞',
}

eat.call(cat);// 年年
eat.call(dog);// 高飞
复制代码

(五)匿名函数调用,指向的是全局对象

var name = '卡卡';
var cat = {
    name:'有鱼',
    eat:(function(){
        console.log(this.name);//卡卡
    })()
}
cat.eat;
复制代码

(六)定时器中调用,指向的是全局变量

var name = '卡卡';
var cat = setInterval(function(){
    var name = '有鱼';
    console.log(this.name);// 卡卡
    clearInterval(cat);
},500);
复制代码

总结: ①普通函数的调用,this指向的是window
②对象方法的调用,this指的是该对象,且是最近的对象
③构造函数的调用,this指的是实例化的新对象
④apply和call调用,this指向参数中的对象
⑤匿名函数的调用,this指向的是全局对象window
⑥定时器中的调用,this指向的是全局变量window

5.闭包的实现原理和做用,能够列举几个开发中闭包的实际应用

包就是可以读取其余函数内部变量的函数 它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000
复制代码

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。 为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。 这段代码中另外一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。

6.理解堆栈溢出和内存泄漏的原理,如何防止

一、内存泄露:是指申请的内存执行完后没有及时的清理或者销毁,占用空闲内存,内存泄露过多的话,就会致使后面的程序申请不到内存。所以内存泄露会致使内部内存溢出 二、堆栈溢出:是指内存空间已经被申请完,没有足够的内存提供了

常见的手段是将一个变量置为null,该变量就会被下一轮垃圾回收机制回收。 常见的内存泄露的缘由:

  • 全局变量引发的内存泄露
  • 闭包
  • 没有被清除的计时器 解决方法:
  • 减小没必要要的全局变量
  • 严格使用闭包(由于闭包会致使内存泄露)
  • 避免死循环的发生

执行机制

1.JavaScript如何实现异步编程,能够详细描述EventLoop机制

image.png
任务队列实际上有两个,一个是宏任务队列,一个是微任务队列,当主线程执行完毕,若是微任务队列中有微任务,则会先进入执行栈,当微任务队列没有任务时,才会执行宏任务的队列。

2.宏任务和微任务分别有哪些

微任务包括: 原生Promise(有些实现的promise将then方法放到了宏任务中),Object.observe(已废弃), MutationObserver, MessageChannel;只有promise调用then的时候,then里面的函数才会被推入微任务中 宏任务包括:setTimeout, setInterval, setImmediate, I/O;

3.使用Promise实现串行

function execute(tasks){
    return tasks.reduce((previousPromise, currentPromise)=>previousPromise.then(resultList=>{
        return new Promise(resolve=>{
            currentPromise().then(result=>{
                resolve(resultList.concat(result))
            }).catch(()=>{
                resolve(resultList.concat(null))
            })
        })
    },Promise.resolve([])))
}
const execute = (tasks = []) => {
    const resultList = [];
    for(task of tasks){
        try{
            resultList.push(await tasks())
        }catch(err){
            resultList.push(null);
        }
    }
    return resultList;
}
复制代码

4.Node与浏览器EventLoop的差别

Node 10之前: 执行完一个阶段的全部任务 执行完nextTick队列里面的内容 而后执行完微任务队列的内容 Node 11之后: 和浏览器的行为统一了,都是每执行一个宏任务就执行完微任务队列。

5.如何在保证页面运行流畅的状况下处理海量数据

语法和API

1.理解ECMAScript和JavaScript的关系

ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现

2.熟练运用es五、es6提供的语法规范,

3.熟练掌握JavaScript提供的全局对象(例如Date、Math)、全局函数(例如decodeURI、isNaN)、全局属性(例如Infinity、undefined)

4.熟练应用map、reduce、filter 等高阶函数解决问题

5.setInterval须要注意的点,使用settimeout实现setInterval

function  mySetInterval(fn,mil){
    function interval(){
        setTimeout(interval,mil);
        fn();
    }
    setTimeout(interval,mil)
}
mySetInterval(function(){console.log(1)},1000)
复制代码

6.JavaScript提供的正则表达式API、可使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题

7.JavaScript异常处理的方式,统一的异常处理方案

2、HTML和CSS

HTML

1.从规范的角度理解HTML,从分类和语义的角度使用标签

2.经常使用页面标签的默认样式、自带属性、不一样浏览器的差别、处理浏览器兼容问题的方式

3.元信息类标签(head、title、meta)的使用目的和配置方法

4.HTML5离线缓存原理

5.可使用Canvas API、SVG等绘制高性能的动画

CSS

1.CSS盒模型,在不一样浏览器的差别

1. W3C 标准盒模型: 属性width,height只包含内容content,不包含border和padding。 2. IE 盒模型: 属性width,height包含border和padding,指的是content+padding+border。 css的盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。但盒子的大小由content+padding+border这几部分决定,把margin算进去的那是盒子占据的位置,而不是盒子的大小! 咱们在编写页面代码时应尽可能使用标准的W3C模型(需在页面中声明DOCTYPE类型),这样能够避免多个浏览器对同一页面的不兼容。 由于若不声明DOCTYPE类型,IE浏览器会将盒子模型解释为IE盒子模型,FireFox等会将其解释为W3C盒子模型;若在页面中声明了DOCTYPE类型,全部的浏览器都会把盒模型解释为W3C盒模型。

2.CSS全部选择器及其优先级、使用场景,哪些能够继承,如何运用at规则

3.CSS伪类和伪元素有哪些,它们的区别和实际应用

image.png
image.png

4.HTML文档流的排版规则,CSS几种定位的规则、定位参照物、对文档流的影响,如何选择最好的定位方式,雪碧图实现原理

5.水平垂直居中的方案、能够实现6种以上并对比它们的优缺点

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .parent1{
            width: 200px;
            height: 200px;
            background: red;
            position: relative;
        }
        .parent2{
            display: table-cell;
            vertical-align: middle;
            text-align: center;
        }
        .parent5{
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .child1{
            width: 100px;
            height: 100px;
            position: absolute;
            left: 50%;
            top:50%;
            margin-left: -50px;
            margin-top: -50px;
            line-height: 100px;
            text-align: center;
        }
        .child2{
            position: absolute;
            left: 50%;
            top:50%;
            transform: translate(-50%,-50%);
        }
        .child3{
            position: absolute;
            left: 0;
            top: 0;
            right: 0;
            bottom: 0;
            margin: auto;
            width: 100px;
            height: 100px;
            line-height: 100px;
            text-align: center;
        }
        .child4{
            display: inline-block;
            width: 100px;
            height: 50px;
            overflow: scroll;
        }
        .child6{
            width: 100px;
            height: 100px;
            display: inline-block;
            text-align: center;
            vertical-align: middle;
            line-height: 100px;
        }
        .parent6{
            text-align: center;
        }
        .parent6::after{
            content: '';
            height: 100%;
            vertical-align: middle;
            display: inline-block;
        }
    </style>
</head>
<body>
    <p>方案一:知道宽度的状况下 absolute+margin负值</p>
    <div class="parent1">
        <div class="child1">child1</div>
    </div>
    <p>方案二:不知道宽度的状况下 absolute+transform</p>
    <div class="parent1">
        <div class="child2">child2</div>
    </div>
    <p>方案三:不知道宽度的状况下 absolute+margin:auto</p>
    <div class="parent1">
        <div class="child3">child3</div>
    </div>
    <p>方案四:多行文本垂直居中 table-cell vertical-align:middle</p>
    <div class="parent1 parent2">
        <div class="child4">多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle</div>
    </div>
    <p>方案五:display:flex</p>
    <div class="parent1 parent5">
        <div class="child5">flex</div>
    </div>
    <p>方案六:伪元素</p>
    <div class="parent1 parent6">
        <div class="child6">伪元素</div>
    </div>
</body>
</html>
复制代码

6.BFC实现原理,能够解决的问题,如何建立BFC

juejin.im/post/5ecdd5…

7.可以使用CSS函数复用代码,实现特殊效果

8.PostCSS、Sass、Less的异同,以及使用配置,至少掌握一种

9.CSS模块化方案、如何配置按需加载、如何防止CSS阻塞渲染

10.熟练使用CSS实现常见动画,如渐变、移动、旋转、缩放等等

11.CSS浏览器兼容性写法,了解不一样API在不一样浏览器下的兼容性状况

12.掌握一套完整的响应式布局方案

3、计算机基础

编译原理

1.理解代码究竟是什么,计算机如何将代码转换为能够运行的目标程序

js是一门解释型语言(英语:Interpreted language),是一种编程语言。这种类型的编程语言,会将代码一句一句直接运行,不须要像编译语言(Compiled language)同样,通过编译器先行编译为机器码,以后再运行。这种编程语言须要利用解释器,在运行期,动态将代码逐句解释(interpret)为机器码,或是已经预先编译为机器码的的子程序,以后再运行。

2.正则表达式的匹配原理和性能优化

3.如何将JavaScript代码解析成抽象语法树(AST)

4.base64的编码原理

5.几种进制的相互转换计算方法,在JavaScript中如何表示和转换

网络协议

1.理解什么是协议,了解TCP/IP网络协议族的构成,每层协议在应用程序中发挥的做用

2.三次握手和四次挥手详细原理,为何要使用这种机制

3.有哪些协议是可靠,TCP有哪些手段保证可靠交付

4.DNS的做用、DNS解析的详细过程,DNS优化原理

5.CDN的做用和原理

6.HTTP请求报文和响应报文的具体组成,能理解常见请求头的含义,有几种请求方式,区别是什么

7.HTTP全部状态码的具体含义,看到异常状态码能快速定位问题

8.HTTP1.一、HTTP2.0带来的改变

9.HTTPS的加密原理,如何开启HTTPS,如何劫持HTTPS请求

10.理解WebSocket协议的底层原理、与HTTP的区别

###设计模式

1.熟练使用前端经常使用的设计模式编写代码,如单例模式、装饰器模式、代理模式等

2.发布订阅模式和观察者模式的异同以及实际应用

3.能够说出几种设计模式在开发中的实际应用,理解框架源码中对设计模式的应用

4、数据结构和算法

JavaScript编码能力

1.多种方式实现数组去重、扁平化、对比优缺点

2.多种方式实现深拷贝、对比优缺点

3.手写函数柯里化工具函数、并理解其应用场景和优点

4.手写防抖和节流工具函数、并理解其内部原理和应用场景

5.实现一个sleep函数

手动实现前端轮子

1.手动实现call、apply、bind

Function.prototype.myCall = function (context = window,...args) {
    context.fn = this;
    console.log(args)
    let result = context.fn(...args);
    delete context.fn;
    return result;
}
Function.prototype.myApply = function (context = window, arr) {
    context.fn = this;
    let result = !arr ? context.fn() : context.fn(...arr);
    delete context.fn;
    return result;
}
Function.prototype.myBind = function (context = window , ...arg) {
    context.fn = this;
    let bound = function () {
        let args = [...args].concat(...arguments);
        context.fn = this instanceof context.fn ? this : context;
        let result = context.fn(...args);
        delete context.fn;
        return result;
    }
    bound.prototype = new this();
    return bound;
}
复制代码

2.手动实现符合Promise/A+规范的Promise、手动实现async await

function handlePromise(promise2, x, resolve, reject){
    if (promise2 === x) { //promise2是否等于x,也就是判断是否将本身自己返回
        return reject(new TypeError('circular reference')); //若是是抛出错误
    }
    if(x !==null && (typeof x==='object' || typeof x ==='function')) {
        let called; //called控制resolve或reject 只执行一次,屡次调用没有任何做用。
        try{
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x,y=>{
                    if(called) return;
                    called = true;
                    handlePromise(promise2,y,resolve,reject)
                },r => {
                    if(called) return;
                    called = true;
                    reject(r);
                })
            } else {
                reject(x);
            }
        }catch(err) {
            if (called) return;
            called = true;
            reject(err);
        }
    } else {
        reject(x);
    }
}
class Promise {
    constructor(executor){
        this.status = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve =  (value) => {
            if(this.status === 'pending') {
                this.value = value;
                this.status = 'resolved';
                this.onResolvedCallbacks.foEach(fn => fn(this.value));
            }
        }
        let reject = (reason) => {
            if(this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedCallbacks.foEach(fn => fn(this.reason));
            }
        }
        try{
            executor(resolve,reject)
        }catch(err){
            reject(err);
        }
        
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
        let promise2; // 返回的新的promise
        if (this.status === 'pending') {
            this.onResolvedCallbacks.push()
            promise2 = new Promise((resolve,reject)=>{
                this.onResolvedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onFulfilled(this.value);
                            handlePromise(promise2,x,resolve,reject)
                        }catch(err){
                            reject(err);
                        }
                    },0)
                    
                })
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onRejected(this.reason);
                            handlePromise(promise2,x,resolve,reject)
                        }catch(err){
                            reject(err);
                        }
                    },0)
                    
                })
            })
        }
        if (this.status === 'resolved') {
            promise2 = new Promise((resolve,reject) =>{
                setTimeout(()=>{
                    try{
                        let x =onFulfilled(this.value);
                        handlePromise(promise2,x,resolve,reject)
                    }catch(err){
                        reject(err);
                    }
                },0)
                
            })
        }
        if(this.status === 'rejected') {
            onRejected(this.reason);
            promise2 = new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    try{
                        let x = onRejected(this.reason);
                        handlePromise(promise2,x,resolve,reject)
                    }catch(err){
                        reject(err);
                    }
                },0)
                
            })
        }
        return promise2;
    }
    catch(onRejected){ //在此处添加原型上的方法catch
        return this.then(null,onRejected);
    }
}
Promise.all = function (promiseArrs) { //在Promise类上添加一个all方法,接受一个传进来的promise数组
    return new Promise((resolve, reject) => { //返回一个新的Promise
        let arr = []; //定义一个空数组存放结果
        let i = 0;
        function handleData(index, data) { //处理数据函数
            arr[index] = data;
            i++;
            if (i === promiseArrs.length) { //当i等于传递的数组的长度时 
                resolve(arr); //执行resolve,并将结果放入
            }
        }
        for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
            promiseArrs[i].then((data) => {
                handleData(i, data); //将结果和索引传入handleData函数
            }, reject)
        }
    })
}
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve, reject);
        }
    })
}
Promise.resolve = function (val) {
    return new Promise((resolve, reject) => resolve(val));
}
Promise.reject = function (val) {
    return new Promise((resolve, reject) => reject(val));
}
module.exports = Promise;
复制代码

3.手写一个EventEmitter实现事件发布、订阅

class EventEmitter {
    constructor(){
        this.events = {}
    }
    on(eventName,callback){
        if(this.events[eventName]){
            this.events[eventName].push(callback)
        } else{
            this.events[eventName] = [callback]
        }
    }
    emit(eventName,...rest){
        if(this.events[eventName]){
            this.events[eventName].forEach(fn=>fn.apply(this,rest))
        }
    }
}
const event =new EventEmitter();
const handle = (...pyload) => console.log(pyload)
event.on('click',handle)
event.emit('click',1,2,3)
复制代码

4.能够说出两种实现双向绑定的方案、能够手动实现

5.手写JSON.stringify、JSON.parse

window.JSON = {
    parse: function (str) {
        return eval('(' + str + ')')   //防止{}会返回undefined
    },
    stringify: function (str) {
        if (typeof str === 'number') {
            return Number(str);
        }
        if (typeof str === 'string') {
            return str
        };
        var s = '';
        console.log(Object.prototype.toString.call(str))
        switch (Object.prototype.toString.call(str)) {
            case '[object Array]':
                s += '[';
                for (var i = 0; i < str.length - 1; i++) {
                    if (typeof str === 'string') {
                        s += '"' + str[i] + '",'
                    } else {
                        s += str[i] + ','
                    }
                }
                if (typeof str[str.length - 1] == 'string') {
                    s += '"' + str[i] + '"'
                } else {
                    if (str[str.length - 1] == null) {
                        str[str.length - 1] = null;
                        s += 'null';
                    } else {
                        s += (str[str.length - 1] ? str[str.length - 1] : '')
                    }
                }
                s += "]";
                break;
            case '[object Date]':
                console.log(str.toJSON())
                s+='"'+(str.toJSON?str.toJSON():str.toString())+'"';
                break;
            case '[object Function]':
                s= 'undefined';
                break
            case '[object Object]':
                s+='{'
                for(var key in str) {
                    if(str[key] === undefined){
                        continue;
                    }
                    if(typeof str[key] === 'symbol' || typeof str[key] === 'function') {
                        continue;
                    }
                    if(typeof Object.prototype.toString.call(str[key]) === '[object RegExp]') {
                        continue;
                    }
                    s+=('"'+key+'":"'+str[key]+'",')
                } 
                s = s.slice(0,s.length-1);
                if(s===''){s+='{'}
                s+='}'
                break   

        }
        return s;
    }
}
复制代码

6.手写一个模版引擎,并能解释其中原理

7 .手写懒加载、下拉刷新、上拉加载、预加载等效果

数据结构

1.理解常见数据结构的特色,以及他们在不一样场景下使用的优缺点

2.理解数组、字符串的存储原理,并熟练应用他们解决问题

3.理解二叉树、栈、队列、哈希表的基本结构和特色,并能够应用它解决问题

4.了解图、堆的基本结构和使用场景

算法

1.可计算一个算法的时间复杂度和空间复杂度,可估计业务逻辑代码的耗时和内存消耗

2.至少理解五种排序算法的实现原理、应用场景、优缺点,可快速说出时间、空间复杂度

3.了解递归和循环的优缺点、应用场景、并可在开发中熟练应用

4.可应用回溯算法、贪心算法、分治算法、动态规划等解决复杂问题

5.前端处理海量数据的算法方案

5、运行环境

浏览器API

1.浏览器提供的符合W3C标准的DOM操做API、浏览器差别、兼容性

2.浏览器提供的浏览器对象模型 (BOM)提供的全部全局API、浏览器差别、兼容性

3.大量DOM操做、海量数据的性能优化(合并操做、Diff、requestAnimationFrame等)

4.浏览器海量数据存储、操做性能优化

5.DOM事件流的具体实现机制、不一样浏览器的差别、事件代理

6.前端发起网络请求的几种方式及其底层实现、能够手写原生ajax、fetch、能够熟练使用第三方库

7.浏览器的同源策略,如何避免同源策略,几种方式的异同点以及如何选型

8.浏览器提供的几种存储机制、优缺点、开发中正确的选择

9.浏览器跨标签通讯

浏览器原理

1.各浏览器使用的JavaScript引擎以及它们的异同点、如何在代码中进行区分

2.请求数据到请求结束与服务器进行了几回交互

3.可详细描述浏览器从输入URL到页面展示的详细过程

4.浏览器解析HTML代码的原理,以及构建DOM树的流程

5.浏览器如何解析CSS规则,并将其应用到DOM树上

6.浏览器如何将解析好的带有样式的DOM树进行绘制

7.浏览器的运行机制,如何配置资源异步同步加载

8.浏览器回流与重绘的底层原理,引起缘由,如何有效避免

当Render Tree中部分或所有元素的尺寸、结构、或某些属性发生改变时,浏览器从新渲染部分或所有文档的过程称为回流会致使回流的操做:

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见的DOM元素
  • 激活CSS伪类(例如::hover)
  • 查询某些属性或调用某些方法

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘CSS

  • 避免使用table布局。
  • 尽量在DOM树的最末端改变class。
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolute或fixed的元素上。
  • 避免使用CSS表达式(例如:calc())。

JavaScript

  • 避免频繁操做样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  • 避免频繁操做DOM,建立一个documentFragment,在它上面应用全部DOM操做,最后再把它添加到文档中。
  • 也能够先为元素设置display: none,操做结束后再把它显示出来。由于在display属性为none的元素上进行的DOM操做不会引起回流和重绘。
  • 避免频繁读取会引起回流/重绘的属性,若是确实须要屡次使用,就用一个变量缓存起来。
  • 对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。
  • 9.浏览器的垃圾回收机制,如何避免内存泄漏 垃圾收集机制的原理 垃圾收集器会按照固定的时间间隔,周期性的找出再也不继续使用的变量,而后释放其占用的内存。

什么叫再也不继续使用的变量?

再也不使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程当中存在,当函数运行结束,没有其余引用(闭包),那么该变量会被标记回收。

全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。

工做原理:

当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工做流程:

  1. 垃圾收集器会在运行的时候会给存储在内存中的全部变量都加上标记。
  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
  3. 那些还存在标记的变量被视为准备删除的变量。
  4. 最后垃圾收集器会执行最后一步内存清除的工做,销毁那些带标记的值并回收它们所占用的内存空间。

到2008年为止,IE、Chorme、Fireofx、Safari、Opera 都使用标记清除式的垃圾收集策略,只不过垃圾收集的时间间隔互有不一样

避免内存泄漏的方法

  • 少用全局变量,避免意外产生全局变量
  • 使用闭包要及时注意,有Dom元素的引用要及时清理。
  • 计时器里的回调没用的时候要记得销毁。
  • 为了不疏忽致使的遗忘,咱们可使用 WeakSet 和 WeakMap结构,它们对于值的引用都是不计入垃圾回收机制的,表示这是弱引用。 举个例子:
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
复制代码

复制代码这种状况下,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。

####10.浏览器采用的缓存方案,如何选择和控制合适的缓存方案 缓存过程分析 浏览器与服务器通讯的方式为应答模式,便是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程以下图:

image.png
由上图咱们能够知道:

  • 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识

  • 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

    以上两点结论就是浏览器缓存机制的关键,他确保了每一个请求的缓存存入与读取,只要咱们再理解浏览器缓存的使用规则,那么全部的问题就迎刃而解了,本文也将围绕着这点进行详细分析。为了方便你们理解,这里咱们根据是否须要向服务器从新发起HTTP请求将缓存过程分为两个部分,分别是强制缓存和协商缓存 。

强制缓存

强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的状况主要有三种(暂不分析协商缓存过程),以下:

  • 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),以下图:

    image.png

  • 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存(暂不分析),以下图

    image.png

  • 存在该缓存结果和缓存标识,且该结果还没有失效,强制缓存生效,直接返回该结果,以下图

    image.png
    当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一块儿返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。 Expires Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,若是客户端的时间小于Expires的值时,直接使用缓存结果。 Cache-Control 在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:

  • public:全部内容都将被缓存(客户端和代理服务器均可缓存)

  • private:全部内容只有客户端能够缓存,Cache-Control的默认取值

  • no-cache:客户端缓存内容,可是是否使用缓存则须要通过协商缓存来验证决定

  • no-store:全部内容都不会被缓存,即不使用强制缓存,也不使用协商缓存

  • max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效 到了HTTP/1.1,Expire已经被Cache-Control替代,缘由在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间作对比,那么若是客户端与服务端的时间由于某些缘由(例如时区不一样;客户端和服务端有一方的时间不许确)发生偏差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无心义,

因为Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。

注:在没法肯定客户端的时间是否与服务端的时间同步的状况下,Cache-Control相比于expires是更好的选择,因此同时存在时,只有Cache-Control生效。

浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?

  • 内存缓存(from memory cache):内存缓存具备两个特色,分别是快速读取和时效性: 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程必定的内存资源,以方便下次运行使用时的快速读取。 时效性:一旦该进程关闭,则该进程的内存则会清空。

  • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存须要对该缓存存放的硬盘文件进行I/O操做,而后从新解析该缓存内容,读取复杂,速度比内存缓存慢。

在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,因此每次渲染页面都须要从硬盘读取缓存(from disk cache)。

协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有如下两种状况:

  • 协商缓存生效,返回304,以下
    image.png
  • 协商缓存失效,返回200和请求结果结果,以下
    image.png
    一样,协商缓存的标识也是在响应报文的HTTP头中和请求结果一块儿返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。 Last-Modified / If-Modified-Since
  • Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,以下。
    image.png
  • If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,经过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间作对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则从新返回资源,状态码为200;不然则返回304,表明资源无更新,可继续使用缓存文件,以下。
    image.png
  • Etag是服务器响应请求时,返回当前资源文件的一个惟一标识(由服务器生成),以下。
    image.png
  • If-None-Match是客户端再次发起该请求时,携带上次请求返回的惟一标识Etag值,经过此字段值告诉服务器该资源上次请求返回的惟一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值作对比,一致则返回304,表明资源无更新,继续使用缓存文件;不一致则从新返回资源文件,状态码为200,以下。
    image.png
    ####总结 强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么表明该请求的缓存失效,从新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存,主要过程以下:
    image.png
    参考文档:www.cnblogs.com/chenhuichao…

Node

1.理解Node在应用程序中的做用,可使用Node搭建前端运行环境、使用Node操做文件、操做数据库等等

2.掌握一种Node开发框架,如Express,Express和Koa的区别

3.熟练使用Node提供的API如Path、Http、Child Process等并理解其实现原理

4.Node的底层运行原理、和浏览器的异同

5.Node事件驱动、非阻塞机制的实现原理

6、框架和类库

TypeScript

1.理解泛型、接口等面向对象的相关概念,TypeScript对面向对象理念的实现

2.理解使用TypeScript的好处,掌握TypeScript基础语法

3.TypeScript的规则检测原理

4.能够在React、Vue等框架中使用TypeScript进行开发

React

1.React和vue选型和优缺点、核心架构的区别

2.React中setState的执行机制,如何有效的管理状态

3.React的事件底层实现机制

4.React的虚拟DOM和Diff算法的内部实现

5.React的Fiber工做原理,解决了什么问题

6.React Router和Vue Router的底层实现原理、动态加载实现原理

7.可熟练应用React API、生命周期等,可应用HOC、render props、Hooks等高阶用法解决问题

8.基于React的特性和原理,能够手动实现一个简单的React

Vue

1.熟练使用Vue的API、生命周期、钩子函数

2.MVVM框架设计理念

3.Vue双向绑定实现原理、Diff算法的内部实现

4.Vue的事件机制

5.从template转换成真实DOM的实现机制

多端开发

1.单页面应用(SPA)的原理和优缺点,掌握一种快速开发SPA的方案

2.理解Viewport、em、rem的原理和用法,分辨率、px、ppi、dpi、dp的区别和实际应用

3.移动端页面适配解决方案、不一样机型适配方案

4.掌握一种JavaScript移动客户端开发技术,如React Native:能够搭建React Native开发环境,熟练进行开发,可理解React Native的运做原理,不一样端适配

5.掌握一种JavaScript PC客户端开发技术,如Electron:可搭建Electron开发环境,熟练进行开发,可理解Electron的运做原理

6.掌握一种小程序开发框架或原生小程序开发

7.理解多端框架的内部实现原理,至少了解一个多端框架的使用

###数据流管理

1.掌握React和Vue传统的跨组件通讯方案,对比采用数据流管理框架的异同

2.熟练使用Redux管理数据流,并理解其实现原理,中间件实现原理

3.熟练使用Mobx管理数据流,并理解其实现原理,相比Redux有什么优点

4.熟练使用Vuex管理数据流,并理解其实现原理

5.以上数据流方案的异同和优缺点,不状况下的技术选型

7、前端工程

项目构建

1.理解npm、yarn依赖包管理的原理,二者的区别

yarn是通过从新设计的崭新的npm客户端,它能让开发人员并行处理全部必须的操做,并添加了一些其余改进。 运行速度获得了显著的提高,整个安装时间也变得更少 像npm同样,yarn使用本地缓存。与npm不一样的是,yarn无需互联网链接就能安装本地缓存的依赖项,它提供了离线模式。这个功能在2012年的npm项目中就被提出来过,但一直没有实现。 容许合并项目中使用到的全部的包的许可证

2.可使用npm运行自定义脚本

3.理解Babel、ESLint、webpack等工具在项目中承担的做用

4.ESLint规则检测原理,经常使用的ESLint配置

5.Babel的核心原理,能够本身编写一个Babel插件

image.png

和编译器相似,babel 的转译过程也分为三个阶段,这三步具体是:

1.解析 Parse 将代码解析生成抽象语法树( 即AST ),也就是计算机理解咱们代码的方式(扩展:通常来讲每一个 js 引擎都有本身的 AST,好比熟知的 v8,chrome 浏览器会把 js 源码转换为抽象语法树,再进一步转换为字节码或机器代码),而 babel 则是经过 babylon 实现的 。简单来讲就是一个对于 JS 代码的一个编译过程,进行了词法分析与语法分析的过程。

2.转换 Transform 对于 AST 进行变换一系列的操做,babel 接受获得 AST 并经过 babel-traverse 对其进行遍历,在此过程当中进行添加、更新及移除等操做。

3.生成 Generate 将变换后的 AST 再转换为 JS 代码, 使用到的模块是 babel-generator。 而 babel-core 模块则是将三者结合使得对外提供的API作了一个简化。

6.能够配置一种前端代码兼容方案,如Polyfill

7.Webpack的编译原理、构建流程、热更新原理,chunk、bundle和module的区别和应用

8.可熟练配置已有的loaders和plugins解决问题,能够本身编写loaders和plugins

nginx

1.正向代理与反向代理的特色和实例

2.可手动搭建一个简单的nginx服务器、

3.熟练应用经常使用的nginx内置变量,掌握经常使用的匹配规则写法

4.能够用nginx实现请求过滤、配置gzip、负载均衡等,并能解释其内部原理

8、项目和业务

性能优化

1.了解前端性能衡量指标、性能监控要点,掌握一种前端性能监控方案

2.了解常见的Web、App性能优化方案

3.SEO排名规则、SEO优化方案、先后端分离的SEO

4.SSR实现方案、优缺点、及其性能优化

5.Webpack的性能优化方案

6.Canvas性能优化方案

7.React、Vue等框架使用性能优化方案

前端安全

1.XSS攻击的原理、分类、具体案例,前端如何防护

跨站脚本攻击是指经过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。 XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而能够达到攻击者盗取用户信息或其余侵犯用户安全隐私的目的。

1.非持久型 XSS(反射型 XSS )

非持久型 XSS 漏洞攻击有如下几点特征:

  • 即时性,不通过服务器存储,直接经过 HTTP 的 GET 和 POST 请求就能完成一次攻击,拿到用户隐私数据。
  • 攻击者须要诱骗点击,必需要经过用户点击连接才能发起
  • 反馈率低,因此较难发现和响应修复
  • 盗取用户敏感保密信息

为了防止出现非持久型 XSS 漏洞,须要确保这么几件事情:

  • Web 页面渲染的全部内容或者渲染的数据都必须来自于服务端。
  • 尽可能不要从 URL,document.referrer,document.forms 等这种 DOM API 中获取数据直接渲染。
  • 尽可能不要使用 eval, new Function(),document.write(),document.writeln(),window.setInterval(),window.setTimeout(),innerHTML,document.createElement() 等可执行字符串的方法。 若是作不到以上几点,也必须对涉及 DOM 渲染的方法传入的字符串参数作 escape 转义。 前端渲染的时候对任何的字段都须要作 escape 转义编码

2.持久型 XSS(存储型 XSS) 持久型 XSS 漏洞,通常存在于 Form 表单提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面得到后端从数据库中读出的注入代码时,刚好将其渲染执行

攻击成功须要同时知足如下几个条件:

  • POST 请求提交表单后端没作转义直接入库。
  • 后端从数据库中取出数据没作转义直接输出给前端。
  • 前端拿到后端数据没作转义直接渲染成 DOM。

持久型 XSS 有如下几个特色:

  • 持久性,植入在数据库中
  • 盗取用户敏感私密信息
  • 危害面广

如何防护 1) CSP CSP 本质上就是创建白名单,开发者明确告诉浏览器哪些外部资源能够加载和执行。咱们只须要配置规则,如何拦截是由浏览器本身实现的。咱们能够经过这种方式来尽可能减小 XSS 攻击。

一般能够经过两种方式来开启 CSP:

  • 设置 HTTP Header 中的 Content-Security-Policy
  • 设置 meta 标签的方式

2) 转义字符 用户的输入永远不可信任的,最广泛的作法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义

3) HttpOnly Cookie 这是预防XSS攻击窃取用户cookie最有效的防护手段。Web应用程序在设置cookie时,将其属性设为HttpOnly,就能够避免该网页的cookie被客户端恶意JavaScript窃取,保护用户cookie信息。

2.CSRF攻击的原理、具体案例,前端如何防护

1.CSRF攻击的原理 完成 CSRF 攻击必需要有三个条件:

  • 用户已经登陆了站点 A,并在本地记录了 cookie
  • 在用户没有登出站点 A 的状况下(也就是 cookie 生效的状况下),访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)。
  • 站点 A 没有作任何 CSRF 防护

2.如何防护 防范 CSRF 攻击能够遵循如下几种规则:

  • Get 请求不对数据进行修改
  • 不让第三方网站访问到用户 Cookie
  • 阻止第三方网站请求接口
  • 请求时附带验证信息,好比验证码或者 Token 1) SameSite 能够对 Cookie 设置 SameSite 属性。该属性表示 Cookie 不随着跨域请求发送,能够很大程度减小 CSRF 的攻击,可是该属性目前并非全部浏览器都兼容。

2) Referer Check HTTP Referer是header的一部分,当浏览器向web服务器发送请求时,通常会带上Referer信息告诉服务器是从哪一个页面连接过来的,服务器籍此能够得到一些信息用于处理。能够经过检查请求的来源来防护CSRF攻击。正常请求的referer具备必定规律,如在提交表单的referer一定是在该页面发起的请求。因此经过检查http包头referer的值是否是这个页面,来判断是否是CSRF攻击。

但在某些状况下如从https跳转到http,浏览器处于安全考虑,不会发送referer,服务器就没法进行check了。若与该网站同域的其余网站有XSS漏洞,那么攻击者能够在其余网站注入恶意脚本,受害者进入了此类同域的网址,也会遭受攻击。出于以上缘由,没法彻底依赖Referer Check做为防护CSRF的主要手段。可是能够经过Referer Check来监控CSRF攻击的发生。

3) Anti CSRF Token 目前比较完善的解决方案是加入Anti-CSRF-Token。即发送请求时在HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器创建一个拦截器来验证这个token。服务器读取浏览器当前域cookie中这个token值,会进行校验该请求当中的token和cookie当中的token值是否都存在且相等,才认为这是合法的请求。不然认为此次请求是违法的,拒绝该次服务。

这种方法相比Referer检查要安全不少,token能够在用户登录后产生并放于session或cookie中,而后在每次请求时服务器把token从session或cookie中拿出,与本次请求中的token 进行比对。因为token的存在,攻击者没法再构造出一个完整的URL实施CSRF攻击。但在处理多个页面共存问题时,当某个页面消耗掉token后,其余页面的表单保存的仍是被消耗掉的那个token,其余页面的表单提交时会出现token错误。

3.HTTP劫持、页面劫持的原理、防护措施

点击劫持的原理 用户在登录 A 网站的系统后,被攻击者诱惑打开第三方网站,而第三方网站经过 iframe 引入了 A 网站的页面内容,用户在第三方网站中点击某个按钮(被装饰的按钮),其实是点击了 A 网站的按钮。

如何防护 1)X-FRAME-OPTIONS X-FRAME-OPTIONS是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防护用 iframe 嵌套的点击劫持攻击。

该响应头有三个值可选,分别是

  • DENY,表示页面不容许经过 iframe 的方式展现
  • SAMEORIGIN,表示页面能够在相同域名下经过 iframe 的方式展现
  • ALLOW-FROM,表示页面能够在指定来源的 iframe 中展现

2)JavaScript 防护 对于某些远古浏览器来讲,并不能支持上面的这种方式,那咱们只有经过 JS 的方式来防护点击劫持了。

if(top.location != self.location){
    top.location = self.location;
}
复制代码

9、资源推荐

语言基础

计算机基础

数据结构和算法

运行环境

框架和类库

前端工程

项目和业务

学习提高

另外推荐我一直在关注的几位大佬的我的博客:

技术以外

相关文章
相关标签/搜索