前端面试八股文(详细版)—上
Posted 旺旺大力包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端面试八股文(详细版)—上相关的知识,希望对你有一定的参考价值。
本文将详细讲解 HTML 、CSS 、JavaScript 、计算机网络知识等方面的内容,Vue 、React 、git 、项目开发实例等内容放在下篇,预祝各位成功上岸!
第一板块:JavaScript
ES6 新增内容
1、模板字符串 变量名写在 $ 中,$ 中可以放入 javascript 表达式
2、箭头函数
3 class 类的继承
class Animal
class Dog extends Animal
constructor(name)
super();
// this.name = name
3、module
4、promise
5、const / let
6、 扩展运算符(...)
- 用于取出参数对象所有可遍历属性然后拷贝到当前对象
- 可用于合并两个对象
7、 解构赋值
- 解构赋值语法是一种 Javascript 表达式。通过解构赋值,可以将属性/值从对象/数组中取出,赋值给其他变量。
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// expected output: 10
console.log(b);
// expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// expected output: Array [30,40,50]
8、 Symbol 数据类型 一种新的原始数据类型,主要是为了避免属性名的重复
9、Map 一种新的数据结构,类似对象,Set 一种新的数据结构,类似数组
普通函数和箭头函数的区别?
1、箭头函数没有自己的 this 指向,它的 this 指向来源于它的上级,并且继承而来的 this 指向是无法改变的。
2、 箭头函数由于没有自己的 this,所以不能作为构造函数。
3、箭头函数中没有 arguments(形参数组),但是可以访问外围函数的arguments对象
补充:ES6 箭头函数中的 this 和所在环境(外层)中的 this 指向一致
如何改变 this 指向
可以使用call()、apply()、bind() 来改变 this
Call bind apply的原理和区别
- call 方法 call()是 apply()的一颗语法糖,作用和 apply()一样,同样可实现继承,唯一的区别就在于 call()接收的是参数列表,而 apply()则接收参数数组。
- bind 方法 bind()的作用与 call()和 apply()一样,都是可以改变函数运行时上下文,区别是 call()和 apply()在调用函数之后会立即执行,而 bind()方法调用并改变函数运行时上下文后,返回一个新的函数,供我们需要时再调用.
1、第一个参数都是 this 的指向对象
2、 apply 的第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。
3、call 传入的第二个参数是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
4、 bind 传入的第二个参数也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数,供我们需要时再调用.。
promise 用法以及相关原理 用法 有那些 API
1、promise是异步编程的一种解决方案,解决多个异步方法串行的问题,比如回调地狱等;
2、promise,简单地说就是一个容器,里面保存着某个未来才会结束的事件,从语法说promise是一个对象,从他可以获取异步操作的消息。promise提供统一的api,各种操作都可以用相同的方法进行处理.;
3、它接受一个 function 作为参数。function 中有两个形参,第一个表示请求成功的回调,第二个表示请求失败的回调,分别是 resolve 和 reject ;
4、.then 在成功的时候触发 .catch 在失败的时候触发
5、promise 的状态不受外界影响不可逆,三个状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
- promise对象的状态改变只有两种可能:从pending变为fulfilled或从pending变为rehected;
6、有两个很重要的 api:
- Promise.all() 表示所有的 Promise 数组中的方法都成功之后触发
返回一个新的promise,只有所有的promise都成功才能成功,只要有一个失败了就直接失败;
- Promise.race() 表示数组中只要有一个完成就结束
返回一个promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝;
回调地狱
回调函数中嵌套回调函数的情况就叫做回调地狱。它就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。
promise 和 async await 的区别
1、Promise是 ES6 中处理异步请求的语法,使用 .then() 来实现链式调用,使用 .catch() 来捕获异常。
2、async/await 是对 Promise 的升级,async用于声明一个函数是异步的,await是等待一个异步方法执行完成
3、async/await 用同步的写法写异步(await一个Promise对象),async/await 的捕获异常可以使用 try/catch 语法。(也可以使用 .catch 语法)用同步的方法写异步的代码;
let const var 的区别
var 关键字:
- var 定义变量,可以声明同名变量,可以不进行初始化;(不初始化的情况下保存一个特殊值 undefined )
- var 不仅可以改变保存的值,也可以改变值的类型(合法但不推荐);
- var 定义的变量在函数作用域内有效;
- var 声明变量存在变量提升,var 声明的变量可以挂载在 window 上;
let 声明:
- let 定义变量,可以不进行初始化;(不初始化的情况下保存一个特殊值 undefined )
- let 不仅可以改变保存的值,也可以改变值的类型(合法但不推荐);
- let 用来定义局部变量,仅在块级作用域内有效;
- let 不允许重复声明;
- 不存在变量提升的现象,所以一定要在定义后使用,否则报错(暂时性死区);
- 在全局作用域中用 let 声明变量不会成为 window对象的属性;
const 声明:
- const 定义的是常量,必须进行初始化(设置初始值);
- const 定义好之后“不能改变”;(不能改变栈里面的数据,堆里面的数据可改变;例如数字不可变,对象的属性等可变)
- 仅在块级作用域内有效;
- const 不允许重复声明;
- 在全局作用域中用 const 声明变量不会成为 window对象的属性;
详细分析查看:https://blog.csdn.net/leoxingc/article/details/127813133
常见数组方法
- push() 向数组尾部添加元素,返回添加后的数组长度,原数组改变
- pop() 从数组的尾部删除一个元素,返回删除的元素,原数组改变
- unshift() 向数组头部添加元素,返回添加后的数组长度,原数组改变
- shift() 从数组的头部删除一个元素,返回删除的元素,原数组改变
- Slice() 提取数组,(1 起 2 止止不算)原数组不变
- splice() 剪接数组,(1 起 2 长 3 添加)原数组改变
- reverse() 反转数组,原数组改变
- sort() 按位比较
- arr.sort(function(a,b)return a - b;); 从小到大
- arr.sort(function(a,b)return b - a;); 从大到小
- Join() 参数最为连接字符,连接数组,转换为字符串,原数组不变
- concat() 合并数组,原数组不变 arr1.concat(arr2,arr3)
- find 查找符合条件的项
- findIndex 查找符合条件项目的下标
- toString()把数组转字符串(之间默认用逗号隔开)
ES5 数组的常用方法
- indexOf() 查找数组中某元素第一次出现的索引,若无返回-1
- lastindexOf() 查找数组中某元素最后一次出现的索引,若无返回-1
- forEach() 遍历数组,不接受返回值
- map() 映射数组,接受返回值
- Filter() 筛选满足条件数组
- Every() 判断数组里每一个元素是否符合条件
- Some() 判断数组里是否有一个符合条件的元素
- reduce() 数组值累加(两个值累加完传递给第一个元素,直到最后)
遍历
1、基本 for 循环
// for
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++)
console.log(arr[i]); // 1,2,3
2、for...of...遍历
// for...of...
var arr = [1, 2, 3];
for (var i of arr)
console.log(arr[i]); // 1,2,3
3、for...in...遍历
// for...in...
var arr = [1, 2, 3];
for (var i in arr)
console.log(arr[i]); // 1,2,3
4、forEach()遍历
// forEach( item, index, arr )
var arr = [1, 2, 3];
arr.forEach((item, index, arr) => // item为arr的元素,index为下标,arr原数组
console.log(item); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(arr); // [1, 2, 3]
);
5、map() 映射
// map() 映射
var arr = [1, 2, 3];
var arr1 = arr.map((item, index, arr) =>
// item为arr的元素,index为下标,arr原数组
console.log(item); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(arr); // [1, 2, 3]
return item * 2; // 返回一个处理过的新数组[2, 4, 6]
);
console.log(arr);
console.log(arr1); // 处理过的新数组[2, 4, 6]
6、filter()
var arr = [1, 2, 3];
var arr1 = arr.filter(function (item, index, arr)
// item为arr的元素,index为下标,arr原数组
console.log(item); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(arr); // [1, 2, 3]
return item > 2; // 返回一个过滤过的新数组[3]
);
console.log(arr);
console.log(arr1); // 过滤过的新数组[3]
7、some()
// some()
var arr = [1, 2, 3];
arr.some((item, index, arr) =>
// item为数组中的元素,index为下标,arr为目标数组
console.log(item); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(arr); // [1, 2, 3]
);
8、every()
// every()
var arr = [1, 2, 3];
var arr1 = arr.every((item, index, arr) =>
// item为数组中的元素,index为下标,arr为目标数组
console.log(item); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(arr); // [1, 2, 3]
return item > 0; // true
);
console.log(arr); // [1, 2, 3]
console.log(arr1); // true
9、reduce()
// every()
var arr = [1, 2, 3];
var arr1 = arr.reduce((sum, item, index, arr) =>
// item为数组中的元素,index为下标,arr为目标数组,sum为上一次调用回调函数时的返回值,
console.log(item);
console.log(index);
console.log(arr);
return sum + item;
, init);
10.详细分析可查看:
前端遍历数组的方法:
数组去重
1、利用数组的 indexOf 方法,新建一个空数组,循环遍历旧数组,判断空数组中是否有当前的元素,如果有就跳过,如果没有就执行 push 方法。
let arr = [1, 1, 2, 2, 3, 3, 4, 5];
let newArr = [];
arr.forEach(item =>
if (newArr.indexOf(item) < 0)
newArr.push(item);
);
2、利用数组的 splice 方法,先利用 sort 方法对数组进行排序,然后循环遍历数组,比较前一项与后一项是否相同,如果相同就执行 spilce 方法删除当前项。
3. 利用 ES6 中 Set 不能存放相同元素的特点,配合...展开运算符进行去重。
let arr=[1,2,3,4,3,2,1,5,3];
let set=new Set(arr);
//因为set结构并不是数组,所以需要转为数组
set=[...set];
4. lodash 插件也可以去重。
判断是不是数组
let a = [1, 3, 4];
Array.isArray(a); //true
a instanceof Array; //true
a.constructor === Array; //true
Object.prototype.toString.call(a) === "[object Array]"; //true
for in 和 for of 的区别
for...in 只能获得对象的键名,对数组来说是下标,对象是属性名。并且手动添加的属性也能遍历到;
for...of 只能获得键值(数组),遍历对象会报错;
数组排序
(1)冒泡排序:数组中的元素两两进行比较,如果前一个比后一个大,交换值,第一轮结束
(2)选择排序:选出一个位置,这个位置上的数,和后面所有的数进行比较,如果比较出大小就交换两个数的位置
(3)/*sort排序 */
arr.sort((a,b)=>
return a-b;
)
将伪数组转换为真数组
1、Array.from() 将伪数组转成真数组
arr=Array.from(arr);
2、arguments 将伪数组转成真正的数组
function test ()
arguments.__proto__ = Array.prototype;
arguments.push(10)
console.log(arguments)
test(1,2,3,4)
随机取值
数据中有 800 个元素,随机取一个
生成一个 0-800 之间的随机数作为数组下标
const arr = [1,2,3,4,5,6,7...]
// 随机获取一个下标
// Math.random的取值范围 0=<x<1
//const index = Math.floor(Math.random()*arr.length);
const index=Math.floor(Math.random() * 800); // 随机整数
arr[index] // 随机的取一个
删除某个元素
数组中有 800 个元素,删除第 701 个
arr.splice(700, 1); // splice参数一表示开始位置 参数二表示个数 后面的参数序列是用来替换的内容,如果没有就只做删除处理
filter, slice + concat;
字符串方法
字符串和数字相加
相加的时候会做隐式转换,数字转换为字符串,最终的结果是字符串拼接;
字符串如何转变为数字
let str = "123";
console.log(Number(str));.
console.log(str \\* 1);
深拷贝和浅拷贝
浅拷贝:只拷贝第一级实现方式:...扩展运算符 Object.asign
浅拷贝:基本数据类型拷贝值,复杂数据类型,拷贝地址
深拷贝:逐层拷贝对象的每一级
实现方式:`JSON.parse(JSON.stringify(obj))`或者使用插件 lodash
深拷贝:拷贝的是值,相互之间没有影响 地址指向堆
深浅拷贝是因为引用数据类型数据存储的问题,引用数据类型会把指针存放在栈内存中,真正的值存放在堆内存中,浅拷贝只是拷贝了地址,而深拷贝则是完全拷贝,并且拷贝后的对象和原对象没有任何关联。
- 栈内存中放地址,堆内存中放值
- - 使用递归实现深拷贝。
- - 使用 lodash 插件中的 deepclone 来实现深拷贝。
- - 使用 JSON.stringfy()和 JSON.parse()来实现。
let obj = name:'zhangsan',age:18
let newObj = JSON.parse(JSON.stringfy(obj))
原型和原型链
每个构造函数都有一个原型,每个原型对象又有一个construtor属性指向构造函数,每个实例都有__proto__指向原型对象,原型对象上的属性方法能被实例访问。
在JS中,用 __proto__ 属性来表示一个对象的原型链。当查找一个对象的属性时,JS会向上遍历原型链,直到找到给定名称的属性为止。
JavaScript prototype(原型对象)
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
所有 JavaScript 中的对象都是位于 原型链顶端的 Object 的实例。
每个对象都有一个`__proto__`属性,并且指向它的`prototype`原型对象
每个构造函数都有一个`prototype`原型对象
`prototype`原型对象里的`constructor`指向构造函数本身
原型链的终点 Object.prototype.__proto__ === null //true
原型和原型链的详细介绍:http://t.csdn.cn/zQZMc
原型/构造函数/实例
原型(prototype): 一个简单的对象,用于实现对象的属性继承。可以简单的理解成对象的爹。每个JavaScript对象中都包含一个–proto-- (非标准)的属性指向它爹(该对象的原型),可obj.–proto–进行访问。
构造函数: 本质上是一个普通的函数,只是可以通过new来 新建一个对象的函数。
实例: 通过构造函数和new创建出来的对象,便是实例。 实例通过__proto__指向原型,通过constructor指向构造函数。
new 操作符调用构造函数的步骤
1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此this指向了这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性)
4、返回新对象
闭包
闭包就可以在全局函数里面操作另一个作用域的局部变量!
在函数外部可以访问函数内部的变量。
使用场景:一时半会想不到了-------(必须答时 1.函数作为参数传递。2.函数作为返回值)
function foo()
var local = 1
function bar()
local++ //可以访问这个函数词法作用域中的变量
return local
return bar //一个函数被当作值返回
var func = foo() //外层函数执行完毕时销毁
func() //这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,而且无法直接访问,必须通过返回的函数。
- 虽然可以保护私有变量,但用多了可能造成内存泄露。
闭包的好处:减少全局变量的定义,减少全局空间的污染;形成命名空间;
闭包的坏处:容易造成内存泄漏:一块内存空间既不能被销毁,也不能被访问,通常出现 IE 低版本;
闭包优化:由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁,
将闭包函数赋值为null 可以销毁闭包;
闭包 this 执行问题:this指向window对象(因为匿名函数的执行具有全局性,所以其this对象指向window);
遇到内存泄漏的问题
当不断向堆中存储数据,而不进行清理,这就是内存泄漏;
当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露;
哪些操作会造成内存泄露
1、全局变量引起的内存泄露
2、闭包引起的内存泄露:慎用闭包
3、dom清空或删除时,事件未清除导致的内存泄漏
4、循环引用带来的内存泄露
页面实时更新
因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
轮询:定时发送请求,响应请求
websocket
js 的执行机制?微任务和宏任务
1. js 是单线程的
所谓单线程就是一次只能执行一段代码,在执行的时候如果遇到比较耗时的操作,默认就会停下来等待这个操作完成之后继续走。这种情况下,就会出现页面卡在那里不动。为了解决这个问题 js 一般把比较耗时的操作做异步处理;
2. js 中的异步处理
js 中存在一个异步队列,所有比较耗时的操作都会被丢在这个异步队列中。当主线程空闲(同步代码)之后会执行异步队列中的代码,这个就是 js 中的 eventloop(事件循环);
宏任务,是运行环境提供的异步操作,例如:setTimeout;
微任务,是 js 语言自身的异步操作,例如:Promise;
在一个宏任务周期中会执行当前周期中的所有微任务,当所有的微任务都执行完成之后会进入下一个宏任务周期;
JS 中的继承
1、原型链继承
核心:将父类的实例作为子类的原型
2、构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
3、实例继承
核心:为父类实例添加新特性,作为子类实例返回
4、组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
5、寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
ESlass 继承,constructor()和 super()关键字
super()方法用来调用父类的构造函数;
constructor 是原型对象上的一个属性,默认指向这个原型的构造函数;
Map 与 Set
- Map
- Map 的键可以是任意值。包括对象 a:1:xxx
- .set() var myMap = new Map(); myMap.set(0, "zero");
- .get()
- .delet()
- Set
- Set 的键名是唯一的,不能重复,undefined 和 NaN 也不重复
- 用来去重
var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]
js 中 Map 和 Object 有什么区别?
- Map 是 es6 新增的一种数据结构,继承自 Object,并对 Object 做了一些拓展。
- Map 的键可以是任意的数据类型,Object 不行。
- Map 是可迭代(iterable)的,可以使用 for...of 遍历,而 Object 不行。
- Map 的长度可以通过 size 直接拿到,Object 不行。
- Map 使用拓展运算符是可以直接展开的,Object 不行。
js 中的数据类型
一般数据类型:string number boolean null undefined symbol bigint
`BigInt`数据类型的目的是比`Number`数据类型支持的范围更大的整数值。
复杂数据类型:对象、数组、function
- 值为一个地址,地址指向真正的数据
js 中如何准确判断一个数据的数据类型?
typeof 如果是基本数据类型,使用typeof来判断,但是typeof不能用来判断引用数据类型的数据
不能判断Null、Array等
instanceof 不能检测Null和undefined
contructor
Object.prototype.toString.call( )
typeof 区分原理
typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。
typeof null 为"object", 原因是因为 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"。
类型判断
typeof
用 typeof 检测**null**返回是 object。在 JavaScript 中 null 表示 "什么都没有"。null 是一个只有一个值的特殊类型。表示一个空对象引用。
typeof 一个**没有值的变量**会返回 **undefined**。
null 和 undefined 的值相等,但类型不等:
typeof "John"; // string
typeof 3.14; // number
typeof false; // boolean
typeof [1, 2, 3, 4]; // object 在JavaScript中,数组是一种特殊的对象类型。 因此 typeof [1,2,3,4] 返回 object。
typeof name: "John" ; // object
typeof undefined; // undefined
typeof null; // object
null === undefined; // false
null == undefined; // true
instanceof
- obj instanceof Object ,可以左边放你要判断的内容,右边放类型来进行 JS 类型判断
- 需要知道是什么类型才能正确判断
[1, 2] instanceof
Array(
// true
function ()
) instanceof
Function(
// true
a: 1
) instanceof
Object(
// true
new Date()
) instanceof
Date; // true
object.prototype.toString.call
Object.prototype.toString.call(999); // "[object Number]"
Object.prototype.toString.call(""); // "[object String]"
Object.prototype.toString.call(Symbol()); // "[object Symbol]"
Object.prototype.toString.call(42n); // "[object BigInt]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(true); // "[object Boolean]
Object.prototype.toString.call( a: 1 ); // "[object Object]"
Object.prototype.toString.call([1, 2]); // "[object Array]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(function () ); // "[object Function]"
类型转换
toString(); // 转化为字符串,不可以转null和underfined
String(); // 转换为字符串,
Number(); // 转换为数字,字符串中有一个不是数值的字符,返回NaN
parseInt(); // 转换为数字,第一个字符不是数字或者符号就返回NaN
Boolean(); // 转换为布尔值
函数声明和函数赋值语句的差别:
赋值式函数需要给变量赋值,以 var、let;声明式是不需要给变量赋值,以 function 开头;
对象的常用方法
Object.assign(target,source1,source2,...)
- - 该方法主要用于对象的合并,将源对象 source 的所有可枚举属性合并到目标对象 target 上,此方法只拷贝源对象的自身属性,不拷贝继承的属性。
- - Object.assign 方法实行的是浅拷贝,而不是深拷贝,当只有一层时为深拷贝
- - 同名属性会替换
Object.create()
- - Object.create()方法接受两个参数:Object.create(obj , propertiesObject) ;
- - propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与 Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。
- - 没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法
hasOwnProperty()
- - 判断对象自身属性中是否具有指定的属性。obj.hasOwnProperty('name')
Object.is()
- - 判断两个值是否相同。
Object.is('foo', 'foo')// true
Object.is(window, window)// true
Object.is('foo', 'bar'); // false
Object.is([], []); // false
var test = a: 1 ;
Object.is(test, test); // true
Object.is(null, null); // true
// 特例
Object.is(0, -0); // false
Object.is(-0, -0); // true
Object.is(NaN, 0 / 0); )// true
Object.keys(obj)
- 返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)
let arr = ["a", "b", "c"];
Object.keys(arr); //["0", "1", "2"]
let obj = age: 20, sex: "男" ;
Object.keys(obj); //["age", "sex"]
Object.values()
- - 方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用 for...in 循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
var obj = 10: "a", 1: "b", 2: "c" ;
Object.values(obj); // ['b', 'c', 'a']
var obj1 = 0: "a", 1: "b", 2: "c" ;
Object.values(obj1); // ['a', 'b', 'c']
entries
- - 分割对象,将对象分割为数组
const obj = foo: "bar", baz: 42 ;
Object.entries(obj); // [ ['foo', 'bar'], ['baz', 42] ]
// array like object
const obj = 0: "a", 1: "b", 2: "c" ;
Object.entries(obj); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// string
Object.entries("abc"); // [['0', 'a'], ['1', 'b'], ['2', 'c']]
Object.entries(100); // []
判断两对象是否相等
1、通过JSON.stringify(obj)来判断两个对象转后的字符串是否相等
2、getOwnPropertyNames方法返回一个由指定对象的所有自身属性的属性名组成的数组。先进行长度的比较,然后进行遍历
3.Object.is(a,b)
// 手写:
function diff(obj1,obj2)
var o1 = obj1 instanceof Object;
var o2 = obj2 instanceof Object;
// 判断是不是对象
if (!o1 || !o2)
return obj1 === obj2;
//Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,
//例如:数组返回下表:let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
if (Object.keys(obj1).length !== Object.keys(obj2).length)
return false;
for (var o in obj1)
var t1 = obj1[o] instanceof Object;
var t2 = obj2[o] instanceof Object;
if (t1 && t2)
return diff(obj1[o], obj2[o]);
else if (obj1[o] !== obj2[o])
return false;
return true;
两个对象如果属性名和属性值都一样,他俩是否相等?为什么?
// 他俩不相等,因为对象的存储位置不一样,所以永远不相等
function compareTwoObj(a, b)
return JSON.stringify(a) == JSON.stringify(b);
获取对象中的所有属性名
Object.keys(obj); // 数组,所有属性名组成的数组
for (let k in obj)
json 字符串如何转换为 js 对象
js对象转字符串
JSON.stringify
// json字符串转对象
JSON.parse
如何删除对象中的一个属性
var obj = a: 1, b: 2, c: 3 ;
delete obj.c; // 删除一个属性
var strJson = `"a": "123", "1-a": "456"`;
面向对象
面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成类
面向对象的三大基本特征:封装、继承、多态
- 封装就是把一些通用的代码或者功能封装在一个文件中
- 继承就是子类可以继承父类的属性和方法,实现代码复用
- 多态就是一个 function 接收的参数不一样,返回不同的结果
//多态演示
// 构造函数
function Behavior ()
Behavior.prototype.sound = function (sound)
return sound
function Man ()
Object.setPrototypeOf(Man.prototype, Behavior.prototype)
function Woman ()
Object.setPrototypeOf(Woman.prototype, Behavior.prototype)
let man = new Man()
console.log(man.sound('哈哈哈')) // 哈哈哈
let woman = new Woman()
console.log(woman.sound('嘻嘻嘻')) // 嘻嘻嘻
对象常用的操作
- 获取所有的属性名,Object.keys、for in
- 获取所有的属性值,Object.values
- 删除一个属性
- 动态获取属性,可以使用数组下标的方式
事件
js 中的常用事件绑定方法
1. 在DOM元素中直接绑定
2. 在JavaScript代码中绑定
3. 绑定事件监听函数
事件传播流程
三个阶段:捕获、事件源、冒泡
捕获是从 html 标签开始,逐层往内进行传递,到触发事件的标签为止
事件源是在当前触发事件的标签上进行传递
冒泡是从当前触发事件的标签开始逐层往外进行传递到 html 节点结束
addEventLister 的第三个参数,是一个 bool 值。默认是 false,表示在冒泡阶段触发
event 对象中的 target 和 currentTarget 有什么区别?
target 触发事件的标签
current Target 绑定事件的标签
addEventListener,第三个参数是一个 bool 值,默认是 false 表示在冒泡阶段触发事件
事件监听、事件委托
- 事件监听
- addEventListener,第三个参数是一个 bool 值,默认是 false 表示在冒泡阶段触发事件
- addEventListener(事件类型,处理函数,布尔值) 布尔值 I.false 事件冒泡 默认 II.true 事件捕获
- 使用事件监听器的好处:
使用事件监听器方式添加事件,可以给同一个 DOM 对象添加多个相同的事件名,对应事件处理函数会依次执行
普通事件添加方式,如果添加多个相同的事件名,后面的会覆盖前面,只会执行最后一个
on类型的事件在事件冒泡阶段触发
target(触发事件的标签)/currentTarget(绑定事件的标签)
**DOM标准事件流的触发的先后顺序为:先捕获再冒泡**
**阻止冒泡**:stopPropagation || cancelBubble
#### 事件默认行为
a标签的跳转,input框的输入,Submit按钮的提交,右键菜单
**阻止默认事件**:returnValue=false || preventDeafault
- 事件委托
- - 利用事件冒泡的原理,将本应该添加某个 DOM 对象上的事件委托给他的父级
- - 让新增的 DOM 具有相同的事件,减少事件添加的次数,提高执行效率
- - 使用场景:大量标签绑定同一事件,动态渲染的标签
事件 onxx 和 addEventListener 有什么区别
onClick 表示添加一个点击事件,属于属性赋值。属性多次赋值会出现覆盖,只有最后一次的赋值是有效果的
addEventlistener 事件委托,可以添加多次,每一次添加都会执行,它可以接收三个参数('事件名字', 回调函数, bool)
bool 默认是 false 表示在冒泡阶段触发
事件拖拽
01-在允许拖拽的节点元素上,使用 on 来监听 mousedown(按下鼠标按钮)事件,鼠标按下后,克隆当前节点
02-监听 mousemove(鼠标移动)事件,修改克隆出来的节点的坐标,实现节点跟随鼠标的效果
03-监听 mouseup(放开鼠标按钮)事件,将原节点克隆到鼠标放下位置的容器里,删除原节点,拖拽完成。
DOM/BOM
BOM 是 browser object model 的缩写,简称浏览器对象模型。是用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。 比如 alert();弹出一个窗口,这属于 BOM,核心对象是 window。
DOM 是 Document ,简称文档对象模型。是用来获取或设置文档中标签的属性,例如获取或者设置 input 表单的 value 值。document.getElementById("").value; 这属于 DOM,核心对象是 标签。
说出几个常用的 BOM 对象和方法
- alert('提示信息')
- confirm("确认信息")
- prompt("弹出输入框")
- open("url 地址",“black 或 self”,“新窗口的大小”)
- close() 关闭当前的网页
常见的 dom 元素选择方式有哪些
- getElementById
- getElementsByClassName
- getElementsByTagName
- querySelector
- querySelectorAll
dataset 数据集
是为标签添加的属性 data-开头的这些,在 js 中可以直接通过使用 dom 对象的 dataset 属性获取
设计模型
工厂模式、单例模式、原型模式,组合模式,发布-订阅模式
jQuery 优点和缺点
优点:出色的浏览器兼容性;出色的DOM操作的封装,可以进行快速的DOM元素操作;支持链式操作,支持插件
缺点:不能向后兼容,一个页面使用多个插件容易冲突,对动画和特效的支持差;
axios 和 ajax
Ajax 核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱
axios支持 Promise API,提供了一些并发请求的接口,自动转换JSON数据,体积也较小;
什么是 mvc
mvc 是一种开发模式
model,用来存储数据
view,用来展示数据
controller [kənˈtrəʊlə(r)] ,用来控制数据的展示方式
第二板块:计算机网络
https,使用 ssl 证书进行加密传输 防止网络抓包 保证信息安全
js 中发起请求的方式:xhr、fetch、websocket
介绍 http 请求报文
请求行------请求行有请求方法、URL和HTTP协议版本3个字段
请求头(携带数据) cookies token content-type
请求体 传输数据 发起post或get请求
数据传输的格式
- json '"name":"zhangsan"'
- urlencoded name=Tom&&age=18
- form-data 此格式数据常常用于文件上传
常见状态码
状态码:200 多,300 多,400 多,500 多
200:成功
301:永久重定向、302:临时重定向
401:未授权
403:服务器拒绝访问
404:页面找不到
500:服务器错误
设计模式
- 单例模式
- 观察者模式
- 工厂模式
- 发布订阅模式
get 和 post 的区别
get 用来获取数据,参数传递的时候在 url 中可以直接看到,不太安全。传递的数据量比较少,可以加入收藏夹
post 用来传递大量数据,数据在请求体中进行传递,url 地址中看不到,相对于 get 请求更安全
1、常见的请求方式
- get 获取数据
- post 新增数据
- put/patch 修改数据
- delete 删除数据
2、 CRUD(增删改查)
- Create
- Read
- Update
- Delete
3、SSR 服务器端渲染
- php
- java
- python
- node.js
4、get和post的区别
- get请求参数明文传输,在url中传递
- post请求在请求体中传输
- post请求相对get请求更安全一些
- post请求传输的参数大小比get请求大
- get传送的数据量不能大于2kb,post传送的数据量较大
- get请求可以加入收藏夹
- get请求常常用于获取数据,请求速度快
5、get请求传递的参数为什么有大小限制?
get请求在url中传递,不同浏览器对url长度有限制
http 三次握手以及常见的状态码?
- 第一次握手是客户端给服务端发送请求,请求建立连接。
- 第二次握手是服务端给客户端发送请求,表示已经收到客户端的请求,并且同意建立连接。
- 第三次握手是客户端向服务端发送请求,表示确认收到服务端发送的信息。
- 三次握手的原因是为了确认客户端和服务端都有收发信息的能力,少一次确认不了,多一次浪费资源。
四次挥手
- - 第一次挥手:先由客户端向服务器端发送一个 FIN,请求关闭数据传输。
- - 第二次挥手:当服务器接收到客户端的 FIN 时,向客户端发送一个 ACK,其中 ack 的值等于 FIN+SEQ
- - 第三次挥手:然后服务器向客户端发送一个 FIN,告诉客户端应用程序关闭。
- - 第四次挥手:当客户端收到服务器端的 FIN 是,回复一个 ACK 给服务器端。其中 ack 的值等于 FIN+SEQ
网页从开始到加载出来经历了什么
- - 输入网址之后会做域名解析
- - 根据 ip 地址查找服务器,开始请求数据
- - 服务器返回数据,浏览器开始解析
- - 浏览器再一次请求页面中使用的资源文件
- - 解析展示整个页面
浏览器渲染引擎的工作原理和工作流程?
1. 解析 HTML 文档,构建 DOM 树(DOM 节点树。
2. 解析 CSS 文档,生成 CSS 规则树。
3. 合并 DOM 树和 CSS 规则树,生成渲染树 render 树。
4. 布局 render 树,计算每个元素的大小,位置等信息(重排会走这一步)。
5. 绘制 render 树,绘制页面的像素信息,(根据 render 树上每个节点的几何属性计算每个节点的像素数,重绘会走这一步)
6. 浏览器会将各层节点的像素信息发给 GPU,GPU 将各层进行合成和渲染,最终展示到页面上。
浏览器安全问题,https 协议,https 与 http 区别
- HTTP 协议工作在 80 端口,HTTPS 协议工作在 443 端口
- HTTPS 需要申请证书(用于验证服务器身份)
- HTTP 在 TCP 三次握手建立连接之后即可开始传输数据;HTTPS 协议则需要在建立 TCP 连接之后客户端与服务器在进行 SSL 加密,确定对话密钥,完成加密后才开始传输数据。
- HTTPS 协议协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全;
HTTP 协议:是超文本传输协议,信息是明文传输;
HTTP2.0 和 HTTP1.0 的区别
- HTTP2 使用的是二进制传送,HTTP1.X 是文本(字符串)传送
- HTTP2 支持路复用
介绍一下 XSS 攻击和 CSRF?
- XSS(跨站脚本攻击),是一种代码注入攻击,是通过在网站中注入恶意代码达到劫持用户 cookie 或其他信息的一种攻击。
- 客户端:限制输入长度
- 上线前:使用扫描工具自动检测 XSS 漏洞
- CSRF(Cross-site request forgery)跨站请求伪造
- 服务端做一些防御操
跨域
域名协议端口号只要有一个不一致就会引起跨域,跨域是浏览器的安全机制导致的,只有在浏览器中有;
跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果。
跨域如何解决:
1、前端我们解决不了,都是服务器端解决的;
2、在开发的时候:我可以通过配置代理服务器或者浏览器安装插件的方式临时解决,但是上线之后还是需要服务器做配置;
3、通过 jsonp 跨域
jsonp是一种跨域通信的手段,它的原理其实很简单:首先是利用script标签的src属性来实现跨域。通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。由于使用script标签的src属性,JSONP 使用简单且兼容性不错,但是只限于 get 请求。
4、postMessage 跨域
5、跨域资源共享(CORS)
需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。浏览器会自动进行
CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
//谷歌插件 corsf
如果面试官问:“CORS 为什么支持跨域的通信?”
答案:跨域时,浏览器会拦截 Ajax 请求,并在 http 头中加 Origin。
6、nginx 代理跨域
7、nodejs 中间件代理跨域
8、WebSocket 协议跨域
同步和异步
同步叫阻塞模式,异步叫非阻塞模式
异步的实现原理:js 中的事件轮训(eventloop),微任务和宏任务
异步实现的原理:
在js中有一个异步回调队列,当遇到异步任务的时候会把这个任务直接转到异步队列中。等待所有的同步任务都完成之后在执行异步队列
异步队列中的任务会分为微任务和宏任务
每一个宏任务周期中会把当前周期中的所有微任务都执行完成之后,在执行下一个宏任务周期
宏任务:所有的运行环境提供的异步任务
微任务:所有的语言自带的异步任务
什么是异步加载?异步加载和延迟加载有什么区别?
异步加载时相对于同步加载而言的,我们平常书写的代码就是同步加载,代码自上而下执行,是阻塞式的,而异步加载是非阻塞式的,在执行同步代码时,并不会阻塞我后续代码的执行,(例如定时器,发送 ajax 请求),而延迟加载则是一开始并不加载,在我需要的时候再进行加载(例如图片的懒加载)
异步解决方法
(1)回调函数callback
(2)Promise
(3)Generator
(4)async/await
垃圾回收机制
js 会自动的进行垃圾回收,就是那些无用的代码和运行任务会在结束之后从内存中清理出来。js 中的垃圾回收使用的是标记清除法
节流和防抖
防抖:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时(本质是一个定时器)。
例如:搜索框的联想功能,不断输入值的时候,使用防抖来节约资源。(短信验证码、提交表单、resize 事件等)
节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
例如:scroll 事件,单位时间后计算一次滚动位置,input 事件(上面提到过),播放事件,计算进度条
两者的共同点:
函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
函数防抖是某一段时间内只执行一次,而函数节流是单位时间执行一次。
回流和重绘
回流一定会触发重绘,但是重绘不一定会引起回流
改变页面布局或者大小尺寸的时候会引起回流,反之就是重绘
重绘:当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新 ,UI 层面, 例如改变元素颜色
回流:又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流
Url 从输入到浏览器显示的页面
1. 网络通信
- 在浏览器中输入 url
- 应用层 DNS 解析域名
- 应用层客户端发送 HTTP 请求
- 传输层 TCP 传输报文
- 网络层 IP 协议查询 MAC 地址
- 数据到达数据链路层
- 服务器接收数据
- 服务器响应请求
- 服务器返回相应文件
2. 页面渲染
cookie,localstorage,sessionstorage 的区别?
- cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
- localStorage:除非被手动清除,否则将会永久保存。
- sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。同源不共享。
- cookie:4KB 左右 localStorage 和 sessionStorage:可以保存 5MB 的信息。
- cookie:每次都会携带在 HTTP 头中,如果使用 cookie 保存过多- 数据会带来性能问题
- localStorage 和 sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信
token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token
的前几位以哈希算法压缩成的一定长度的十六进制字符串)
特点:服务端无状态化、可扩展性好支持移动端设备.安全.支持跨程序调用
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。**用解析
token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库**
token 完全由应用管理,所以它可以避开同源策略
获取用户浏览器信息、浏览器查询
navigator.userAgent 用户代理
获取 url 地址信息并解析 url 参数
location.href
location.search
- 通过字符串截取获取 url 中的参数
优化
web 项目性能优化
- 减少 http 请求次数
- 资源文件压缩合并,js 和 css 文件压缩合并
- 使用精灵图片
- 如果图片特别多,使用图片懒加载
- 使用 cdn 的方式加载第三方资源【服务器端运维的范畴】
- 开启 web 服务器的 GZIP 压缩【服务器端运维的范畴】
- CDN
属于运维的范畴,和前端没有关系。我们以后如果要开启服务器的 CDN 加速,只需要买节点就好
seo 搜索引擎优化
- - 为网页添加关键字和描述信息,在 meta 标签中,
- - 删除页面中无用的空标签,为图片添加 alt 属性
- - 去除不必要的空格, 注释 减少文件的总尺寸,较小的页面可以获得更快的加载速度
- - 把 CSS 放在顶端: css 文件的加载不会阻塞 dom 树的解析,把 CSS 文件放在网站的顶端,可以让网站尽可能同时加载其他部件,如图片和文字。
- - 设置页面中的 h1-h6 标签 每个页面只出现一个 h1 标签,h2 可以出现多次
- - 不定期的更新网站的内容,增加收录量
- - 优化网站代码,减少页面加载时间
- - 购买外链【花钱】
- - 百度竞价排名【花钱】
- - 重要内容优先加载
- - 语义化 HTML 代码 ,符合 W3C 标准
- - 少用 iframe :搜索引擎不会抓取 iframe 中的内容
什么是精灵图片,实现原理是什么?
就是把小图标集中放置在一张图片上,使用背景定位的方式展示每一个小图标
如何实现局部更新?
- ajax,异步的 javascript 和 XML;
- websocket,长连接。建立起链接之后,服务器端可以主动的向客户端推送数据;
网络传输的时候数据安全性如何保障
https,使用 ssl 证书进行加密传输
你做的页面都在哪些浏览器测试过,这些浏览器的内核分别是什么?
- IE 浏览器:Trident 内核
- Chrome 浏览器:webkit(过去)、blink(现在)
- FireFox:Gecko 内核
- Edge:chormium 内核
- Safari:webkit 内核
- 国内主要兼容 360 安全浏览器,极速模式和兼容模式,分别对应谷歌的 blink 内核和 IE 的 trident 内核
- 电脑中浏览器的兼容性(360、qq 浏览器、搜狗)
- 手机浏览器的浏览器兼容性(一般手机,小米手机,ios 手机),微信浏览器。
你是如何解决这些兼容性问题的?
- 答:我们都是浏览器挨个去调试,或者加浏览器前缀,或者 jQuery 来解决 js 的兼容问题,避免使用 h5 和 css3 新增的标签和样式。
tcp 和 udp 的区别
tcp 是面向连接的;udp 不是
第三板块:HTML
块元素
div dl form h1 h2 h3 h4 h5 h6 hr ol li p pre table td tr
行内元素
a b s i u span br img input textarea sub sup
行内元素和块级元素 空元素的特点
行内元素:
1.无法设置宽高;
2. 对margin仅设置左右有效,上下无效;
3. padding上下左右有效;不会自动换行
块级元素:
1.可以设置宽高
2. margin和padding的上下左右均对其有效
3. 超出当前行会自动换行
4. 多个块状元素标签写在一起,默认排列方式为从上至下
img:属于行内块元素(inline-block),即有行内元素的属性也有块级元素的属性
元素之间的转化可以通过设置样式:display:block/inline/inline-block来改变自身的元素属性
空元素:
没有闭合标签的标签被称作为空标签。<br/> <hr/>
在我们使用的标签中,有的具有闭合标签<td></td>
H5 语义化标签(H5 新增)
- header、nav、footer、main、section、article、address 等等
- 语义化标签就是那些看到单词就知道是做什么的标签,它的优势是便于 seo 优化和可读性更强
多媒体标签(H5 新增特性)
- 如何在网页中添加一个多媒体资源
audio、video
我们在实际开发适合可能需要使用到插件:flv.js、video.js、dplayer、hls.js
* 新的表单属性(time、email、url、search)
第四板块:CSS
css3 新增特性
- 阴影:文字阴影 text-shadow 盒子阴影:box-shadow
- 图片背景:background-size background-origin
- 边框:border-radius border-image:CSS3 边框图片
- 伪类(:link,:visited,:active,:hover,:focus,:before,:after)
* 渐变 background-image: linear-gradient(#e66465, #9198e5);
* 过渡 transition: width 2s, height 2s;
* 动画 animation @keyframes
伪类
:checked | input:checked | 选择所有选中的表单元素 |
:link | a:link | 选择所有未访问链接 |
:visited | a:visited | 选择所有访问过的链接 |
:active | a:active | 选择正在活动链接 |
:hover | a:hover | 把鼠标放在链接上的状态 |
:focus | input:focus | 选择元素输入后具有焦点 |
:before | p:before | 在每个 元素之前插入内容 |
:after | p:after | 在每个 元素之后插入内容 |
媒体查询
- @media
伪元素
content 属性
选择器 | 示例 | 示例说明 |
---|---|---|
:before | p:before | 在每个 元素之前插入内容 |
:after | p:after | 在每个 元素之后插入内容 |
flex 布局
display:flex; 设置为弹性盒(父元素添加)
flex-direction:row(默认)/column(子元素竖向显示)
justify-content:; 主轴对齐方式()
fle-start 开头
flex-end 结尾
center 居中
space-around 空间在子元素两边平均分布
space-betwee以上是关于前端面试八股文(详细版)—上的主要内容,如果未能解决你的问题,请参考以下文章