前端知识点汇总——面试看这一篇就够了

一年前写了一篇JavaScript八张思惟导图,主要是对前端JavaScript知识点的一个系统的整理和总结。本篇文章用了近一个月时间,搜集整理了网上各类面试笔试题及本人针对前端的一些理解以及各路大神针对前端难点部分的详细介绍,能够做为之后面试或者考察面试人员的参考。 相信经过这两篇文章的学习,必定会让你对前端有一个更深的认识。javascript

后续会持续更新......css

JavaScript篇

介绍JS的基本数据类型

string number boolean undefined nullhtml

介绍JS有哪些内置对象

  • 数据封装类对象: String Number Boolean Array Object
  • 其余对象: Function Math Date Error Arguments RegExp

宿主对象和原生对象的区别

  • 原生对象(native object)是由ECMAScript规范定义的JavaScript内置对象,如 String Number Boolean Array Object Function Math Date Error Arguments RegExp等。
  • 宿主对象(host object)是由运行时的环境(浏览器或node)决定的,如window、XMLHTTPRequest等。

null、undefined及未声明变量之间的区别。如何区分?

  • 未声明变量默认值为undefined
  • typeof null === 'object' // true
  • typeof undefined === 'undefined' // true

==和===的区别

  • ==比较以前会先进行类型转换,即不会对类型进行比较。例如:前端

    12 == '12'  // true
      true == 1   // true
      false == '0' // true
    复制代码
  • ===会比较数值和类型。例如:vue

    12 === '12' // false
      12 === 12 // true
      true === 1 // false
      false === '0' // false
    复制代码

JS隐式转换及应用场景

JS在使用运算符号或者对比符时,会自带隐式转换,规则以下:java

  • -、*、/、% :一概转换成数值后计算node

  • +:jquery

    • 数字 + 字符串 = 字符串, 运算顺序是从左到右
    • 数字 + 对象, 优先调用对象的valueOf -> toString
    • 数字 + boolean/null -> 数字
    • 数字 + undefined -> NaN
  • [1].toString() === '1'nginx

  • {}.toString() === '[object object]'git

  • NaN !== NaN 、+undefined 为 NaN

  • juejin.im/post/5a7172…

"Attribute"和"Property"的区别

"Attribute"是在HTML中定义的,而"property"是在DOM上定义的。为了说明区别,假设咱们在HTML中有一个文本框:
<input type="text" value="Hello">

const input = document.querySelector('input');
console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello
复制代码

可是在文本框中键入“ World!”后:

console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello World!
复制代码

NaN是什么?如何判断是不是NaN类型

  • 定义: 全局属性 NaN 的值表示不是一个数字(Not-A-Number)

  • 如何判断一个值是不是NaN: 等号运算符(== 和 ===) 不能被用来判断一个值是不是 NaN。必须使用 Number.isNaN() 或 isNaN() 函数。

    NaN === NaN;        // false
      Number.NaN === NaN; // false
      isNaN(NaN);         // true
      isNaN(Number.NaN);  // true
    复制代码

如何判断两个对象相等

须要考虑三个问题:

  1. 若是对象的属性值之一自己就是一个对象
  2. 若是属性值中的一个是NaN(在JavaScript中,是否是等于本身惟一的价值?)
  3. 若是一个属性的值为undefined,而另外一个对象没有这个属性(于是计算结果为不肯定?)

检查对象的“值相等”的一个强大的方法,最好是依靠完善的测试库,涵盖了各类边界状况。Underscore和Lo-Dash有一个名为_.isEqual()方法,用来比较好的处理深度对象的比较。您可使用它们像这样:

// Outputs: true
console.log(_.isEqual(obj1, obj2));
复制代码

什么是'user strict',使用它有什么优缺点?

'use strict' 是用于对整个脚本或单个函数启用严格模式的语句。严格模式是可选择的一个限制 JavaScript 的变体一种方式 。
优势:

  • 没法再意外建立全局变量。
  • 会使引发静默失败(silently fail,即:不报错也没有任何效果)的赋值操抛出异常。
  • 试图删除不可删除的属性时会抛出异常(以前这种操做不会产生任何效果)。
  • 要求函数的参数名惟一。
  • 全局做用域下,this的值为undefined。
  • 捕获了一些常见的编码错误,并抛出异常。
  • 禁用使人困惑或欠佳的功能。

缺点:

  • 缺失许多开发人员已经习惯的功能。
  • 没法访问function.caller和function.arguments。
  • 以不一样严格模式编写的脚本合并后可能致使问题。

总的来讲,我认为利大于弊,我历来不使用严格模式禁用的功能,所以我推荐使用严格模式。

call,apply和bind的做用是什么?二者区别是什么?

.call和.apply都用于调用函数,第一个参数将用做函数内 this 的值。然而,.call接受逗号分隔的参数做为后面的参数,而.apply接受一个参数数组做为后面的参数。一个简单的记忆方法是,从call中的 C 联想到逗号分隔(comma-separated),从apply中的 A 联想到数组(array)。

