记录一下面试题吧~以后总能用得上。
Promise原理
Promise是一种异步的操作,对象的状态不受外界影响,可以解决回调地域的问题,通过状态驱动,有三种状态:pengding,fulfilled,rejected;只有异步操作的结果可以决定当前是哪种状态,一旦状态改变就不会再变,只能是pending到fulfill或者pending到reject,且状态是不可逆的。
工作流程:
创建Promise对象=>进入等待处理阶段pending状态
处理完成后,切换到fulfill/reject的状态
根据状态执行.then或者.catch的回调函数
宏任务和微任务
宏任务包括:setTimeout setInterval Ajax DOM事件
微任务:Promise async/await
正常来说宏任务和微任务都属于异步任务,在主程序走完之后开始执行,任务列表优先执行的是微任务,执行完后,走下一轮宏任务(所以也可以把主程序可以看作是第一次的宏任务),宏任务结束后,会继续查看是否有新的微任务,如果有则执行微任务,如果没有则继续执行下一项宏任务,如此循环执行。
函数柯里化
函数柯里化就是我们给一个函数传入一部分参数,此时就会返回一个函数来接收剩余的参数。
add(1)(2)(3)....(n)的求和实现,n也可以是多个参数,如add(1,2,3)
![2c6b5232cc31994c46fb78e7cc07ae2d.png](https://i-blog.csdnimg.cn/blog_migrate/14538136ef367baea19615825b704e3a.png)
父子组件生命周期函数执行顺序
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted->父beforeUpdate->子beforeUpdate->子updated->父updated->父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
http和https的区别
HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。https则是具有安全性的ssl加密传输协议。http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
什么是闭包
在一个函数里边再定义一个函数。这个内部函数一直保持有对外部函数中作用域的访问,通俗的来说,就是子函数调用父函数的作用域,导致父函数的作用域没法释放的现象就是闭包,它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
![30c63dd4b57994de565e0e37addff510.png](https://i-blog.csdnimg.cn/blog_migrate/ce7f3c74dec1c6b9ceaaa3fb46b4e1e4.png)
节流和防抖,分别手写
节流:节流就是减少流量,将频繁触发的事件减少,并每隔一段时间执行, n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: 防抖就是防止抖动,避免事件的重复触发,n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时。
防抖代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">点击</button>
</body>
<script>
function debounce(handler,wait,immediate){
if(typeof handler !="function")throw new Error("handler must ne a function")
if(typeof wait =="undefined")wait = 3000
if(typeof wait =="boolean"){
immediate=wait
wait = 3000
}else if(typeof immediate !="boolen")immediate=false
let timer =null
return function proxy(...args){
let init = immediate && !timer
clearTimeout(timer)
timer = setTimeout(()=>{
timer=null
!immediate?handler.call(this,...args):null
},wait)
init?handler.call(this,...args):null
}
}
function btnClick(){
console.log(123)
}
btn.onclick = debounce(btnClick,200,false)
</script>
</html>
节流代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
body{
height: 5000px;
}
</style>
<body>
<button id="btn">点击</button>
</body>
<script>
function throttle(handler,wait){
if(typeof handler !="function")throw new Error("handler must ne a function")
if(typeof wait =="undefined")wait = 3000
if(typeof wait =="boolean"){
immediate=wait
wait = 3000
}else if(typeof immediate !="boolen")immediate=false
let timer =null
let previous = 0
return function proxy(...args){
let now = new Date()
let interval = wait -(now-previous)
if(interval<=0){
handler.call(this,...args)
previous=new Date()
}else if(!timer){
timer = setTimeout(()=>{
clearTimeout(timer)
timer = null
handler.call(this,...args)
previous=new Date()
},interval)
}
}
}
function scrollFn(){
console.log("456")
}
window.onscroll = throttle(scrollFn,1000,false)
</script>
</html>
浏览器请求的过程原理
计算机网络模型有7层的osi模型,还有5层以及TCP/IP的4层的模型,浏览器通过http协议发送请求,那么首先在应用层利用DNS服务器解析域名,获得ip地址,通过http或者https封装,包括请求头等信息。
接下来数据来到传输层:再次将上层内容封装,封装为TCP/UDP报文;
网络层:再次进行封装为IP报文;
数据链路层:将IP报文进行分组形成帧;
物理层:帧封装成0/1信号,就是bit单位,再进行传输;
然后就是网线传输
到了服务端其实就是一套逆行:bit->帧->IP报文,之后服务器再进行响应,流程与发送请求类似。
到了浏览器,获取到了返回的数据,则开始渲染页面。
前端常遇到的注入问题
csrf 跨站请求伪造
跨站请求伪造(Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。
用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
用户未退出网站A之前,在同一个浏览器中,打开一个Tab页访问网站B;
网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。
网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
假设你正在购物,看重了某个商品,商品 id 是 100 。
同时这个商品的付费接口时 xxx.com/pay?id=100,但是没有任何验证。
这个时候我是攻击者,我看中了一个商品,id 是 200 。
那么,我如何让你来为我付款?
这个时候我像你发送了一封邮件,邮件标题很是吸引人。
但邮件正文隐藏着 <img src = "xxx.com/pay?id=200"> 。
你一查看邮件,一点击,就帮我购买了 id 是 200 的商品
特点:
攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作,而不是直接窃取数据。 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”
防治措施:
在请求地址中添加token并验证,可以在 HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是CSRF 攻击而拒绝该请求。 这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对。
XSS(跨站脚本攻击)
不需要你做任何的登录认证,它会通过合法的操作(比如在 url 中输入、在评论框中输入),向你的页面注入脚本(可能是 js、hmtl代码块等)。有三种:
非持久型跨站(也叫反射型)
攻击者构造出特殊的 URL ,其中包含恶意代码。 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL中取出,拼接在HTML中返回给浏览器。 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
持久型跨站(也叫存储型)
攻击者将恶意代码提交到目标网站的数据库中。用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在HTML中返回给浏览器。用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM跨站
攻击者构造出特殊的 URL ,其中包含恶意代码。 用户打开带有恶意代码的 URL 。
用户浏览器接收到响应后解析执行,前端JavaScript 取出 URL 中的恶意代码并执行。
恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
只要有输入数据的地方,就可能存在 XSS 危险。
1.编码: 对于用户输入进行编码;
2.过滤: 移除用户输入和事件相关的属性;(过滤 script、style、iframe 等节点);
3.校正: 使用 DOM Parse 转换,校正不配对的 DOM 标签;
4.设置HttpOnly 在 cookie 中设置 HttpOnly 属性后,js 脚本(document.cookie)将无法读取到 cookie 信息。
5.CSP(内容安全策略): CSP 的实质就是白名单策略,预先设定好哪些资源能被加载执行而哪些不能,为了防止跨域脚本攻击而制定。
防范:使用htmlspecialchars(string,quotestyle,character-set)函数。需注意第二个参数, 直接用htmlspecialchars($string)的话,第二个参数默认是ENT_COMPAT,函数默认只是转化双引号(“), 不对单引号(‘)做转义。
两者区别:
CSRF: 需要用户先登录网站 A,获取 cookie, 是利用网站 A 本身的漏洞,去请求网站 A 的 api。
XSS: 不需要登录,是向网站 A 注入 JS 代码,然后执行 JS里的代码,篡改网站 A 的内容。
总结: 通常来说 CSRF 是由 XSS 实现的,CSRF 时常也被称为 XSRF(CSRF 实现的方式还可以是直接通过命令行发起请求等)。本质上讲,XSS 是代码注入问题,CSRF 是 HTTP 问题。 XSS 是内容没有过滤导致浏览器将攻击者的输入当代码执行,CSRF 则是浏览器在发送 HTTP 请求时候进行。
前端判断数据类型的方法
typeof 基本数据类型
适用于:number、boolean、string、object、undefined、function、symbol。
instanceof 引用数据类型
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。判断已知实例对象是哪个构造类型/引用类型(类)。
var c= [1,2,3];
var d = new Date();
var e = function(){alert(111);};
var f = function(){this.name="22";};
console.log(c instanceof Array) //true
console.log(d instanceof Date) //true
console.log(e instanceof Function) //true
// console.log(f instanceof function ) //false
// 注意左侧必须是对象(object),如果不是,直接返回false,具体见基础类型。
constructor
根据对象的constructor判断,返回对创建此对象的数组函数的引用。几乎可以判断基本数据类型和引用数据类型 。
var c= [1,2,3];
var d = new Date();
var e = function(){alert(111);};
alert(c.constructor === Array) ----------> true
alert(d.constructor === Date) -----------> true
alert(e.constructor === Function) -------> true
//注意: constructor 在类继承时会出错
Object.prototype.toString.call()
最准确的判断方式,强烈推荐,所有数据类型均可判断:Object.prototype.toString.call()
这是对象的一个原生原型扩展函数,用来更精确的区分数据类型(类)。
var gettype = Object.prototype.toString
gettype.call('aaaa') 输出 [object String]
gettype.call(2222) 输出 [object Number]
gettype.call(true) 输出 [object Boolean]
gettype.call(undefined) 输出 [object Undefined]
gettype.call(null) 输出 [object Null]
gettype.call({}) 输出 [object Object]
gettype.call([]) 输出 [object Array]
gettype.call(function(){}) 输出 [object Function]
Object.prototype.toString() 的调用
对于 Object.prototype.toString() 方法,会返回一个形如 "[object XXX]" 的字符串。
var gettype=Object.prototype.toString
var utility={
isObj:function(o){
return gettype.call(o)=="[object Object]";
},
isArray:function(o){
return gettype.call(o)=="[object Array]";
},
isNULL:function(o){
return gettype.call(o)=="[object Null]";
},
isDocument:function(){
return gettype.call(o)=="[object Document]"|| [object HTMLDocument];
}
........
}
但是,大多数对象,toString() 方法都是重写了的,这时,需要用 call() 或 Reflect.apply() 等方法来调用。
var x = {
toString() {
return "X";
},
};
x.toString(); // => "X"
Object.prototype.toString.call(x); // => "[object Object]"
Reflect.apply(Object.prototype.toString, x, []); // => "[object Object]"
原理
当调用一个方法时(这里指的是toString),函数执行上下文的this指向调用者(这里通过call来指定的)。该方法判断this是否为undefined或者null,如果是的话,直接返回[object Undefined]或者[object Null],如果不是的话,就把this(可能是数字,可能是字符串,可能是函数,数组等)通通指向对象O(toObject(this))。
每一个对象都有一个自己的私有属性[[class]],是一个字符串值,表明了该对象的类型,该私有属性不能直接获取,只能通过Object.prototype.toString方法调用,所以toString方法获取到对象私有属性[[class]]的值class 后, 最后返回由"[object"+" "+"class"]"三个部分组成的字符串
function OPTSCType(typeObj) {
// Object.prototype.toString.call(数据) // 最后返回由"[object"+" "+"class"]"三个部分组成的字符串
return Object.prototype.toString.call(typeObj).slice(Object.prototype.toString.call(typeObj).indexOf(' ') + 1, Object.prototype.toString.call(typeObj).length - 1);
}
// console.log(OPTSCType(undefined)); // Undefined