javascript面试题

文章涵盖了JavaScript中的Promise原理,解释了异步操作和回调地狱的解决方案。还讨论了宏任务和微任务的执行顺序,以及函数柯里化的概念。此外,提到了Vue.js组件的生命周期和HTTP与HTTPS的区别。文章还深入介绍了闭包,以及节流和防抖的实现。最后,讨论了前端开发中的安全问题,如CSRF和XSS攻击及其防范措施。
摘要由CSDN通过智能技术生成

记录一下面试题吧~以后总能用得上。

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

 

父子组件生命周期函数执行顺序

父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

 

节流和防抖,分别手写

节流:节流就是减少流量,将频繁触发的事件减少,并每隔一段时间执行, 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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Goat恶霸詹姆斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值