function add(a, b) {
return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3
复制代码

.call和.apply是当即执行的, .bind 返回函数的副本,但带有绑定上下文!它不是当即执行的。

const person = { name: 'Lydia' }

function sayHi(age) {
console.log(`${this.name} is ${age}`)
}

sayHi.call(person, 21)
sayHi.bind(person, 21)

结果: Lydia is 21 function
复制代码

请说明Function.prototype.bind的用法

摘自MDN:

bind()方法建立一个新的函数, 当被调用时,将其 this 关键字设置为提供的值,在调用新函数时,在任何提供以前提供一个给定的参数序列。

根据个人经验,将this的值绑定到想要传递给其余函数的类的方法中是很是有用的。在 React 组件中常常这样作。

如何判断是否为空数组

var arr = [];
if (Array.isArray(arr) && arr.length === 0) {
    console.log('是空数组');
}
// Array.isArray是ES5提供的,若是不支持。用下面的方案。
if (!Array.isArray) {
    Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
}
复制代码

数组方法

  • map: 遍历数组,返回回调返回值组成的新数组
  • forEach: 没法break,能够用try/catch中throw new Error来中止
  • filter: 过滤
  • some: 有一项返回true,则总体为true
  • every: 有一项返回false,则总体为false
  • join: 经过指定链接符生成字符串
  • sort(fn) / reverse: 排序与反转,改变原数组
  • concat: 链接数组,不影响原数组, 浅拷贝
  • slice(start, end): 返回截断后的新数组,不改变原数组
  • splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
  • indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
  • reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)

数组乱序:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});
复制代码

数组拆解:

// flat: [1,[2,3]] --> [1, 2, 3]

Array.prototype.flat = function() {
    return this.toString().split(',').map(item => +item )
}
复制代码

push、pop、shift、unshift功能及返回值

  • push:添加新元素到数组末尾,返回数组长度
  • pop:移除数组最后一个元素,返回元素自己
  • unshift:添加新元素到数组开头,返回数组长度
  • shift:移除数组首个元素,返回元素自己

以上4种操做均会改变数组自己

.forEach和.map()循环的主要区别,使用场景举例

map用法:

let array = [1, 2, 3, 4, 5];
let newArray = array.map((item, i, arr) => {
    return item * 2;
});
console.log("array:", array);       // [1, 2, 3, 4, 5]
console.log("newArray:", newArray); // [2, 4, 6, 8, 10]
// 此处的array接受map方法运算以后的返回值
// 可是map方法并不能改变原来的数组
复制代码

forEach用法:

let array = [1, 2, 3, 4, 5];
let newArray = array.forEach((item, i, arr) => {
    console.log('item:' + item + ', index:' + i);
    // array[i] = item * 2;    // 能够用这种方式改变原始数组的值
});
console.log("array:", array);       // [1, 2, 3, 4, 5]
console.log("newArray:", newArray); // undefined
// forEach方法没有返回值
复制代码
  • 能用forEach()作到的,map()一样能够。反过来也是如此。
  • map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
  • forEach()容许callback更改原始数组的元素。map()返回新的数组。

JS执行对象查找时,永远不会去查找原型的函数是哪一个?

Object​.prototype​.has​OwnProperty()
复制代码

如何将arguments转为数组

  1. Array.prototype.slice.call(arguments)
  2. [].slice.call(arguments)

对象的遍历方法

  • for循环

    for (let property in obj) {
          console.log(property);
      }
    复制代码

    可是,这还会遍历到它的继承属性,在使用以前,你须要加入obj.hasOwnProperty(property)检查。

  • Object.keys()

    Object.keys(obj).forEach((property) => { ... })
    复制代码

    Object.keys()方法会返回一个由一个给定对象的自身可枚举属性组成的数组。

  • Object.getOwnPropertyNames()

    Object.getOwnPropertyNames(obj).forEach((property) => { ... })
    复制代码

    Object.getOwnPropertyNames()方法返回一个由指定对象的全部自身属性的属性名(包括不可枚举属性但不包括 Symbol 值做为名称的属性)组成的数组。

数组的遍历方法

  • for loops

    for (let i = 0; i < arr.length; i++) { ... }
    复制代码
  • forEach

    arr.forEach((item, index, arr) { ... })
    复制代码
  • map

    arr.map((item, index, arr) => { ... })
    复制代码

匿名函数的典型应用场景

匿名函数能够在 IIFE 中使用,来封装局部做用域内的代码,以便其声明的变量不会暴露到全局做用域。

(function() {
    // 一些代码。
})();
复制代码

匿名函数能够做为只用一次,不须要在其余地方使用的回调函数。当处理函数在调用它们的程序内部被定义时,代码具备更好地自闭性和可读性,能够省去寻找该处理函数的函数体位置的麻烦。

setTimeout(function() {
    console.log('Hello world!');
}, 1000);
复制代码

匿名函数能够用于函数式编程或 Lodash(相似于回调函数)。

const arr = [1, 2, 3];
const double = arr.map(function(el) {
    return el * 2;
});
console.log(double); // [2, 4, 6]
复制代码

IIFE(当即执行函数)的用法

IIFE( 当即调用函数表达式)是一个在定义时就会当即执行的 JavaScript 函数。

(function () {
    statements
})();
复制代码

这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法做用域。这不只避免了外界访问此 IIFE 中的变量,并且又不会污染全局做用域。

第二部分再一次使用 () 建立了一个当即执行函数表达式,JavaScript 引擎到此将直接执行函数。

document的load事件和DOMContentLoaded事件之间的区别

当初始的 HTML 文档被彻底加载和解析完成以后,DOMContentLoaded事件被触发,而无需等待样式表、图像和子框架的完成加载。

window的load事件仅在 DOM 和全部相关资源所有完成加载后才会触发。

数据类型判断方式有几种

下面将对以下数据进行判断它们的类型

