前端工程师自检清单73答

开篇

本文参考文章《一名【合格】前端工程师的自检清单》, 并对其中的部分题目进行了解答,如有遗漏或错误之处望你们指出纠正,共同进步。(点击题目展开答案!) javascript

此文章 Markdown 源文件地址:github.com/zxpsuper/bl…css

1、JavaScript基础

前端工程师吃饭的家伙,深度、广度同样都不能差。html

变量和类型

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

JavaScript中的每个值都有它本身的类型,JavaScript规定了七种语言类型,他们是:前端

Undefined Null Boolean String Number Symbol Objectvue

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

对象数据被存储于堆中 (如对象、数组、函数等,它们是经过拷贝和new出来的)。html5

引用类型的数据的地址指针是存储于栈中的,当咱们想要访问引用类型的值的时候,须要先从栈中得到对象的地址指针,而后,在经过地址指针找到堆中的所须要的数据。java

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

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。node

symbol类型的 key 不能被 Object.keysfor..of 循环枚举。所以可看成私有变量使用。react

let mySymbol = Symbol('key');
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};
复制代码
4. JavaScript 中的变量在内存中的具体存储形式

JavaScript 中的变量分为基本类型和引用类型:webpack

基本类型: 保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,经过按值访问

引用类型: 保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript 不容许直接访问堆内存中的位置,所以操做对象时,实际操做对象的引用

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

String(), Number(), Boolean()

装箱:就是把基本类型转变为对应的对象。装箱分为隐式和显示

// 隐式装箱: 每当读取一个基本类型的值时,后台会建立一个该基本类型所对应的对象。
  // 在这个基本类型上调用方法,实际上是在这个基本类型对象上调用方法。
  // 这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后马上被销毁。
  let num=123;
  num.toFixed(2); // '123.00'//上方代码在后台的真正步骤为
  var c = new Number(123);
  c.toFixed(2);
  c = null;
  // 显式装箱: 经过内置对象 Boolean、Object、String 等能够对基本类型进行显示装箱。
  var obj = new String('123');
复制代码

拆箱: 拆箱与装箱相反,把对象转变为基本类型的值。

Number([1]); //1
  // 转换演变:
  [1].valueOf(); // [1];
  [1].toString(); // '1';Number('1'); //1 
复制代码
6. 理解值类型和引用类型

JavaScript中的变量分为基本类型和引用类型:

基本类型: 保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,经过按值访问

引用类型: 保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript 不容许直接访问堆内存中的位置,所以操做对象时,实际操做对象的引用

7. null 和 undefined 的区别
  1. Number 转换的值不一样,Number(null) 输出为 0, Number(undefined) 输出为 NaN

  2. null 表示一个值被定义了,可是这个值是空值

  3. undefined 表示缺乏值,即此处应该有值,可是尚未定义

8. 至少能够说出三种判断 JavaScript 数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
  1. typeof —— 返回给定变量的数据类型,可能返回以下字符串:
'undefined'——Undefined
  'boolean'——Boolean
  'string'——String
  'number'——Number
  'symbol'——Symbol
  'object'——Object / Null (Null 为空对象的引用)
  'function'——Function
  // 对于一些如 error() date() array()没法判断,都是显示object类型
复制代码
  1. instanceof 检测 constructor.prototype 是否存在于参数 object 的原型链上,是则返回 true,不是则返回 false
alert([1,2,3] instanceof Array) // true
  alert(new Date() instanceof Date) // true
  alert(function(){this.name="22";} instanceof Function) //true
  alert(function(){this.name="22";} instanceof function) //false // instanceof 只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪一种类型。 复制代码
  1. constructor —— 返回对象对应的构造函数。
alert({}.constructor === Object);  =>  true
  alert([].constructor === Array);  =>  true
  alert('abcde'.constructor === String);  =>  true
  alert((1).constructor === Number);  =>  true
  alert(true.constructor === Boolean);  =>  true
  alert(false.constructor === Boolean);  =>  true
  alert(function s(){}.constructor === Function);  =>  true
  alert(new Date().constructor === Date);  =>  true
  alert(new Array().constructor === Array);  =>  true
  alert(new Error().constructor === Error);  =>  true
  alert(document.constructor === HTMLDocument);  =>  true
  alert(window.constructor === Window);  =>  true
  alert(Symbol().constructor);    =>    undefined 
  // null 和 undefined 是无效的对象,没有 constructor,所以没法经过这种方式来判断。
复制代码
  1. Object.prototype.toString() 默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,是一个字符串,其中 Xxx 就是对象的类型。
Object.prototype.toString.call(new Date);//[object Date]
  Object.prototype.toString.call(new String);//[object String]
  Object.prototype.toString.call(Math);//[object Math]
  Object.prototype.toString.call(undefined);//[object Undefined]
  Object.prototype.toString.call(null);//[object Null]
  Object.prototype.toString.call('') ;   // [object String]
  Object.prototype.toString.call(123) ;    // [object Number]
  Object.prototype.toString.call(true) ; // [object Boolean]
  Object.prototype.toString.call(Symbol()); //[object Symbol]
  Object.prototype.toString.call(new Function()) ; // [object Function]
  Object.prototype.toString.call(new Date()) ; // [object Date]
  Object.prototype.toString.call([]) ; // [object Array]
  Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
  Object.prototype.toString.call(new Error()) ; // [object Error]
  Object.prototype.toString.call(document) ; // [object HTMLDocument]
  Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
  // 比较全面
复制代码
9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

隐式转换通常说的是 Boolean 的转换

if 语句中,null""undefinded, 0, false 都会被转化为 false

通常应用于对接口数据判空时使用

10. 出现小数精度丢失的缘由,JavaScript 能够存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
  • 精度丢失缘由,说是 JavaScript 使用了 IEEE 754 规范,二进制储存十进制的小数时不能完整的表示小数

  • 可以表示的最大数字 Number.MAX_VALUE 等于 1.7976931348623157e+308 ,最大安全数字 Number.MAX_SAFE_INTEGER 等于 9007199254740991

  • 避免精度丢失

    • 计算小数时,先乘 1001000,变成整数再运算
    • 若是值超出了安全整数,有一个最新提案,BigInt 大整数,它能够表示任意大小的整数,注意只能表示整数,而不受安全整数的限制

