前端面试小册
常考面试题一
1、原始类型有那种?nulll是对象吗?
原始数据类型主要分为:
number,string,Boolean,null,undefind
null是对象
2、对象类型和基础类型的不同之处,函数参数是对象类型和基础类型分别会如何?
基础类型按值储存在栈内存中
对象类型(引用类型)储存在堆内存中。以指针方式去使用
函数参数和复制操作:
基础数据类型在重新创建一份新值。改变其中任何一个,另一个不受影响
引用数据类型。变量对象也会为其开辟空间,但复制的是指针,都指向内存的值,一个改变,另一个也回受影响
3、typeof 与 instanceof?
typeof 与 instanceof都是判断数据类型的。区别如下:
typeof判断基础数据类型。当判断null的时候将返回objcet
instanceof判断引用数据类型,原理根据原型链追踪去实现
4、类型转换?
- 转数字?
null=》0
undefined=》NaN
Boolean
true =》1
false=》0
Sting
数字=》数字
字母=》NaN
空字符串=》0
数组
空数组=》0
一个元素且为数字=》数字
其他=》NaN
引用类型
NaN - 转字符串
underfind=》‘underfind’
null=》‘null’
Number
'数字'
Boolean
‘true’
‘false’
数组
‘【12.43,323】’=>12.43,323
对象
[object Object]
function
function(){console.log(212)} - 转布尔值?
null
false
underfind
false
Number
0,-0,NaN=>false
其他=>true
String
''=>false
其他=>true
引用类型
true
5、对象转基础数据类型?
转字符串类型直接调用toString()
转其他类型先转valueOf()
6、四则运算?
运算中其中一方是字符串,另一个方也将转换成字符串
如果一方不是字符串数字。那么会将它转成数字或者字符串
除了加减运算之外。一方为数字,另一方转为数字
7、比较运算符?
如果是对象,就通过 toPrimitive 转换对象
如果是字符串,就通过 unicode 字符索引来比较
8、如何判断this指向,箭头函数中this指向那里?
- 全局this
window
- 函数中的this
对象调用,则指向这个对象
独立调用,指向window - 使用call,apply显示指定this
apply.call,bind动态this
- 构造函数与原型方法上的this
指向当前实例化后的对象。
- 箭头函数中的this
调用箭头函数外层第一个普通函数的this
常考面试题二
==和===的区别?
- ==
先判断数据类型是否相同,一致判断值大小
类型不同,进行类型转换
先判断是否是null和underfind的比较,是返回true
判断是否是string和number的比较,是先将字符串转成对应的数字进行比较
判断一方是否为布尔值,是则把布尔值换成number进行比较
判断一方是否为object,且另外一方为string,number,是则把object转成基础类型进行判断 - ===
不会尽享类型转换,直接对比值的大小
闭包
- 什么是闭包?
在函数执行上下A
在执行上下文A中的函数B
调用了A中的变量。闭包产生 - 闭包的好处?以及和垃圾回收机制的关系?
在js中,函数上下文执行完之后,生命周期结束后。垃圾回收机制就会回收内部不被使用的变量,也就是内存中失去引用的变量,对其进行回收。闭包会阻止此过程
js中具有自动垃圾回收机制,对于函数内部的变量失去引用之后,很快会被回收,但是处于全局的变量,js不会回收,除非引用完及时释放,尽量少使用全局变量
浅拷贝和深拷贝
- 浅拷贝
基础类型值的拷贝
引用类型拷贝的引用地址(指针)
一个改变另一个必受影响
Object.assign()和...都是浅拷贝 -
深拷贝
重新开辟内存空间,互相不影响
function deepClone(obj) { function isObject(o) { return (typeof o == 'object' || typeof o == 'function') && o !== null } if (!isObject(obj)) { throw new Error('this is not objcet') } let isArray = Array.isArray(obj) let newObj = isArray ? [...obj] : { ...obj } Reflect.ownKeys(newObj).forEach(key => { newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] }) return newObj }
原型链
对象的 __proto__ 属性指向原型, __proto__ 将对象和原型连接起来组成了原型链
- 构造函数
- 原型
- 原型
es6常考知识点
什么是提升?什么是暂时性死区?var、let 及 const 区别?
- 提升
当控制器执行到可执行的代码的时候,js就会创建执行上下文
执行上下文的创建阶段会创建变量对象。此时
此时会先创建arguments对象
检查函数,以函数声明并创建属性
检查var声明的变量,复制underfind
其实这个就是所谓的变量提升阶段 - var 、const、let的区别?
区别:
var存在变量提升
可以重复声明
可以用window去调用
const、let
不存在变量提升
不可重复声明
拥有块级作用域
不可通过window去调用
const赋值不可更改 - 暂时性死区
当变量未声明之前。去调用此变量,被叫做暂时性死区
原型如何实现继承?Class 如何实现继承?Class 本质是什么?
-
原型继承
- 组合继承
构造函数继承通过
call改变this指向去继承上面的方法和属性
原型上的继承
将子类的原型指向父级的实例话对象 - 寄生继承
构造函数继承同样使用call改变指向进行继承
原型继承则通过Object.create()进行继承
- 组合继承
- class继承
class 实现继承的核心在于使用 extends 表明继承自哪个父类
并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value) - class本质
class 的本质就是函数。
为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?
- 模块化的使用
解决命名冲突
实现复用
提高代码的可维护程度
避免变量全局污染 - 匿名函数自执行
- ES Module
不支持动态引入
Proxy 可以实现什么功能?
-
对象的代理
代理,可是实现数据的双向绑定
let handler = { get(target, property, receiver) { getLogger(target, property) return Reflect.get(target, property, receiver) }, set(target, property, value, receiver) { setBind(value, property) return Reflect.set(target, property, value) } } return new Proxy(obj, handler) } let p = onWatch( obj, (v, property) => { console.log(`监听到属性${property}改变为${v}`) }, (target, property) => { console.log(`'${property}' = ${target[property]}`) } ) p.a = 2 // 监听到属性a改变 console.log(p.a)
- 表单校验
- 阅后即焚
- 过滤不存在的属性
map, filter, reduce
- map
返回一个新数组。可以在这个函数中加逻辑处理返回
接受三个参数
当前的元素
index
愿数组 - filter
返回一个新数组。对符合条件元素进行返回
接受三个参数
当前的元素
index
愿数组 - reduce
数组的计算
接受两个参数,分别是回调函数和初始值
性能优化琐碎事
图片优化
常考算法知识点
Vue 常考基础知识点
生命周期
父子组建传旨
设计模式
工厂模式
隐藏了这个复杂的过程,只需要一句代码调用就能实现功能。
单例模式
隐藏了这个复杂的过程,只需要一句代码调用就能实现功能。
适配器模式
适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。
class Man {
getName() {
return '港版插头'
}
}
class Son {
constructor() {
this.proxy = new Man()
}
getName() {
return this.proxy.getName() + 'hahahahh'
}
static create(name) {
console.log(name)
}
}
let s = new Son()
console.log(s.getName())
装饰模式
代理模式
代理是为了控制对对象的访问,不让外部直接访问到对象。在现实生活中,也有很多代理的场景。比如事件代理
发布-订阅模式
发布-订阅模式也叫做观察者模式。通过一对一或者一对多的依赖关系,当对象发生改变时,订阅方都会收到通知。
外观模式
JS 异步编程及常考面试题
并发(concurrency)和并行(parallelism)区别?
并发是一段时间相继完成,比如A和B,一段时间内切换完成类似早上起床前。洗漱,吃饭,出门。一段时间完成的一些事
并行是同时进行,比如边看手机边吃饭
什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题?
当我们异步请求成功时会在回调函数中写我们的逻辑。当然这里的逻辑还涉及到进一步的请求,不断嵌套就会出现回调地狱
回调函数缺点:
嵌套过深,耦合度高,不易维护
难以捕捉错误
不能使用try。。catch
使用Generator和promise以及async都可以
你理解的 Generator 是什么?
天然的迭代器,可以是遍历停下来
可控制迭代器的函数,可以暂停,也可以选择任何时候恢复
使用场景:
抽奖
小游戏
斐波那契数列
Promise 的特点是什么,分别有什么优缺点?什么是 Promise 链?Promise 构造函数执行和 then 函数执行有什么区别?
特点:
Promise三个状态,padding,resolve和reject。状态不可逆,一旦确认,无法改变
Promise链:
通过then方法去传递执行,执行then方法之后会返回一个promise对象,来完成链式操作
构造函数和then函数执行有哪些区别?
构造函数内的内容立即执行。
then的函数当状态改变在进行执行
async 及 await 的特点,它们的优点和缺点分别是什么?await 原理是什么?
优化了promise链式调用,以同步的写法去操作异步代码
原理其实是promise的语法糖,await 内部通过promise静态方法Promise.resove()返回一个新的promise
setTimeout、setInterval、requestAnimationFrame 各有什么特点?
JS 进阶知识点及常考面试题
apply
Function.prototype.myApply = function (context, arr) {
if (typeof this !== 'function') {
throw new Error('error')
}
context = context || window
context.fn = this
let reslut;
if (arr) {
reslut = context.fn(...arr)
} else {
reslut = context.fn()
}
delete context.fn
return reslut
}
call
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
throw new Error('error')
}
context = context || window
context.fn = this
let args = [...arguments].slice(1)
let reslut = context.fn(...args)
delete context.fn
return reslut
}
bind
Function.prototype.myApply = function (context, arr) {
if (typeof this !== 'function') {
throw new Error('error')
}
context = context || window
context.fn = this
let reslut;
reslut = arr ? context.fn(...arr) : context.fn()
delete context.fn
return reslut
}
new
function news() {
let obj = new Object()
let Constructor = [...arguments].shift()
obj.__proto__ = Constructor.prototype
let res = constructor.apply(obj, arguments)
return typeof res === 'object' ? ret : obj
}
instanceof
function instanceofs(left, right) {
let prototype = right.prototype
left = left.__proto__
while (1) {
if (left == null || left == undefined) {
return false
}
if (left == prototype) {
return true
}
left = left.__proto__
}
}
垃圾回收机制
v8实现了精确式GC,GC算法采用分代垃圾回收机制,因此。v8将内存(堆)分为新生代和老生代两部分
-
新生代算法
对象存活时间较短,使用 Scavenge GC 算法
在新生代内存空间分为2部分,分别是from空间和to空间,在这2个空间其中必然有一个是空闲的。新分配的对象会放到from空间,当from空间被占满的时候,新生代GC就会启动,算法会检查from空间的存活对象,将他们复制到to空间,其中失去存活的对象就会被销毁,将复制完,from和to空间互换,- from空间
- to空间
-
老生代算法
- 什么时候启动标记清除算法?
某一个空间没有分块时
空间中被对象超过一段限制时
空间不能保证新生代对象转到老生代中
这个时候遍历堆中所有活的对象,在标记完成后,销毁那些没有标记的对象。 - 标记整理算法
清除对象后会造成堆内存出现碎片的情况
当碎片超过一定限制的时,会启动压缩算法。在压缩过程中,将活的对象像一端移动,直到所有对象都移动完成然后清理掉不需要的内存。 - 什么时候对象会出现老生代算法中?
新生代中的对象是否已经经历过Scavenge算法。如果经历过,会将新生代空间转到老生代空间中
当to空间的对象占空间大小的25%,这样情况下。为了不影响内存分配。会将对象从新生代的空间转老生代空间中
- 什么时候启动标记清除算法?
浏览器基础知识点及常考面试题
跨域
- 什么是跨域?
浏览器出与安全考虑。有同源策略。也就是当协议。域名。端口号任何一个不同就是跨域。Ajax就会请求失败
- 为什么浏览器要使用同源策略?
其实主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。
- 你有几种方式可以解决跨域问题?
jsonp
cors
document.domain
postMessage - 了解预检请求嘛?
对于复杂请求来说,首先会发起一个预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。
事件
-
事件处理程序
- IE事件处理程序
增加和和删除事件:
element.attachEvent('on'+事件类型,处理函数)
element.detachEvent(‘on’+事件类型,处理函数) - chrome事件处理程序
element.addEventListener('click',处理函数,是否冒泡)
false(默认冒泡)
true(捕获)
element.removeEventListener('click',处理程序,是否冒泡)
同添加事件一致
- IE事件处理程序
-
事件对象
- IE事件对象
cancelBubble =》默认值为false,但设置true就可以取消事件冒泡
returnValue =》默认true,设置为false,,就可以取消事件的默认行为
srcElement =>事件目标
type=>事件类型 - chrome事件对象
stopPropagation =》取消事件冒泡
preventDefault =》取消事件的默认行为
target =>事件目标
type=>事件类型
- IE事件对象
- 事件代理
如果一个节点中子节点是动态生成的,那么子节点需要注册事件的化应该注册在父节点上
好处:
节省内存空间
不需要给子节点注销事件 -
事件的触发过程是怎么样的?知道什么是事件代理嘛?
事件触发有三个阶段:
window往事件触发传播。遇到注册的捕获事件会触发
传播到事件触发时触发注册事件
从事件触发之处向window传播。遇到注册的冒泡会触发
从捕获到目标阶段再到冒泡的过程- 事件捕获
从上向下执行的一个过程
Document=》html=》body=》div - 事件执行阶段
- 事件冒泡
从当前事件触发向上查找过程
div=>body=>html=>document
- 事件捕获
存储
cookie已经不建议存储了,如果没有大量数据存储需要,使用localStorage和sessionStorage
对于不怎么改变的数据存使用localStorage。否则使用sessionStorage
-
cookie
作用:主要用于储存用户的登陆信息,
生命周期:一般有服务器生成,可以设置过期时间
数据储存大小:4k
与服务端通信:每次都会携带在headr中,对于请求性能影响属性 作用 value 如果用于保存登陆状态,应加密,不能使用明文的用户标示 http-only 不能通过js访问cookie,减少xss的攻击 secure 只能在协议为https的请求中携带 same-site 规定浏览器不能在跨域请求中携带cookie,减少CSRE攻击 - localStorage
生命周期:除非被清理,否则一直存在
数据存储大小:5M
与服务端通信:不参与 - sessionStorage
数据生命周期:页面关闭就清理
数据存储大小:5M
与服务端通信:不参与 - indexDB
数据生命周期:除非被清理,否则一直存在
数据存储大小:无限
是否与服务端通信:不参与
Service Worker
service worker是一段脚本,与web worker一样,也在后台运行,作为一个独立的线程。运行环境和普通脚本不同,所以不能直接参与web交互行为。native app可以做到离线使用,消息推送。后台自启动,service worker的出现为了是web app 也有类似的能力
-
使用场景
- 后台消息传送
- 网络代理转发请求。伪造响应
-
离线缓存
- 离线缓存
- 消息推送
浏览器缓存机制
性能优化领域
缓存可以说是性能优化中最简单高效的一种方式了。他以最高减少网络传输所带来的损耗
数据请求:
网络请求
后端处理
浏览器响应
直接使用缓存,而不发请求
或发起请求后端存储的数据和前端一致,那么就没必要将数据回传过来,这样可以减少响应数据
缓存位置
- Service Worker
优点:
它的缓存和其他内建的缓存机制不同。它可以由我们自定缓存那些文件,如何匹配缓存,如何读取缓存,并且缓存是持久性的 -
Memory Cache
优点:
内存中的缓存。读取内存中的数据比磁盘要快很多
访问过页面之后,再次刷新页面,可以发现之前很多数据都来自内存缓存
缺点:
持续性短,随着进程的释放而释放
大文件一般都是不会储存内存中,反之优先 - Disk Cache
优点:
储存在硬盘中。覆盖率基本是最大的,什么都可以存入
跟内存相比。容量和储存时效性超级好 - Push Cache
储存在会话中,一旦会话结束就会被释放
缓存策略
-
强缓存
- Expires
是HTTP/1的产物,表示资源的过期时间,并受限与本地时间。如果修改了时间,缓存失效
- Cache-control
出现于HTTP/1.1的产物,优先级比Expires要高
该属性可以调整设置时间
Cache-control可以在请求头或者响应中设置,并且可以组合多种指令
private=>响应可以被客户端缓存
pablic=》可以同被客户端和代理服务器缓存
no-cache=》资源会被缓存,但立即失效。下次发起请求会验证是否过期
- Expires
-
协议缓存
如果缓存过期了。就需要发起请求验证资源是否更新,协议缓存可以通过2个http-header实现:Last-Modified+ETag
当浏览器发起请求验证资源时,如果资源没有做出改变,那么服务器会返回304状态吗。并且更新缓存有效期
如果资源有所改变,更新资源- Last-Modified
表示本地文件最后更改的日期。If-Modified-Since会将last-Modified的值发送给服务器,询问服务器在该日期更新后资源是否有变动,有更新,返回更新后的信息,反之,返回状态码
弊端:
如果是本地打开缓存文件,即使没有对文件进行修改,但还是会造成last-Modified的修改。服务端不能命中缓存导致发送相同的资源
因为last-Modified只能以秒计时。如果在不可感知的时间你内完成了这个文件,那么会造成服务端认为资源被秒中,不会返回正确值 - ETag
类似指纹,If-None-Match会将ETag发送给你服务器,询问服务器资源ETag是否改变,如有改变,将更新的资源返回来,ETag比Last-Modified的优先级高
- Last-Modified
浏览器渲染机制
浏览器接收到 HTML 文件并转换为 DOM 树
首先浏览器打开一个网页的时候,首先会解析他对应的html,在网络传输中我们平时所写的js+css+html都都是以子节数据(0-1)进行传输
转子节转为字符串。
字符串通过词法解析为标记(token),这一过程也被称为标记法。
将结束标记之后,将会把标记转为node
根据node之前的联系转成dom
将 CSS 文件转换为 CSSOM 树
首先这一过程是非常耗性能的,因为浏览器会确定每一个节点样式分别是什么,需要递归匹配到数据的变化
子节数据
字符串
标记(token)
node
cssom
生成渲染树?
当我们生成dom树和cssom树的时候,就需要将这2个树和成渲染树
在这一过程中,将包括需要的节点和这些节点的样式去渲染出来
当浏览器生成渲染树之后,浏览器根据渲染树布局,然后GPU绘制。合成图层
为什么操作 DOM 慢?
因为dom属于渲染引擎,js属于js引擎,面对两个线程之前的通信,操作dom次数一多,也就等同于进行线程之前的切换,并且操作dom可能带来回流。
什么情况阻塞渲染?
渲染的前提是生成渲染树,所有html和css肯定会堵塞渲染
降低一开始需要渲染的文件大小
并且扁平化。优化选择器
当浏览器在执行到script标签时,就会暂时dom,完成后从暂停处继续执行,
首评加载的越快,就不该在首评加载js。建议将script放到body下面的原因
async(适合没有依赖的文件)
js文件下载和解析不会阻塞渲染
defer
并行下载,会等到html执行完毕之后在进行执行
重绘(Repaint)和回流(Reflow)
回流一定会引起重绘
但重绘不一定会引起回流
- 重绘(Repaint)
当改变节点的样的时候。不会改变布局的时候,比如修改color样式。
- 回流(Reflow)
一般布局修改,dom操纵一般会触发回流
减少重绘和回流的方式?
使用 transform 替代 top
使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
不要把节点的属性值放在一个循环里当成循环里的变量
不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
CSS 选择符从右往左匹配查找,避免节点层级过多
不考虑缓存和优化网络协议的前提下,考虑可以通过哪些方式来最快的渲染页面?
从文件大小
从script标签上使用来考虑
从html和css代码书写上来考虑
从需要下载的内容是否需要在首屏使用上来考虑
安全防范知识点
xss
想尽一切办法将可执行的代码注入网页中
- 持久型攻击
攻击的代码被服务器写日数据库,这种危害会很大,如果网站访问量大的话,会导致正常访问页面的用户都收到攻击
一般会以评论方式去注入 - 非持久型攻击
一般以修改URL参数的方式进行攻击
诱导用户访问链接从而实现攻击
转义字符
对于用户输入的东西永远不要相信,最普通的做法是对用户输入的内容进行转译。
引号。尖括号。斜杠等进行转译‘
或者使用白名单也可以,js-xss来实现
cors
跨站伪造请求,攻击者构造出一个向后端请求的地址,诱导用户点击或者通过某种途径自动发起请求,如果用户是在登陆的情况下,后端以为是用户操作,从而进行诱导。
常用方式:
加入网站中有个get请求提交表单的接口。那么攻击者会在钓鱼网站加入一个图片,图片的地址就是这个评论的接口
防御:
get请求不对数据进行修改
不让第三方访问到用户的cookie(some-site)
阻止第三方网站请求接口(验证refrer)
请求是附带验证信息,比如token或者验证码,进行判断
点击劫持
攻击者通过将要攻击的网站通过iframe的方式放入自己的网站中,并将iframe设为透明,诱导用户点击
防御:
js判断,删除页面中的iframe
中间人攻击
是指攻击方同时将服务端和客户端同时进行连接。并让对方认为都是安全的,但实际上整个过程中,都是被控制了,攻击者可以同时修改用户的信息和数据库中的内容
防御:
尽量避免在公众场合使用Wi-Fi。避免被攻击
v8下的js性能优化
XMind: ZEN - Trial Version
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。