var bool = true
var num = 1
var str = 'abc'
var und = undefined
var nul = null
var arr = [1, 2, 3]
var obj = {a: 'aa', b: 'bb'}
var fun = function() {console.log('I am a function')}
复制代码
  • typeof: 适合基本的数据类型和函数

    console.log(typeof bool);   // boolean
      console.log(typeof num);    // number
      console.log(typeof str);    // string
      console.log(typeof und);    // undefined
      console.log(typeof nul);    // object
      console.log(typeof arr);    // object
      console.log(typeof obj);    // object
      console.log(typeof fun);    // function
    复制代码

    由结果可知typeof能够测试出number、string、boolean、undefined及function,而对于null及数组、对象,typeof均检测出为object,不能进一步判断它们的类型。

  • instanceof: 判断对象类型,基于原型链去判断。
    obj instanceof Object: 左操做数是一个对象,右操做数是一个函数构造器或者函数对象,判断左边的操做数的原型链_proto_属性是否有右边这个函数对象的proptotype属性。

    console.log(bool instanceof Boolean);// false
      console.log(num instanceof Number); // false
      console.log(str instanceof String); // false
      console.log(und instanceof Object); // false
      console.log(arr instanceof Array);  // true
      console.log(nul instanceof Object); // false
      console.log(obj instanceof Object); // true
      console.log(fun instanceof Function);// true
    
      var bool2 = new Boolean()
      console.log(bool2 instanceof Boolean);// true
    
      var num2 = new Number()
      console.log(num2 instanceof Number);// true
    
      var str2 = new String()
      console.log(str2 instanceof String);//  true
    
      function Animation(){}
      var ani = new Animation()
      console.log(ani instanceof Animation);// true
    
      function Dog(){}
      Dog.prototype = new Animation()
      var dog = new Dog()
      console.log(dog instanceof Dog);    // true
      console.log(dog instanceof Animation);// true
      console.log(dog instanceof Object); // true
    复制代码

    从结果中看出instanceof不能区别undefined和null,并且对于基本类型若是不是用new声明的则也测试不出来,对因而使用new声明的类型,它还能够检测出多层继承关系。

  • constructor: 返回对建立此对象的函数的引用

    console.log(bool.constructor === Boolean);  // true
      console.log(num.constructor === Number);    // true
      console.log(str.constructor === String);    // true
      console.log(arr.constructor === Array);     // true
      console.log(obj.constructor === Object);    // true
      console.log(fun.constructor === Function);  // true
    
      console.log(ani.constructor === Animation); // true
      console.log(dog.constructor === Dog);       // false
      console.log(dog.constructor ===  Animation);// true
    复制代码

    null 和 undefined 是无效的对象,所以是不会有 constructor 存在的,这两种类型的数据须要经过其余方式来判断。

    函数的 constructor 是不稳定的,这个主要体如今自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失。因此dog.constructor === Animation 而不是 Dog

  • Object.prototype.toString.call

    console.log(Object.prototype.toString.call(bool));  //[object Boolean]
      console.log(Object.prototype.toString.call(num));   //[object Number]
      console.log(Object.prototype.toString.call(str));   //[object String]
      console.log(Object.prototype.toString.call(und));   //[object Undefined]
      console.log(Object.prototype.toString.call(nul));   //[object Null]
      console.log(Object.prototype.toString.call(arr));   //[object Array]
      console.log(Object.prototype.toString.call(obj));   //[object Object]
      console.log(Object.prototype.toString.call(fun));   //[object Function]
    
      console.log(Object.prototype.toString.call(dog));   //[object Object]  
    复制代码

    原理(摘自高级程序设计3):在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。每一个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。 可是它不能检测非原生构造函数的构造函数名。

  • 使用jquery中的$.type

    console.log($.type(bool));  //boolean
      console.log($.type(num));   //number
      console.log($.type(str));   //string
      console.log($.type(und));   //undefined
      console.log($.type(nul));   //null
      console.log($.type(arr));   //array
      console.log($.type(obj));   //object
      console.log($.type(fun));   //function
    
      console.log($.type(dog));   //object
    复制代码

    $.type()内部原理就是用的Object.prototype.toString.call()

DOM操做(增删改查)

  • 添加操做

    let element = document.createElement("div");   // 建立元素
      body.appendChild(element);  // 将一个节点添加到指定父节点的子节点列表末尾
    复制代码
  • 删除操做

    var oldChild = node.removeChild(child); // 删除子元素
      ChildNode.remove() // 删除元素
    复制代码
  • 修改操做

    Node.innerText // 修改元素文本内容
      Element.innerHTML // 设置或获取描述元素后代的HTML语句
    复制代码
  • 查找操做

    Document.getElementById() // 返回对拥有指定 id 的第一个对象的引用
      Document.querySelector() // 返回文档中匹配指定的CSS选择器的第一元素
      Document.querySelectorAll() // 返回与指定的选择器组匹配的文档中的元素列表
    复制代码
  • developer.mozilla.org/zh-CN/docs/…

异步加载JS 的方式有哪些

浏览器下载除JS外的资源时,会并行下载,以提升性能。但下载JS脚本时,会禁止并行下载(称为脚本阻塞Scripts Block Downloads)。浏览器遇到JS时,必须等JS下载,解析,执行完后,才能继续并行下载下一个资源。缘由是JS可能会改变页面或改变JS间的依赖关系,例如A.js中用document.write改变页面,B.js依赖于A.js。所以要严格保证顺序,不能并行下载。

因为浏览器在遇到<body>标签前是不会渲染页面的,为了不白屏,一般的建议是将JS放到标签底下,能够有最佳的用户体验。