原型和原型链

1. 理解原型设计模式以及 JavaScript 中的原型规则
A. 全部的引用类型(数组、对象、函数),都具备对象特性,便可自由扩展属性;
B. 全部的引用类型(数组、对象、函数),都有一个`__proto__`属性(隐式原型),属性值是一个普通的对象;
C. 全部的函数,都具备一个 `prototype`(显式原型),属性值也是一个普通对象;
D. 全部的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;`(obj._proto_ === Object.prototype)`;
E. 当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的 `__proto__` (即它的构造函数的 `prototype`)中去寻找;
复制代码
2. instanceof 的底层实现原理,手动实现一个 instanceof

简单说就是判断实例对象的__proto__是否是强等于对象的prototype属性,若是不是继续往原型链上找,直到 __proto__null 为止。

function instanceOf(obj, object) {//obj 表示实例对象,object 表示对象
  var O = object.prototype;
  obj = obj.__proto__;
  while (true) { 
      if (obj === null) 
          return false; 
      if (O === obj) // 这里重点:当 O 严格等于 obj 时,返回 true 
          return true; 
      obj = obj.__proto__; 
  } 
}
复制代码
3. 理解 JavaScript 的执行上下文栈,能够应用堆栈信息快速定位问题

执行上下文 就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。

执行上下文总共有三种类型:全局执行上下文, 函数执行上下文, Eval 函数执行上下文

执行栈,在其余编程语言中也被叫作调用栈,具备 LIFO(后进先出)结构,用于存储在代码执行期间建立的全部执行上下文。

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

详情请点击:《继承的几种实现方式》

5. 能够描述 new 一个对象的详细过程,手动实现一个 new 操做符
  • new一个对象的详细过程:
function Test() {}
const test = new Test();
复制代码
  1. 建立一个对象 const obj = {}

  2. 设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象

obj.constructor = Test;
obj.__proto__ = Test.prototype;
复制代码
  1. 使用新对象调用函数,函数中的 this 被指向新实例对象 Test.call(obj)

  2. 将初始化完毕的新对象地址,保存到等号左边的变量中

  • 实现一个new操做符
function myNew(Obj,...args){
    var obj = Object.create(Obj.prototype);//使用指定的原型对象及其属性去建立一个新的对象
    Obj.apply(obj,args); // 绑定 this 到obj, 设置 obj 的属性
    return obj; // 返回实例
}
复制代码
6. 理解 es6 class 构造以及继承的底层实现原理
  • ES6 类的底层仍是经过构造函数去建立的。
// es6 Parent类实现
class Parent {
  constructor(name,age){
      this.name = name;
      this.age = age;
  }
  speakSomething(){
      console.log("I can speek chinese");
  }
}
// 转化为
var Parent = function () {
  function Parent(name, age) {
      _classCallCheck(this, Parent); // 判断实例 Parent instanceof Parent(函数)是否为true

      this.name = name;
      this.age = age;
  }
  // 此方法经过使用 Object.defineProperty 为 function Parent 的 prototype 添加属性值
  _createClass(Parent, [{
      key: "speakSomething",
      value: function speakSomething() {
          console.log("I can speek chinese");
      }
  }]);

  return Parent;
}();
复制代码
  • ES6 的继承实现
//定义子类,继承父类
class Child extends Parent {
  static width = 18
  constructor(name,age){
      super(name,age);
  }
  coding(){
      console.log("I can code JS");
  }
}
// 转化为
var Child = function (_Parent) {
  _inherits(Child, _Parent);

  function Child(name, age) {
      _classCallCheck(this, Child);

      return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name, age));
  }

  _createClass(Child, [{
      key: "coding",
      value: function coding() {
          console.log("I can code JS");
      }
  }]);

  return Child;
}(Parent);
复制代码

这里其实就是多了一个 _inherits(Child, _Parent); 方法,实现了如下功能,具体可看文章《ES6类以及继承的实现原理》

//实现的结果是:
  subClass.prototype.__proto__ = superClass.prototype
  subClass.__proto__ = superClass // 实现静态属性的继承
复制代码

做用域和闭包

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

词法做用域也称静态做用域,javascript 采用静态做用域

静态做用域 —— 函数的做用域基于函数建立的位置。

动态做用域 —— 函数的做用域基于函数的使用位置。

var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar(); // 输出 1 。JavaScript 采用的是词法做用域,也称为静态做用域。相同的,动态做用域此代码应该输出 2
复制代码
2. 理解 JavaScript 的做用域和做用域链

做用域(scope)就是变量访问规则的有效范围。

JavaScript 中全局变量的做用域是全局的,在代码的任何地方都是有定义的。然而函数的参数和局部变量只在函数体内有定义。另外局部变量的优先级要高于同名的全局变量,也就是说当局部变量与全局变量重名时,局部变量会覆盖全局变量。

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

this的几种不一样使用场景的取值 + JavaScript 的 this 原理

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

原理:闭包就是可以读取其余函数内部变量的函数。因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成"定义在一个函数内部的函数"。

因此,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。

做用:闭包能够用在许多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。

应用:1. 匿名自执行函数 2. 结果缓存 3. 封装局部变量

参考连接:《学习Javascript闭包(Closure)》

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

堆栈溢出 的产生是因为过多的函数调用,致使调用堆栈没法容纳这些调用的返回地址,通常在递归中产生。堆栈溢出极可能由无限递归(Infinite recursion)产生,但也可能仅仅是过多的堆栈层级.

参考连接:《内存泄漏与避免》

6. 如何处理循环的异步操做
  1. 将异步操做变同步,使用 async/await.
  2. 去掉循环,将循环变成递归

执行机制

1. 为什么 try 里面放 return,finally 还会执行,理解其内部机制

try 语句中,在执行 return 语句时,要返回的结果已经准备好了,就在此时,程序转到 finally 执行了。