按推荐度排序:

  1. 动态建立<script>标签(Dynamic Script Element)

    var script = document.createElement('script');  // 建立script标签
     script.type = "text/javascript";
     script.src = "A.js";
     document.getElementsByTagName('head')[0].appendChild(script);   // 塞进页面
    复制代码

    先用document.createElement(‘script’)生成一个script标签,再设置它的src属性,最后将其插入到<head>中。

    script标签被插入到页面的DOM树后,就会开始下载src属性指定的脚本。并且经过动态脚本元素下载脚本是异步的,不会阻塞页面的其余下载和处理过程,所以script标签插入<head>中也没问题。

  2. Script async

    <script type="text/javascript" src="A.js" async></script>
    复制代码

    浏览器解析到HTML里的该行script标签,发现指定为async,会异步下载解析执行脚本。

    async 是HTML5里为script标签新增的属性,对于低版本浏览器会存在兼容性问题。

    它会在下载完成后马上执行,而不是会等到DOM加载完成以后再执行,因此仍是有可能会形成阻塞。

    这种方式只适用于引用外部js文件的<script>标签。

    添加async属性的js文件不该该使用document.write方法。

    对多个带有async的js文件,它不能保证按顺序执行,它是哪一个js文件先下载完就先执行哪一个。

  3. Script defer

    <script type="text/javascript" src="A.js" defer></script>
    复制代码

    浏览器解析到HTML里的该行script标签,发现指定为defer,会暂缓下载解析执行脚本。而是等到页面加载完毕后,才加载脚本(更精确地说,是在DOM树构建完成后,在window.onload触发前,加载defer的脚本)。

    defer也是只适用于外部js文件,也不能在js中使用document.write方法。

    能够保证多个js文件的执行顺序就是它们在页面中出现的顺序。

document.write和innerHTML有何区别

  • document.write会重绘整个页面,若是不指定元素的话,它会覆盖掉整个页面内容。
  • innerHTML只会重绘页面的一部分。

jQuery.extend和jQuery.fn.extend的区别

针对jQuery性能的优化方法

如何判断当前脚本运行在浏览器仍是node环境中

经过判断Global对象是否为window,若是不为window,则当前脚本运行在node.js环境中。

this === window ? 'browser' : 'node';
复制代码

Canvas和SVG的比较

Ajax的原理

JS事件委托、事件冒泡

IE和Firefox的事件机制有何区别,如何阻止冒泡?

  • IE只支持事件冒泡,火狐同时支持事件捕获事件冒泡两种。
  • 阻止事件冒泡的方式不一样
    IE: e.cancelBubble = true
    W3C: e.stopPropagation()

JS内存空间的管理

  • 基础数据类型与栈内存
    JS中的基础数据类型,这些值都有固定的大小,每每都保存在栈内存中,由系统自动分配存储空间。咱们能够直接操做保存在栈内存空间的值,所以基础数据类型都是按值访问。

    数据在栈内存中的存储与使用方式相似于数据结构中的堆栈数据结构,遵循后进先出的原则。

    基础数据类型: Number String Null Undefined Boolean

  • 引用数据类型与堆内存
    与其余语言不一样,JS的引用数据类型,好比数组Array、对象Object、函数Function,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不容许直接访问堆内存中的位置,所以咱们不能直接操做对象的堆内存空间。

    在操做对象时,其实是在操做对象的引用而不是实际的对象。所以,引用类型的值都是按引用访问的。这里的引用,咱们能够粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。

总结:

栈内存 堆内存
存储基础数据类型 存储引用数据类型
按值访问 按引用访问
存储的值大小固定 存储的值大小不定,可动态调整
由系统自动分配内存空间 由开发人员经过代码分配
主要用来执行程序 主要用来存放对象
空间小,运行效率高 空间大,可是运行效率相对较低
先进后出,后进先出 无序存储,可根据引用直接获取

JS执行上下文

JS变量对象详解

请解释变量提高

JS做用域及做用域链/闭包(closure),经常使用场景举例说明

请简述JS中的this

JS函数与函数式编程

JS原型,原型链。实现继承的方式

JS有哪几种建立对象的方式

  1. 利用Object构造函数方式建立
  2. 利用对象字面量方式建立
  3. 利用工厂模式建立
  4. 利用构造函数模式建立
  5. 利用原型方式建立
  6. 利用构造函数和原型的组合模式建立

请解释事件循环,调用堆栈和任务队列的区别

谈谈对Promise的理解

ES6知识点

  1. var let const
  2. =>箭头函数
  3. 模版字符串
  4. 解析结构
  5. 函数默认参数
  6. ...展开运算符
  7. class
  8. Promise

防抖与节流

防抖与节流函数是一种最经常使用的 高频触发优化方式,能对性能有较大的帮助。

  • 防抖 (debounce): 将屡次高频操做优化为只在最后一次执行,一般使用的场景是:用户输入,只需再输入完成后作一次输入校验便可。

  • 节流(throttle): 每隔一段时间后执行一次,也就是下降频率,将高频操做优化成低频操做,一般使用场景: 滚动条事件 或者 resize 事件,一般每隔 100~500 ms执行一次便可。

    function throttle(method, context) {
          clearTimeout(method.tID);
          method.tID = setTimeout(function () {
              method.call(context);
          }, 1000);
      }
    复制代码

    用法:

    function showTime() {
          console.log("nowDate:" + new Date().toLocaleDateString());
      }
    
      setInterval(function () {
          throttle(showTime);
      }, 2000);
    复制代码

模块化

模块化开发在现代开发中已经是必不可少的一部分,它大大提升了项目的可维护、可拓展和可协做性。一般,咱们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。

分类:

  • es6: import / export
  • commonjs: require / module.exports / exports
  • amd: require / defined

require与import的区别

  • require支持 动态导入,import不支持,正在提案 (babel 下可支持)
  • require是 同步 导入,import属于 异步 导入
  • require是 值拷贝,导出值变化不会影响导入值;import指向 内存地址,导入值会随导出值而变化

oAuth实现方案

如何实现单点登陆(Single Sign On)

请解释SPA(单页应用),优缺点是什么?如何使其对SEO友好

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。