在转去以前,try 中先把要返回的结果存放到局部变量中去,执行完 finally 以后,在从中取出返回结果。

所以,即便finally 中对返回的结果进行了改变,可是不会影响返回结果。

它应该使用栈保存返回值。

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

JavaScript 如何实现异步编程:

  1. callback (回调函数) 回调函数表明着,当某个任务处理完,而后须要作的事。好比读取文件,链接数据库,等文件准备好,或数据库链接成功执行编写的回调函数,又好比像一些动画处理,当动画走完,而后执行回调。

  2. 发布订阅模式 顾名思义,即是先订阅了事件,有人一发布事件你就知道了,接着执行后面的操做。

  3. Promise Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件的结果,相比回调函数,Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。

  4. Generator (生成器)函数 Generator 函数是 ES6 提供的一种异步编程解决方案,其行为相似于状态机。

  5. async/await async/await 本质上仍是基于 Generator 函数,能够说是 Generator 函数的语法糖,async 就至关于以前写的run函数(执行Generator函数的函数),而 await 就至关于 yield ,只不过 await 表达式后面只能跟着 Promise 对象,若是不是 Promise 对象的话,会经过 Promise.resolve 方法使之变成 Promise 对象。async 修饰 function,其返回一个 Promise 对象。

《浏览器 Event Loop 机制》

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

宏任务: setTimeout,setInterval,setImmediate (Node独有),requestAnimationFrame (浏览器独有),I/O,UI rendering (浏览器独有)

微任务: process.nextTick (Node独有),Promise,Object.observe,MutationObserver

4. 能够快速分析一个复杂的异步嵌套逻辑,并掌握分析方法
// 执行顺序,先微队列,后宏队列。
console.log(1);
setTimeout(() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
});
new Promise((resolve, reject) => {
  console.log(4)
  setTimeout(() => {
    console.log(10);
  })
  resolve()
}).then(() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
})
setTimeout(() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
})
console.log(7);
复制代码

从头到尾执行一次代码,根据上面分类规则分至不一样队列, new promise( function ) 也是当即执行。setTimeout 的回调函数属于宏队列(macrotask)resolve 的回调函数属于微队列

// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
复制代码
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
复制代码
// 微队列
() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
}
复制代码

优先执行微队列,微队列执行过程当中产生的微队列和宏队列置于队列末尾排序执行,而宏队列产生的微队列和宏队列于新的队列中等待。。

执行微队列:(分类)

// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
复制代码
// 微队列
() => {
  console.log(11)
});
复制代码
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
复制代码

此时新增了一个微队列console.log(11),由于是微队列产生的,继续执行:

// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11)
复制代码
// 微队列-空
复制代码
// 宏队列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
复制代码

执行完微队列后执行宏队列:

// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
复制代码
// 微队列
() => {
  console.log(3)
}
() => {
  console.log(9)
}
复制代码
// 宏队列
() => {
  console.log(8);
}
() => {
  console.log(12);
}
复制代码

接下来执行微队列后宏队列,即:

// 栈区(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
////////
console.log(3)
console.log(9)
////////
console.log(8);
console.log(12);
复制代码
5. 使用 Promise 实现串行
// 一个 promise 的 function
function delay(time) {
 return new Promise((resolve, reject) => {
   console.log(`wait ${time}s`)
   setTimeout(() => {
     console.log('execute');
     resolve()
   }, time * 1000)
 })
}
const arr = [3, 4, 5];
复制代码
  1. reduce
arr.reduce((s, v) => {
 return s.then(() => delay(v))
}, Promise.resolve())
复制代码
  1. async + 循环 + await
(
 async function () {
   for (const v of arr) {
     await delay(v)
   }
 }
)()
复制代码
  1. 普通循环
let p = Promise.resolve()
for (const i of arr) {
 p = p.then(() => delay(i))
}
复制代码
  1. 递归
function dispatch(i, p = Promise.resolve()) {
 if (!arr[i]) return Promise.resolve()
 return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)
复制代码
6. Node 与浏览器 EventLoop 的差别

《JavaScript 运行机制详解:再谈Event Loop》

《带你完全弄懂Event Loop》

7. 如何解决页面加载海量数据而页面不卡顿
  1. 分治思想,在必定的时间内屡次加载数据,直至渲染完成,使用 window.requestAnimationFramedocument.createDocumentFragment() 实现, 可参考文章【如何解决页面加载海量数据而不冻结前端UI】
  2. 局部显示,毕竟用户能看到的就一屏内容,监听用户的滚动行为,改变显示元素,可以使 DOM 结构最简单化。可参考文章【大数据如何在前端流畅展现】,不过他的 Demo有点问题.

语法和API

1. 理解 ECMAScript 和 JavaScript 的关系

ECMAScriptJavaScript 的规范,JavaScriptECMAScript 的实现。

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

【JavaScript 标准参考教程(alpha)】

【ECMAScript 6 入门】

3. setInterval 须要注意的点,使用 settimeout 实现 setInterval
  • setInterval 须要注意的点:

在使用 setInterval 方法时,每一次启动都须要对 setInterval 方法返回的值作一个判断,判断是不是空值,若不是空值,则要中止定时器并将值设为空,再从新启动,若是不进行判断并赋值,有可能会形成计时器循环调用,在同等的时间内同时执行调用的代码,并会随着代码的运行时间增长而增长,致使功能没法实现,甚至占用过多资源而卡死奔溃。所以在每一次使用setInterval方法时,都须要进行一次判断。

let timer = setInterval(func, 1000)
// 在其余地方再次用到setInterval(func, 1000)
if (timer !== null) {
    clearInterval(timer)
    timer = null
}
timer = setInterval(func, 1000)
复制代码
  • 使用 settimeout 实现 setInterval
setIntervalFunc = () =>{
  console.log(1) //使用递归
  setTimeout(setIntervalFunc, 1000);
};
setInterval()
复制代码
4. JavaScript 提供的正则表达式 API、可使用正则表达式(邮箱校验、URL解析、去重等)解决常见问题

邮箱校验:

function isEmail(emailStr) {
    return /^[a-zA-Z0-9]+([._-]*[a-zA-Z0-9]*)*@[a-zA-Z0-9]+.[a-zA-Z0-9{2,5}$]/.test(emailStr);
}
复制代码

URL解析:

function isUrl(urlStr) {
    return /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.%]+$/.test(value)
}
复制代码

数组去重:

// set结构
let arr = [1, 1, 2, 2, 3, 3]
arr2 = [...new Set(arr)]
console.log(arr2) // [1,2,3]

// Object.keys(), 利用属性 key 的惟一性
let arrObj = [1, 1, 2, 2, 3, 3]
arrObj2 = {}
for (i in arrObj) {
    arrObj2[arrObj[i]] = true
}
let arrObj3 = Object.keys(arrObj2)
console.log(arrObj3)

// 利用 indexOf() 查询数组内是否已经包含该元素
var arrIndexOf = ['a','c','b','d','a','b']
var arrIndexOf2 = [];
for(var i = 0;i<arrIndexOf.length;i++){
    if(arrIndexOf2.indexOf(arrIndexOf[i])<0){
        arrIndexOf2.push(arrIndexOf[i]);
    }
}
console.log(arrIndexOf2)// ['a', 'c', 'b', 'd']
复制代码

2、HTML和CSS

HTML

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

语义化标签<header> <footer> <nav> <section> <article> <aside> 等

  • 让页面呈现清晰的结构
  • 屏幕阅读器(若是访客有视障)会彻底根据你的标记来“读”你的网页
  • 搜索引擎的爬虫依赖标签肯定上下文和权重问题
  • 便于团队开发和维护

标签分类

  • 文档标签(10 个):<html>、<head>、<body>、<title>、<meta>、<base> 、<style>、<link>、<script>、<noscript>

  • 表格标签(10 个):<table>、<thead>、<tbody>、<tfoot>、<tr>、<td>、<th> 、<col>、<colgroup>、<caption>

  • 表单标签(10 个):<from>、<input>、<textarea>、<button>、<select> 、<optgroup>、<option>、<label>、<fieldset>、<legend>

  • 列表标签(6个):<ul>、<ol>、<li>、<dl>、<dt>、<dd>

  • 多媒体标签(5个):<img>、<map>、<area>、<object>、<param>

  • 文章标签:<h1> - <h6> 、<p>、<br>、<span>、<bdo>、<pre>、<acronym>、<abbr>、<blockquote>、<q>、<ins>、<del>、<address>

  • 字体样式标签:<tt>、<i>、<b>、<big>、<small>、<em>、<strong>、<dfn>、<code>、<samp>、<kbd>、<var>、<cite>、<sup>、<sub>

    在不一样的场景使用不一样的标签,更能显示清晰的结构。

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

<head> 标签用于定义文档的头部,它是全部头部元素的容器。<head> 中的元素能够引用脚本、指示浏览器在哪里找到样式表、提供元信息等等。能够包含的标签有: <base>, <link>, <meta>, <script>, <style>, 以及 <title>。

<title> 定义文档的标题,它是 head 部分中惟一必需的元素。

<meta>元素可提供有关页面的元信息(meta-information),好比针对搜索引擎和更新频度的描述和关键词。使用方法参考【meta标签详解】

3. HTML5 离线缓存原理
  • 待补充
4. 可使用 Canvas API、SVG 等绘制高性能的动画

CSS

1. CSS 盒模型,在不一样浏览器的差别
  • 标准 w3c 盒子模型的范围包括 margin、border、padding、content,而且 content 部分不包含其余部分
  • ie 盒子模型的范围也包括 margin、border、padding、content,和标准 w3c 盒子模型不一样的是:ie 盒子模型的 content 部分包含了 borderpading
2. CSS 全部选择器及其优先级、使用场景,哪些能够继承,如何运用at规则

不一样级别优先级!important > 行内样式 > ID选择器 > 类选择器 > 元素 > 通配符 > 继承 > 浏览器默认属性

相同级别优先级内联(行内)样式 > 内部样式表 > 外部样式表 > 导入样式(@import)。

可继承属性

字体系列属性, font-family, font-weight, font-size, font-style...
文本系列属性, text-indent, text-align, line-heigh,&emsp;word-spacing, letter-spacing, text-transform, color
元素可见性:visibility, 光标属性:cursor
复制代码

AT rule:

1、什么是 at-rules

eg:@charset "utf-8";

at-ruleCSS 样式声明,以 @ 开头,紧跟着是标识符(charset),最后以分号(;)结尾。

2、几个 at-rules

一、@charset —定义被样式表使用的字符集

二、@import ——告诉 CSS 引擎包含外部的 CSS 样式表

三、@namespace——告诉 CSS 引擎全部的内容都必须考虑使用 XML 命名空间前缀

四、嵌套at-rules

(1)@media——条件组规则。若是设备符合标准定义的条件查询则使用该媒体

(2)@font-face——描述了一个将从外部下载的字体

(3)@keyframes——描述了中间步骤在 CSS 动画的序列

(4)@page——描述了文件的布局变化,当要打印文档时。

(5)@supports——条件组规则,若是浏览器知足给出的规则,则把它应用到内容中

(6)@document——条件组规则,若是被用到文档的 CSS 样式表知足了给定的标准,那么将被应用到全部的内容中。

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

伪类:用于向某些选择器添加特殊的效果. :active, :focus, :link, :visited, :hover, :first-child

伪元素:用于将特殊的效果添加到某些选择器. :before, :after, :first-line, :first-letter

伪类和伪元素的根本区别在于:它们是否创造了新的元素(抽象)。从咱们模仿其意义的角度来看,若是须要添加新元素加以标识的,就是伪元素,反之,若是只须要在既有元素上添加类别的,就是伪类。

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

HTML 文档流的排版规则: 把元素按从上而下,从左到右的顺序默认排列。不在一行的元素从上而下,在一行的从左到右排列。

CSS 几种定位的规则:

  • static 定位(普通流定位)
  • float 定位(浮动定位), 有两个取值:left (左浮动)和 right (右浮动)。