单页应用SPA 多页应用MPA
组成 一个外壳页面和多个页面片断组成 多个完整页面构成
资源共用(css,js) 共用,只需在外壳部分加载 不共用,每一个页面都须要加载
刷新方式 页面局部刷新或更改 整页刷新
url 模式 a.com/#/pageone
a.com/#/pagetwo
a.com/pageone.html
a.com/pagetwo.html
用户体验 页面片断间的切换快,用户体验良好
因为要一次加载全部的资源(html/js),故首屏加载慢
页面切换加载缓慢,流畅度不够,用户体验比较差
首屏加载很快
转场动画 容易实现 没法实现
数据传递 容易 依赖 url传参、或者cookie 、localStorage等
搜索引擎优化(SEO) 须要单独方案、实现较为困难、不利于SEO检索。
Prerender预渲染优化SEO
实现方法简易
试用范围 高要求的体验度、追求界面流畅的应用 适用于追求高度支持搜索引擎的应用
开发成本 较高,常需借助专业的框架 较低,但页面重复代码多
维护成本 相对容易 相对复杂

前端性能优化方案

如下是移动端的优化方案,大部分Web端也一样适用

正则表达式

设计模式举例(实现、应用、优缺点)

前端经常使用框架对比

JS编码规范

浏览器相关

浏览器架构

  • 用户界面
  • 主进程
  • 内核
    • 渲染引擎
    • JS 引擎
      • 执行栈
    • 事件触发线程
      • 消息队列
        • 微任务
        • 宏任务
    • 网络异步线程
    • 定时器线程

浏览器下事件循环(Event Loop)

事件循环是指: 执行一个宏任务,而后执行清空微任务列表,循环再执行宏任务,再清微任务列表

  • 微任务 microtask(jobs): promise / ajax / Object.observe(该方法已废弃)
  • 宏任务 macrotask(task): setTimout / setInterval / script / IO / UI Rendering

浏览器解析流程

从输入 url 到展现的过程

  • DNS 解析
  • TCP 三次握手
  • 发送请求,分析 url,设置请求报文(头,主体)
  • 服务器返回请求的文件 (html)
  • 浏览器渲染
    • HTML parser --> DOM Tree
      • 标记化算法,进行元素状态的标记
      • dom 树构建
    • CSS parser --> Style Tree
      • 解析 css 代码,生成样式树
    • attachment --> Render Tree
      • 结合 dom树 与 style树,生成渲染树
    • layout: 布局
    • GPU painting: 像素绘制页面

功能检测、功能推断、navigator.userAgent的区别

功能检测(feature detection)

功能检测包括肯定浏览器是否支持某段代码,以及是否运行不一样的代码(取决于它是否执行),以便浏览器始终可以正常运行代码功能,而不会在某些浏览器中出现崩溃和错误。例如:

if ('geolocation' in navigator) {
// 可使用 navigator.geolocation
} else {
// 处理 navigator.geolocation 功能缺失
}
复制代码

Modernizr是处理功能检测的优秀工具。

功能推断(feature inference)

功能推断与功能检测同样,会对功能可用性进行检查,可是在判断经过后,还会使用其余功能,由于它假设其余功能也可用,例如:

if (document.getElementsByTagName) {
element = document.getElementById(id);
}
复制代码

很是不推荐这种方式。功能检测更能保证万无一失。

UA 字符串

这是一个浏览器报告的字符串,它容许网络协议对等方(network protocol peers)识别请求用户代理的应用类型、操做系统、应用供应商和应用版本。它能够经过navigator.userAgent访问。 然而,这个字符串很难解析而且极可能存在欺骗性。例如,Chrome 会同时做为 Chrome 和 Safari 进行报告。所以,要检测 Safari,除了检查 Safari 字符串,还要检查是否存在 Chrome 字符串。不要使用这种方式。
考虑到历史缘由及现代浏览器中用户代理字符串(userAgent)的使用方式,经过用户代理字符串来检测特定的浏览器并非一件轻松的事情。因此使用用户代理检测是最后的选择。
用户代理检测通常适用如下的情形:

  • 不能直接准确的使用功能检测。
  • 同一款浏览器在不一样平台下具有不一样的能力。这个时候可能就有必要肯定浏览器位于哪一个平台。
  • 为了跟踪分析等目的须要知道特定的浏览器。

浏览器版本检测方式

可使用navigator.userAgent。

JS同源策略(same-origin policy)

同源策略限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

下表给出了相对http://store.company.com/dir/page.html同源检测的示例:

URL 结果 缘由
store.company.com/dir2/other.… 成功 只有路径不一样
store.company.com/dir/inner/a… 成功 只有路径不一样
store.company.com/secure.html 失败 不一样协议 ( https和http )
store.company.com:81/dir/etc.htm… 失败 不一样端口 ( http:// 80是默认的)
news.company.com/dir/other.h… 失败 不一样域名 ( news和store )

跨标签页通信

不一样标签页间的通信,本质原理就是去运用一些能够 共享的中间介质,所以比较经常使用的有如下方法:

  • 经过父页面window.open()和子页面postMessage
    • 异步下,经过 window.open('about: blank') 和 tab.location.href = '*'
  • 设置同域下共享的localStorage与监听window.onstorage
    • 重复写入相同的值没法触发
    • 会受到浏览器隐身模式等的限制
  • 设置共享cookie与不断轮询脏检查(setInterval)
  • 借助服务端或者中间层实现

跨域的解决方案

按实际使用量排序(我的理解):

浏览器数据本地存储方法(localStroage、sessionStroage、cookie、indexedDB)

目前常见的存储方式为如下三种:

  • Cookie
  • web存储 (locaStorage和seesionStorage)
  • IndexedDB

在H5出现以前,数据都是存储在cookie中的。为了解决cookie的局限性引入了Web存储,indexedDB用于客户端存储大量结构化数据(包括, 文件/ blobs)。

共同点:都是保存在浏览器端、且同源的
区别

Cookie localStorage sessionStorage indexedDB
容量大小 4kb左右 5M左右 5M左右 无限容量
过时时间 只在设置的过时时间以前一直有效,
即便窗口或者浏览器关闭
始终有效 当前浏览器窗口关闭前有效 始终有效
存储方式 浏览器和服务器间来回传递 本地保存 本地保存 本地保存
做用域 在同源窗口中共享 在同源窗口中共享 在同源窗口而且同一窗口中共享 在同源窗口中共享

Web安全举例

  • XSS(跨站脚本攻击)几种形式,防范手段,过滤哪些字符

  • csrf(跨站请求伪造)原理,实现,防范手段

  • sql注入

  • 命令行注入

  • DDoS(Distributed Denial of Service) 又叫分布式拒绝服务

  • 流量劫持 DNS劫持 HTTP劫持

  • 服务器漏洞

  • juejin.im/entry/5a559…

状态码

Web Worker

现代浏览器为JavaScript创造的 多线程环境。能够新建并将部分任务分配到worker线程并行运行,两个线程可 独立运行,互不干扰,可经过自带的 消息机制 相互通讯。

限制:

  • 同源限制
  • 没法使用 document / window / alert / confirm
  • 没法加载本地资源

内存泄露

  • 意外的全局变量: 没法被回收
  • 定时器: 未被正确关闭,致使所引用的外部变量没法被释放
  • 事件监听: 没有正确销毁 (低版本浏览器可能出现)
  • 闭包: 会致使父级中的变量没法被释放
  • dom 引用: dom 元素被删除时,内存中的引用未被正确清空

可用 chrome 中的 timeline 进行内存标记,可视化查看内存的变化状况,找出异常点。

HTTP缓存机制

cookie和session的区别

常见兼容性问题(移动端/PC端)

polyfill的做用

代码相关

44个 Javascript 题解析

lidaguang1989.github.io/2018/01/jav…

43个 javascript 进阶问题列表

github.com/lydiahallie…

如何实现数组去重

正则实现trim()功能

function myTrim(str) {
let reg = /^\s+|\s+$/g;
return str.replace(reg, "");
}
console.log(myTrim('    asdf    '));
复制代码

CSS篇

a标签上四个伪类的执行顺序是怎么样的?

link > visited > hover > active

  • L-V-H-A love hate 用喜欢和讨厌两个词来方便记忆

css 属性 content 有什么做用?

content 属性专门应用在 before/after 伪元素上,用于插入额外内容或样式。可减小标签的使用。

伪元素和伪类的区别和做用?

  • 伪元素 -- 在内容元素的先后插入额外的元素或样式,可是这些元素实际上并不在文档中生成。

  • 它们只在外部显示可见,但不会在文档的源代码中找到它们,所以,称为“伪”元素。例如:

    p::before {content:"第一章:";}
      p::after {content:"Hot!";}
      p::first-line {background:red;}
      p::first-letter {font-size:30px;}
    复制代码
  • 伪类 -- 将特殊的效果添加到特定选择器上。它是已有元素上添加类别的,不会产生新的元素。例如: a:hover {color: #FF00FF} p:first-child {color: red}

::before 和 :after 中双冒号和单冒号有什么区别?

  • 在 CSS 中伪类一直用 : 表示,如 :hover, :active 等
  • 伪元素在CSS1中已存在,当时语法是用 : 表示,如 :before 和 :after
  • 后来在CSS3中修订,伪元素用 :: 表示,如 ::before 和 ::after,以此区分伪元素和伪类
  • 因为低版本IE对双冒号不兼容,开发者为了兼容性各浏览器,继续使使用 :after 这种老语法表示伪元素
  • 综上所述:::before 是 CSS3 中写伪元素的新语法; :after 是 CSS1 中存在的、兼容IE的老语法

在CSS样式中常使用 px、em 、rem的区别

  • px(绝对长度单位)
    • 在缩放页面时没法调整那些使用它做为单位的字体、按钮等的大小
  • em(相对长度单位)
    • 浏览器的默认字体都是16px,那么1em=16px,以此类推计算10px=0.625em

    • 为了简化font-size的换算,通常都会在body中写入如下代码

      body { font-size: 62.5%; } /*  公式16px*62.5%=10px  */  
      复制代码
    • em的值并非固定的

    • em会继承父级元素的字体大小(参考物是父元素的font-size)

    • em中全部的字体都是相对于父元素的大小决定的;因此若是一个设置了font-size:1.2em的元素在另外一个设置了font-size:1.2em的元素里,而这个元素又在另外一个设置了font-size:1.2em的元素里,那么最后计算的结果是1.2X1.2X1.2=1.728em

  • rem(相对长度单位)
    • rem单位可谓集相对大小和绝对大小的优势于一身
    • 和em不一样的是rem老是相对于根元素(如:root{}),而不像em同样使用级联的方式来计算尺寸。这种相对单位使用起来更简单。
    • rem支持IE9及以上,意思是相对于根元素html(网页),不会像em那样,依赖于父元素的字体大小,而形成混乱。使用起来安全了不少。

你对 line-height 是如何理解的?

  • line-height 指一行字的高度,包含了字间距,其实是下一行基线到上一行基线距离
  • 若是一个标签没有定义 height 属性,那么其最终表现的高度是由 line-height 决定的
  • 一个容器没有设置高度,那么撑开容器高度的是 line-height 而不是容器内的文字内容
  • 把 line-height 值设置为 height 同样大小的值能够实现单行文字的垂直居中
  • line-height 和 height 都能撑开一个高度,height 会触发 haslayout,而 line-height 不会

line-height 三种赋值方式有何区别?(带单位、纯数字、百分比)

  • 带单位:px 是固定值,而 em 会参考父元素 font-size 值计算自身的行高
  • 纯数字:会把比例传递给后代。例如,父级行高为 1.5,子元素字体为 18px,则子元素行高为 1.5 * 18 = 27px
  • 百分比:将计算后的值传递给后代

display: none; 与 visibility: hidden; 的区别

  • 联系:它们都能让元素不可见
  • 区别:
    • display:none;会让元素彻底从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden;不会让元素从渲染树消失,渲染师元素继续占据空间,只是内容不可见
    • display: none;是非继承属性,子孙节点消失因为元素从渲染树消失形成,经过修改子孙节点属性没法显示;visibility:hidden;是继承属性,子孙节点消失因为继承了hidden,经过设置visibility: visible;可让子孙节点显式
    • 修改常规流中元素的display一般会形成文档重排。修改visibility属性只会形成本元素的重绘
    • 读屏器不会读取display: none元素内容;会读取visibility: hidden元素内容

rgba() 与 opacity的区别

  • rgba()
    • 仅仅改变的是背景的透明度
    • 不会对文本形成影响,不具备继承性
  • opacity
    • 不只会改变背景的透明度
    • 还会改变文本的透明度,而且具备继承性

盒模型的理解

页面渲染时,dom 元素所采用的 布局模型。可经过box-sizing进行设置。

  • content-box (W3C 标准盒模型)

默认值,标准盒子模型。 width 与 height 只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。注意: 内边距、边框和外边距都在这个盒子的外部。 好比说,.box {width: 350px; border: 10px solid black;} 在浏览器中的渲染的实际宽度将是 370px。

尺寸计算公式:

width = 内容的宽度

height = 内容的高度

宽度和高度的计算值都不包含内容的边框(border)和内边距(padding)。

  • border-box (IE盒模型)

width 和 height 属性包括内容,内边距和边框,但不包括外边距。这是当文档处于 Quirks模式 时Internet Explorer使用的盒模型。注意,填充和边框将在盒子内 , 例如, .box {width: 350px; border: 10px solid black;} 致使在浏览器中呈现的宽度为350px的盒子。内容框不能为负,而且被分配到0,使得不可能使用border-box使元素消失。

尺寸计算公式:

width = border + padding + 内容的宽度

height = border + padding + 内容的高度

relative、fixed、absolute和static四种定位的区别

通过定位的元素,其position属性值必然是relative、absolute、fixed或sticky。

  • static:默认定位属性值。该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。
  • relative:该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(所以会在此元素未添加定位时所在位置留下空白)。
  • absolute:不为元素预留空间,经过指定元素相对于最近的非 static 定位祖先元素的偏移,来肯定元素位置。绝对定位的元素能够设置外边距(margins),且不会与其余边距合并。
  • fixed:不为元素预留空间,而是经过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出如今的每页的固定位置。fixed 属性会建立新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改成该祖先。
  • sticky:盒位置根据正常流计算(这称为正常流动中的位置),而后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。在全部状况下(即使被定位元素为 table 时),该元素定位均不对后续元素形成影响。当元素 B 被粘性定位时,后续元素的位置仍按照 B 未定位时的位置来肯定。position: sticky 对 table 元素的效果与 position: relative 相同。

block, inline和inline-block有什么区别?

block inline-block inline
大小 填充其父容器的宽度 取决于内容 取决于内容
定位 重新的一行开始,而且不容许旁边有 HTML 元素(除非是float) 与其余内容一块儿流动,并容许旁边有其余元素 与其余内容一块儿流动,并容许旁边有其余元素。
可否设置width和height 不能 设置会被忽略
可使用vertical-align对齐 不能够 能够 能够
边距(margin)和填充(padding) 各个方向都存在 各个方向都存在 只有水平方向存在。垂直方向会被忽略。 尽管border和padding在content周围,但垂直方向上的空间取决于'line-height'
浮动(float) - - 就像一个block元素,能够设置垂直边距和填充。

flexbox布局

栅格布局(grid)

居中布局

水平居中

  • 行内元素(inline-block): text-align: center
  • 块级元素: margin: 0 auto
  • absolute + transform
  • flex + justify-content: center

垂直居中

  • line-height: height
  • absolute + transform
  • flex + align-items: center
  • table

水平垂直居中

  • absolute + transform
  • flex + justify-content + align-items

选择器优先级

!important > 行内样式 > #id > .class > tag > * > 继承 > 默认

选择器 从右往左 解析

  • 相同权重,定义最近者为准:行内样式 > 内部样式 > 外部样式
  • 含外部载入样式时,后载入样式覆盖其前面的载入的样式和内部样式
  • 选择器优先级: 行内样式[1000] > id[100] > class[10] > Tag[1]
  • 在同一组属性设置中,!important 优先级最高,高于行内样式

解释浏览器如何肯定哪些元素与 CSS 选择器匹配

浏览器从最右边的选择器(关键选择器)开始查找,根据关键选择器,浏览器从 DOM 中筛选出元素,而后向上遍历被选元素的父元素,判断是否匹配。

例如,对于形如p span的选择器,浏览器首先找到全部<span>元素,并遍历它的父元素直到根元素以找到<p>元素。对于特定的<span>,只要找到一个<p>,就知道已经匹配并中止继续匹配。

基于以上原理,为了编写高效CSS,应注意如下几点:

  • 避免使用标签和通用选择器做为关键选择器。由于它们会匹配大量的元素,浏览器必需要进行大量的工做,去判断这些元素的父元素们是否匹配
  • 选择器匹配语句链越短,浏览器的匹配速度越快
  • 搞清楚哪些 CSS 属性会触发从新布局(reflow)、重绘(repaint)和合成(compositing)。在写样式时,避免触发从新布局的可能。

CSS预处理器(Sass/Less)的优缺点分别是什么?

  • CSS 预处理器基本思想:为 CSS 增长了一些编程的特性(变量、逻辑判断、函数等)
  • 开发者使用这种语言进行进行 Web 页面样式设计,再编译成正常的 CSS 文件使用
  • 使用 CSS 预处理器,可使 CSS 更加简洁、适应性更强、可读性更佳,无需考虑兼容性
  • 最经常使用的 CSS 预处理器语言包括:Sass(SCSS)和 Less

优势:

  • 提升 CSS 可维护性
  • 易于编写嵌套选择器
  • 引入变量,增添主题功能。能够在不一样的项目中共享主题文件
  • 经过混合(Mixins)生成可复用的 CSS
  • 将代码分割成多个文件。不进行预处理的 CSS,虽然也能够分割成多个文件,但须要创建多个 HTTP 请求加载这些文件

缺点:

  • 须要预处理工具
  • 从新编译的时间可能会很慢

区别:

  • Less 用 JavaScript 实现,与 NodeJS 高度结合
  • Less 中,变量名称以@做为前缀,容易与 CSS 关键字混淆,如@media、@import和@font-face
  • 经过node-sass使用 Sass,它用 C ++ 编写的 LibSass 绑定。在 Node 版本切换时,必须常常从新编译。

link 与 @import 的区别

  • link功能较多,能够定义 RSS,定义 Rel 等做用,而@import只能用于加载 css
  • 当解析到link时,页面会同步加载所引的 css,而@import所引用的 css 会等到页面加载完才被加载
  • @import须要 IE5 以上才能使用
  • 浏览器对 link 支持早于 @import ,可使用 @import 对老浏览器隐藏样式
  • link可使用 js 动态引入,@import不行
  • @import 必须在样式规则以前,能够在css文件中引用其余文件
  • 整体来讲:link优于@import

常常遇到的浏览器的兼容性有哪些?缘由,解决方法是什么,经常使用hack的技巧 ?

  • 浏览器默认的margin和padding不一样。解决方案是加一个全局的*{margin:0;padding:0;}来统一
  • IE下,even对象有x,y属性,可是没有pageX,pageY属性
  • Firefox下,event对象有pageX,pageY属性,可是没有x,y属性

CSS优化、提升性能的方法有哪些?

  • 多个css合并,尽可能减小HTTP请求
  • 将css文件放在页面最上面
  • 移除空的css规则
  • 避免使用CSS表达式
  • 选择器优化嵌套,尽可能避免层级过深
  • 充分利用css继承属性,减小代码量
  • 抽象提取公共样式,减小代码量
  • 属性值为0时,不加单位
  • 属性值为小于1的小数时,省略小数点前面的0
  • css雪碧图

抽离样式模块怎么写,说出思路?

CSS能够拆分红2部分:公共CSS 和 业务CSS:

  • 网站的配色,字体,交互提取出为公共CSS。这部分CSS命名不该涉及具体的业务
  • 对于业务CSS,须要有统一的命名,使用公用的前缀。能够参考面向对象的CSS

什么是响应式设计?响应式设计的基本原理是什么?如何兼容低版本的IE?

  • 响应式设计就是网站可以兼容多个终端,而不是为每一个终端作一个特定的版本

  • 基本原理是利用CSS3媒体查询,为不一样尺寸的设备适配不一样样式

  • 对于低版本的IE,可采用JS获取屏幕宽度,而后经过resize方法来实现兼容

    $(window).resize(function () {
          screenRespond();
      });
      screenRespond();
    
      function screenRespond(){
          var screenWidth = $(window).width();
          if(screenWidth <= 1800){
              $("body").attr("class", "w1800");
          }
          if(screenWidth <= 1400){
              $("body").attr("class", "w1400");
          }
          if(screenWidth > 1800){
              $("body").attr("class", "");
          }
      }
    复制代码

用纯CSS建立一个三角形的原理是什么?

// 把上、左、右三条边隐藏掉(颜色设为 transparent)
#demo {
width: 0;
height: 0;
border-width: 20px;
border-style: solid;
border-color: transparent transparent red transparent;
}
复制代码

什么状况下,用translate()而不用绝对定位?何时,状况相反。

  • translate()是transform的一个值。改变transform或opacity不会触发浏览器从新布局(reflow)或重绘(repaint),只会触发复合(compositions)
  • 改变绝对定位会触发从新布局,进而触发重绘和复合
  • transform使浏览器为元素建立一个 GPU 图层,但改变绝对定位会使用到 CPU。
  • 所以translate()更高效,能够缩短平滑动画的绘制时间。

当使用translate()时,元素仍然占据其原始空间(有点像position:relative),这与改变绝对定位不一样。

对BFC规范(块级格式化上下文:block formatting context)的理解?

对CSS浮动Float的理解

CSS动画的理解

  • translate
  • transform
  • animation
  • transition

最后推荐一本由CSS大神Lea Verou著, CSS魔法译的书

一本为新一代CSS所写的新一代CSS图书。在我所知的技术专家中,没人比Lea Verou更能领会新一代CSS的精髓。 ——Jeffrey Zeldman, 《网站重构》做者

源文件已上传GitHub,若是以为对你有所帮助,但愿可以动动手指给个Star(O(∩_∩)O)。

相关文章
相关标签/搜索