浮动元素会在没有浮动元素的上方,效果上看是遮挡住了没有浮动的元素,有float样式规则的元素是脱离文档流的,它的父元素的高度并不能有它撑开。

  • relative 定位(相对定位), 相对本元素的左上角进行定位,top,left,bottom,right 均可以有值。虽然通过定位后,位置可能会移动,可是本元素并无脱离文档流,还占有原来的页面空间。
  • absolute 定位(绝对定位), 相对于祖代中有 relative (相对定位)而且离本元素层级关系上是最近的元素的左上角进行定位,若是在祖代元素中没有有 relative定位的,就默认相对于body进行定位。绝对定位是脱离文档流的
  • fixed 定位(固定定位),这种定位方式是相对于整个文档的,只需设置它相对于各个方向的偏移值,就能够将该元素固定在页面固定的位置,一般用来显示一些提示信息,脱离文档流;

雪碧图实现原理CSS Sprite,是一种 CSS 图像合并技术,该方法是将小图标和背景图像合并到一张图片上,而后利用 css 的背景定位来显示须要显示的图片部分。

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

参考文章: 【CSS实现水平垂直居中的1010种方式】

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

BFC(Block formatting context) 直译为"块级格式化上下文"。它是一个独立的渲染区域,只有块级元素参与, 它规定了内部的块级元素如何布局,而且与这个区域外部绝不相干。

BCF 能够解决的问题:浮动定位,消除外边距折叠,清除浮动,自适应多栏布局

BFC的建立:根元素或包含根元素的元素,浮动元素(float 不为none),绝对定位元素( positionabsolute 或者 fixed),displayinline-block,table-cell,table-caption,overflow 值不为 visible,弹性元素( flex 布局),网格元素( grid 布局)

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

CSS模块化方案: 文件细化,命名约定,CSS Modules , css in js

如何防止 CSS 阻塞渲染:

CSS 是阻塞渲染的资源。须要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。

有一些 CSS 样式只在特定条件下(例如显示网页或将网页投影到大型显示器上时)使用,咱们能够经过 CSS“媒体类型”和“媒体查询”来解决这类用例:

<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">

首屏相关的关键 `CSS` 使用阻塞渲染的方式加载,全部的非关键 `CSS` 在首屏渲染完成后加载。
复制代码
8. 手写图片瀑布流效果

参考文章:【瀑布流布局的实现】

9. 使用CSS绘制几何图形(圆形、三角形、扇形、菱形等)
// 圆形
.circle{
	width:100px;
	height:100px;
	border-radius:50%;
	background:blue;
}
// 三角形
.triangle {
    width: 0;
    height: 0;
    border: 50px solid blue;
    /* 经过改变边框颜色,能够改变三角形的方向 */
    border-color: blue transparent transparent transparent;
}
// 扇形,扇形是由一个圆形和一个矩形进行组合获得的,用矩形遮住圆形的一部分就造成了扇形。
.sector {
    width: 142px;
    height: 142px;
    background: #fff;
    border-radius: 50%;
    background-image: linear-gradient(to right, transparent 50%, #655 0);
}

.sector::before {
    content: '';
    display: block;
    margin-left: 50%;
    height: 100%;
	width: 100%;
    background-color: inherit;
    transform-origin: left;
	/*调整角度,改变扇形大小*/
    transform: rotate(230deg);
}
// 菱形
.rhombus {
    width: 200px;
    height: 200px;
    transform: rotateZ(45deg) skew(30deg, 30deg);
    background: blue;
}
复制代码
10. 使用纯 CSS 实现曲线运动(贝塞尔曲线)

CSS3 新增了 transition-timing-function 属性,它的取值就能够设置为一个三次贝塞尔曲线方程。

参考文章: 【贝塞尔曲线的css实现——淘宝加入购物车基础动画】

11. 实现经常使用布局(三栏、圣杯、双飞翼、吸顶),说出多种方式并理解其优缺点

圣杯布局, 两边顶宽,中间自适应的三栏布局。

  • 期待评论补充

3、计算机基础

关于编译原理,不须要理解很是深刻,可是最基本的原理和概念必定要懂,这对于学习一门编程语言很是重要

编译原理

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

代码就是程序员用开发工具所支持的语言写出来的源文件,是一组由字符、符号或信号码元以离散形式表示信息的明确的规则体系。

计算机源代码最终目的是将人类可读文本翻译成为计算机可执行的二进制指令,这种过程叫编译,它由经过编译器完成。

2. 正则表达式的匹配原理和性能优化
  • 待补充
3. 如何将JavaScript代码解析成抽象语法树(AST)
  • 待补充
4. base64 的编码原理
  • 待补充
5. 几种进制的相互转换计算方法,在 JavaScript 中如何表示和转换

parseInt(str, radix) 将一个 radix 进制的 str 转化为十进制,parseInt('23',8) // 19,将八进制的‘23’转化为10进制的‘19’

number.toString(radix) 将一个数字转化为 radix 进制的数字字符串

0x11.toString(8) // 21
0x11.toString(10) // 17
0x11.toString(2) // 10001
复制代码

网络协议

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

协议,网络协议的简称,网络协议是通讯计算机双方必须共同听从的一组约定。如怎么样创建链接、怎么样互相识别等。只有遵照这个约定,计算机之间才能相互通讯交流。它的三要素是:语法、语义、时序。

TCP/IP 网络协议族的构成: TCP/IP 协议是 Internet 最基本的协议。由传输层的 TCP 协议和网络层的 IP 协议组成。

TCP 负责发现传输的问题,一有问题就发出信号,要求从新传输,直到全部数据安全正确地传输到目的地。而 IP 是给因特网的每一台联网设备规定一个地址。

应用层

应用层决定了向用户提供应该服务时通讯的活动。

TCP/IP 协议族内预存了各种通用的应用服务。好比,FTP(File Transfer Protocol,文件传输协议)和 DNS(Domain Name System,域名系统)服务就是其中的两类。HTTP 协议也处于该层。

传输层

传输层对上层应用层,提供处于网络链接中两台计算机之间的数据传输。

在传输层有两个性质不一样的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。

网络层(又名网络互连层)

网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了经过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。

与对方计算机之间经过多台计算机或网络设备进行传输时,网络层所起的所用就是在众多的选项内选择一条传输路线。

链路层(又名数据链路层,网络接口层)

用来处理链接网络的硬件部分。包括控制操做系统、硬件的设备驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括链接器等一切传输媒介)。硬件上的范畴均在链路层的做用范围以内。

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

三次握手和四次挥手详细原理:

三次握手:避免链接请求的数据包丢失,数据传输过程由于网络并发量很大在某结点被阻塞

四次挥手: TCP链接是全双工通道,须要双向关闭。

参考文章: 【TCP/IP协议族】

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

TCP的协议:FTP(文件传输协议)、Telnet(远程登陆协议)、SMTP(简单邮件传输协议)、POP3(和 SMTP 相对,用于接收邮件)、HTTP 协议等。

TCP 提供可靠的、面向链接的数据传输服务。使用 TCP 通讯以前,须要进行“三次握手”创建链接,通讯结束后还要使用“四次挥手”断开链接。

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

DNS 的做用:DNS是互联网的一项服务。它做为将域名和IP地址相互映射的一个分布式数据库,可以令人更方便地访问互联网。

DNS解析过程

一、在浏览器中输入 www.qq.com 域名,操做系统会先检查本身本地的 hosts 文件是否有这个网址映射关系,若是有,就先调用这个 IP 地址映射,完成域名解析。

二、若是 hosts 里没有这个域名的映射,则查找本地 DNS解析器缓存,是否有这个网址映射关系,若是有,直接返回,完成域名解析。

三、若是 hosts本地DNS解析器缓存 都没有相应的网址映射关系,首先会找 TCP/ip 参数中设置的 首选DNS服务器,在此咱们叫它 本地DNS服务器,此服务器收到查询时,若是要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具备权威性。

四、若是要查询的域名,不禁 本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个 IP 地址映射,完成域名解析,此解析不具备权威性。

五、若是 本地DNS服务器 本地区域文件与缓存解析都失效,则根据 本地DNS服务器 的设置(是否设置转发器)进行查询,若是未用转发模式,本地 DNS 就把请求发至 13 台根 DNS,根 DNS 服务器收到请求后会判断这个域名 (.com) 是谁来受权管理,并会返回一个负责该顶级域名服务器的一个IP本地DNS服务器 收到 IP 信息后,将会联系负责 .com 域的这台服务器。这台负责 .com 域的服务器收到请求后,若是本身没法解析,它就会找一个管理 .com 域的下一级 DNS 服务器地址(http://qq.com)本地DNS服务器。当 本地DNS服务器 收到这个地址后,就会找 http://qq.com 域服务器,重复上面的动做,进行查询,直至找到 www.qq .com 主机。

六、若是用的是转发模式,此 DNS 服务器就会把请求转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器若是不能解析,或找根 DNS 或把转请求转至上上级,以此循环。无论是 本地DNS服务器 用是是转发,仍是根提示,最后都是把结果返回给 本地DNS服务器,由此 DNS 服务器再返回给客户机。

DNS 优化:减小DNS的请求次数;进行DNS预获取 。

减小DNS的请求次数————在项目中减小不一样域名的http请求,尽可能少的域名减小DNS的请求数

DNS预获取————减小用户的等待时间,提高用户体验 。

默认状况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,而且缓存结果,这就是隐式的 DNS Prefetch。若是想对页面中没有出现的域进行预获取,那么就要使用显示的 DNS Prefetch 了。

<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//www.itechzero.com">
<link rel="dns-prefetch" href="//api.share.baidu.com">
<link rel="dns-prefetch" href="//bdimg.share.baidu.com">
复制代码
5. CDN的做用和原理
  • CDN的做用

CDN 的全称是 Content Delivery Network,即内容分发网络。其基本思路是尽量避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。

  • CDN 的原理 一、多域名加载资源

通常状况下,浏览器都会对单个域名下的并发请求数(文件加载)进行限制,一般最多有 4 个,那么第 5 个加载项将会被阻塞,直到前面的某一个文件加载完毕。由于 CDN 文件是存放在不一样区域(不一样 IP)的,因此对浏览器来讲是能够同时加载页面所需的全部文件(远不止 4 个),从而提升页面加载速度。

二、文件可能已经被加载过并保存有缓存

一些通用的 js 库或者是 css 样式库,如 jQuery ,在网络中的使用是很是广泛的。当一个用户在浏览你的某一个网页的时候,颇有可能他已经经过你网站使用的 CDN 访问过了其余的某一个网站,恰巧这个网站一样也使用了 jQuery,那么此时用户浏览器已经缓存有该 jQuery 文件(同 IP 的同名文件若是有缓存,浏览器会直接使用缓存文件,不会再进行加载),因此就不会再加载一次了,从而间接的提升了网站的访问速度。

三、分布式的数据中心

假如你的站点布置在北京,当一个香港或者更远的用户访问你的站点的时候,他的数据请求势必会很慢很慢。而 CDN 则会让用户从离他最近的节点去加载所需的文件,因此加载速度提高就是理所固然的了。

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

参考文章:【HTTP 请求详解】

HTTP协议的六种请求方法

  1. GET: 发送请求来得到服务器上的资源,请求体中不会包含请求数据,请求数据放在协议头中

  2. POST: 和 get 同样很常见,向服务器提交资源让服务器处理,好比提交表单、上传文件等,可能致使创建新的资源或者对原有资源的修改。提交的资源放在请求体中。不支持快取。非幂等

  3. HEAD: 本质和 get 同样,可是响应中没有呈现数据,而是 http 的头信息,主要用来检查资源或超连接的有效性或是否能够可达、检查网页是否被串改或更新,获取头信息等,特别适用在有限的速度和带宽下。

  4. PUT: 和 post 相似,html 表单不支持,发送资源与服务器,并存储在服务器指定位置,要求客户端事先知道该位置;好比 post 是在一个集合上(/province),而 put 是具体某一个资源上(/province/123)。因此 put 是安全的,不管请求多少次,都是在 123 上更改,而 post 可能请求几回建立了几回资源。幂等

  5. DELETE: 请求服务器删除某资源。和 put 都具备破坏性,可能被防火墙拦截

  6. CONNECT: HTTP/1.1 协议中预留给可以将链接改成管道方式的代理服务器。就是把服务器做为跳板,去访问其余网页而后把数据返回回来,链接成功后,就能够正常的 getpost 了。

7: OPTIONS: 获取 http 服务器支持的 http 请求方法,容许客户端查看服务器的性能,好比 ajax 跨域时的预检等。 8: TRACE: 回显服务器收到的请求,主要用于测试或诊断。通常禁用,防止被恶意攻击或盗取信息。

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

    • 100 Continue 继续,通常在发送post请求时,已发送了http header以后服务端将返回此信息,表示确认,以后发送具体参数信息
  • 2XX:成功状态码

    • 200 OK 正常返回信息
    • 201 Created 请求成功而且服务器建立了新的资源
    • 202 Accepted 服务器已接受请求,但还没有处理
  • 3XX:重定向

    • 301 Moved Permanently 请求的网页已永久移动到新位置。
    • 302 Found 临时性重定向。
    • 303 See Other 临时性重定向,且老是使用 GET 请求新的 URI。
    • 304 Not Modified 自从上次请求后,请求的网页未修改过。
  • 4XX:客户端错误

    • 400 Bad Request 服务器没法理解请求的格式,客户端不该当尝试再次使用相同的内容发起请求。
    • 401 Unauthorized 请求未受权。
    • 403 Forbidden 禁止访问。
    • 404 Not Found 找不到如何与 URI 相匹配的资源。
  • 5XX: 服务器错误

    • 500 Internal Server Error 最多见的服务器端错误。
    • 503 Service Unavailable 服务器端暂时没法处理请求(多是过载或维护)。
8. HTTP1.一、HTTP2.0带来的改变

缓存处理,在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires 来作为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。

带宽优化及网络链接的使用HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是须要某个对象的一部分,而服务器却将整个对象送过来了,而且不支持断点续传功能,HTTP1.1 则在请求头引入了 range 头域,它容许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和链接。

错误通知的管理,在 HTTP1.1 中新增了24 个错误状态响应码,如 409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

Host头处理,在 HTTP1.0 中认为每台服务器都绑定一个惟一的 IP 地址,所以,请求消息中的 URL 并无传递主机名 (hostname)。但随着虚拟主机技术的发展,在一台物理服务器上能够存在多个虚拟主机(Multi-homed Web Servers),而且它们共享一个 IP 地址。HTTP1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中若是没有 Host 头域会报告一个错误(400 Bad Request)

长链接HTTP 1.1 支持长链接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个 TCP 链接上能够传送多个 HTTP 请求和响应,减小了创建和关闭链接的消耗和延迟,在 HTTP1.1 中默认开启 Connection: keep-alive,必定程度上弥补了 HTTP1.0 每次请求都要建立链接的缺点。

HTTP2.0和HTTP1.X相比的新特性

  • 新的二进制格式(Binary Format),HTTP1.x 的解析是基于文本。基于文本协议的格式解析存在自然缺陷,文本的表现形式有多样性,要作到健壮性考虑的场景必然不少,二进制则不一样,只认 01 的组合。基于这种考虑 HTTP2.0 的协议解析决定采用二进制格式,实现方便且健壮。

  • 多路复用(MultiPlexing),即链接共享,即每个 request 都是是用做链接共享机制的。一个 request 对应一个 id,这样一个链接上能够有多个 request,每一个链接的 request 能够随机的混杂在一块儿,接收方能够根据 requestidrequest 再归属到各自不一样的服务端请求里面。

  • header 压缩,如上文中所言,对前面提到过 HTTP1.xheader 带有大量信息,并且每次都要重复发送,HTTP2.0 使用 encoder 来减小须要传输的 header 大小,通信双方各自 cache 一份 header fields 表,既避免了重复 header 的传输,又减少了须要传输的大小。

  • 服务端推送(server push),HTTP2.0具备 server push 功能。

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

参考文章:【一个故事讲完 https】

设计模式

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

参考文章:【设计模式】

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

观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

// 观察者模式
class Subject{
  constructor(){
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(){
    this.subs.forEach(sub=> {
      sub.update();
    });
  }
}
class Observer{
  update(){
    console.log('update');
  }
}
let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify();   //update
复制代码
// 发布订阅者模式
class PubSub {
    constructor() {
        this.subscribers = {}
    }
    subscribe(type, fn) {
        if (!Object.prototype.hasOwnProperty.call(this.subscribers, type)) {
          this.subscribers[type] = [];
        }
        
        this.subscribers[type].push(fn);
    }
    unsubscribe(type, fn) {
        let listeners = this.subscribers[type];
        if (!listeners || !listeners.length) return;
        this.subscribers[type] = listeners.filter(v => v !== fn);
    }
    publish(type, ...args) {
        let listeners = this.subscribers[type];
        if (!listeners || !listeners.length) return;
        listeners.forEach(fn => fn(...args));        
    }
}

let ob = new PubSub();
ob.subscribe('add', (val) => console.log(val));
ob.publish('add', 1);
复制代码

4、数据结构和算法

据我了解的大部分前端对这部分知识有些欠缺,甚至抵触,可是,若是突破更高的天花板,这部分知识是必不可少的,并且我亲身经历——很是有用!

JavaScript编码能力

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

参考文章:【JS 数组去重方法整理】

【5种方式实现数组扁平化】

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

参考文章:【递归实现深拷贝】

3. 手写函数柯里化工具函数、并理解其应用场景和优点
  • 待补充
4.手写防抖和节流工具函数、并理解其内部原理和应用场景

参考文章:【函数的防抖与节流】

5.实现一个 sleep 函数
function sleep(time) {
  return new Promise((resolve,reject) => setTimeout(resolve, time))
}
sleep(3000).then(() => {console.log('沉睡3000ms')})
复制代码

手动实现前端轮子

1. 手动实现call、apply、bind

call

  1. 判断当前 this 是否为函数,防止 Function.prototype.myCall() 直接调用
  2. context 为可选参数,若是不传的话默认上下文为 window
  3. context 建立一个 Symbol(保证不会重名)属性,将当前函数赋值给这个属性
  4. 处理参数,传入第一个参数后的其他参数
  5. 调用函数后即删除该 Symbol 属性
Function.prototype.myCall = function(context = window, ...args) {
    if (this === Function.prototype) {
        return undefined; // 用于防止 Function.prototype.myCall() 直接调用
    }
    context = context || window;
    const fn = Symbol();
    context[fn] = this;
    const result = context[fn](...args);
    delete context[fn];
    return result;
};
复制代码

apply

apply 实现相似 call,参数为数组

Function.prototype.myApply = function(context = window, args) {
    if (this === Function.prototype) {
        return undefined; // 用于防止 Function.prototype.myCall() 直接调用
    }
    const fn = Symbol();
    context[fn] = this;
    let result;
    if (Array.isArray(args)) {
        result = context[fn](...args);
    } else {
        result = context[fn]();
    }
    delete context[fn];
    return result;
};
复制代码

bind

由于 bind() 返回一个方法需手动执行,所以利用闭包实现。

Function.prototype.myBind = function(context, ...args1) {
    if (this === Function.prototype) {
        throw new TypeError('Error');
    }
    const _this = this;
    return function F(...args2) {
        // 判断是否用于构造函数
        if (this instanceof F) {
            return new _this(...args1, ...args2);
        }
        return _this.apply(context, args1.concat(args2));
    };
};
复制代码
2.手动实现符合 Promise/A+ 规范的 Promise

参考文章:【手动实现 promise】

3. 手写一个 EventEmitter 实现事件发布、订阅
function EventEmitter() {
      this._events = Object.create(null);
    }

    // 向事件队列添加事件
    // prepend为true表示向事件队列头部添加事件
    EventEmitter.prototype.addListener = function (type, listener, prepend) {
      if (!this._events) {
        this._events = Object.create(null);
      }
      if (this._events[type]) {
        if (prepend) {
          this._events[type].unshift(listener);
        } else {
          this._events[type].push(listener);
        }
      } else {
        this._events[type] = [listener];
      }
    };

    // 移除某个事件
    EventEmitter.prototype.removeListener = function (type, listener) {
      if (Array.isArray(this._events[type])) {
        if (!listener) {
          delete this._events[type]
        } else {
          this._events[type] = this._events[type].filter(e => e !== listener && e.origin !== listener)
        }
      }
    };

    // 向事件队列添加事件,只执行一次
    EventEmitter.prototype.once = function (type, listener) {
      const only = (...args) => {
        listener.apply(this, args);
        this.removeListener(type, listener);
      }
      only.origin = listener;
      this.addListener(type, only);
    };

    // 执行某类事件
    EventEmitter.prototype.emit = function (type, ...args) {
      if (Array.isArray(this._events[type])) {
        this._events[type].forEach(fn => {
          fn.apply(this, args);
        });
      }
    };
    // 测试一下
    var emitter = new EventEmitter();

    var onceListener = function (args) {
      console.log('我只能被执行一次', args, this);
    }

    var listener = function (args) {
      console.log('我是一个listener', args, this);
    }

    emitter.once('click', onceListener);
    emitter.addListener('click', listener);

    emitter.emit('click', '参数');
    emitter.emit('click');

    emitter.removeListener('click', listener);
    emitter.emit('click');
复制代码
4.能够说出两种实现双向绑定的方案、能够手动实现

参考文章:【Vue 双向数据绑定原理】

5.手写JSON.stringify、JSON.parse
let Myjson = {
      parse: function(jsonStr) {
          return eval('(' + jsonStr + ')');
      },
      stringify: function(jsonObj) {
          var result = '',
              curVal;
          if (jsonObj === null) {
              return String(jsonObj);
          }
          switch (typeof jsonObj) {
              case 'number':
              case 'boolean':
                  return String(jsonObj);
              case 'string':
                  return '"' + jsonObj + '"';
              case 'undefined':
              case 'function':
                  return undefined;
          }

          switch (Object.prototype.toString.call(jsonObj)) {
              case '[object Array]':
                  result += '[';
                  for (var i = 0, len = jsonObj.length; i < len; i++) {
                      curVal = JSON.stringify(jsonObj[i]);
                      result += (curVal === undefined ? null : curVal) + ",";
                  }
                  if (result !== '[') {
                      result = result.slice(0, -1);
                  }
                  result += ']';
                  return result;
              case '[object Date]':
                  return '"' + (jsonObj.toJSON ? jsonObj.toJSON() : jsonObj.toString()) + '"';
              case '[object RegExp]':
                  return "{}";
              case '[object Object]':
                  result += '{';
                  for (i in jsonObj) {
                      if (jsonObj.hasOwnProperty(i)) {
                          curVal = JSON.stringify(jsonObj[i]);
                          if (curVal !== undefined) {
                              result += '"' + i + '":' + curVal + ',';
                          }
                      }
                  }
                  if (result !== '{') {
                      result = result.slice(0, -1);
                  }
                  result += '}';
                  return result;

              case '[object String]':
                  return '"' + jsonObj.toString() + '"';
              case '[object Number]':
              case '[object Boolean]':
                  return jsonObj.toString();
          }
      }
  };
复制代码
6. 手写懒加载效果

参考文章:【图片懒加载】

浏览器原理

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

参考文章:【输入URL至页面渲染】

2. 浏览器的垃圾回收机制,如何避免内存泄漏

参考文章:【浏览器内存回收机制】

参考文章:【内存泄漏与避免】

资源推荐

语言基础

计算机基础

数据结构和算法

运行环境

框架和类库

前端工程

项目和业务

学习提高

博客推荐

技术以外

此文章 Markdown 源文件地址:github.com/zxpsuper/bl…

相关文章
相关标签/搜索