原生JavaScript系列面试题

大家好,我是有用就点赞,有用就扩散。

1.__ proto __ 和prototype之间有什么关系?

在JavaScript中万物皆对象,所有对象都有__ proto __ 属性,函数这个特殊对象除了具有__ proto __ 属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和 __ proto __属性。prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而 _ _proto _ _是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。

2.call,apply,bind的区别和作用?bind方法如何来实现?

call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向。区别在于调用方式及参数传递上。

(1)call 传参就是参数

(2)apply 传参必须是一个数组

(3)bind 传参是普通的参数,但是多一道执行,并且参数可以在前面传也可以在后面传

bind实现如下:

Function.prototype.myBind = function(context){
    //判断调用对象是否为函数
    if(typeof this !== "function"){
        throw new TypeError("Error")
    }
    
    //获取参数
    let args = [...arguments].slice(1)
    let fn = this
    return function Fn(){
        return fn.apply(this instanceof Fn ? new fn:context,args.concat(...arguments))
    }
}

3.js中基础类型有哪几种?了解包装对象吗?

基础类型有6种:

(1)boolean

(2)null

(3)undefined

(4)number

(5)string

(6)symbol

包装对象:基础数据类型都是值,所以没有方法提供调用,例如:undefined.split(“”);比如”abc“.split(“”)类似这种调用可以被允许?原因是JavaScript中存在包装对象,会把字符先包装成对象然后在调用对象下的一些方法,方法调用完成之后在销毁对象,这样就完成了基础数据类型的函数调用功能。

null 是不是一个Object类型?

null 通过typeof 可以看到是Object,但并不是Object类型,就是一个基础的类型,原因是JavaScript在早期发展的时候,只能存在32位的操作系统之中,为了考虑性能问题,会把所有以0开头的定义成为对象,null全部都是0,会被系统误认为是Object,设计之初就是一个基础的类型。

4.如何判断this?箭头函数的this是什么?

三种场景描述:

(1)函数直接调用的this,这个this会指向window,需要注意在严格模式下this会是undefined,同样注意在script标签type ="module"下也是undefined

(2)在对象里调用的情况,this会指向调用的对象

(3)在构造函数及类中this会指向实例化的对象

箭头函数的this,会继承上一层的this

5.如何中断ajax请求?

原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax,注意的是abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。更多的应用场景就是文件上传或者在等待,网络超时这种情况下,手动去停止ajax请求,停止的只是前端的请求,跟后端是没有关系的。

6.什么是同步?什么是异步?

同步和异步是一种消息通知机制

  • 同步阻塞:A调用B,B处理获取结果,才返回给A。A在这个过程中,一直等待B的处理结果,拿到结果然后继续往下执行。
  • 异步非阻塞:A调用B,无需等待B的结果,B通过状态,通知等待通知A或者回调函数来处理。

7.什么是宏任务?什么是微任务?

微任务和宏任务都是异步任务

微任务:一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。

宏任务:宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合。

常见微任务:

(1)Promise.then

(2)MutationObserver

(3)Object.observe(已废弃)

(4)process.nextTick(Node.js)

常见宏任务:

(1)script(外层同步代码)

(2)setTimeout/setInterval

(3)UI rendering/UI事件

(4)postMessage,MessageChannel

(5)setImmediate,I/O (Node.js)

8.什么是回调?回调使用中存在什么问题?

回调即是函数指针的调用,即是一个通过函数指针调用的函数。

function fn(cb){
    cb && cb()
}
fn(()=>{console.log(“我是回调函数”)})

使用回调函数有一个很大缺点:就是造成回调地狱,回调地狱是为了实现某些逻辑出现函数的层层嵌套。回调地狱会造成可读性及可维护性变差。

解决回调地狱问题可以通过:观察者模式、promise、async/await来处理。

9.Promise.allSettled了解吗?动手实现一下Promise.allSettled?

Promise.allSettled是ES2020新特性,可以执行多个promise对象,获取多个promise对象状态,无论成功或者失败的状态。实现代码如下:

function selfallSettled(lists){
    let resArr = new Array(lists.length)
    //引入预加载变量
    let num = 0
    return new Promise(resolve=>{
        lists.forEach((item,key)=>{
            let obj = {}
            item.then(res=>{
                obj["status"] = "fulfilled"
                obj["value"] = res
                resArr[key] = obj
                num ++
                if(num===lists.length){
                    resolve()
                }
            },err=>{
                obj["status"] = "rejected"
                obj["reson"] = err
                resArr[key] = obj
                num ++
                if(num===lists.length){
                    resolve()
                }
            })
        })
    })
}

10.简述http常见状态码有哪些?

2XX成功

(1)200 OK,表示从客户端发来的请求在服务器被正确处理

(2)204 No content,表示请求成功,但响应报文不含实体的主体部分

(3)205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与204响应不同在于要求请求方重置内容

(4)206 Partial Content,进行范围请求

3XX重定向

(1)301 moved permanently,永久性重定向,表示资源已被分配了新的URL

(2)302 found,临时性重定向,表示资源临时被分配了新的URL

(3)303 see other,表示资源存在着另一个URL,应使用GET方法获取资源

(4)304 not modified,表示服务器允许访问资源,但因发生请求为满足条件的情况

(5)307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求

4XX客户端错误

(1)400 bad request,请求报文存在语法错误

(2)401 unauthorized,表示发送的请求需要有通过HTTP认证的认证信息

(3)403 forbidden,表示对请求资源的访问被服务器拒绝

(4)404 not found,表示在服务器上没有找到请求的资源

5XX服务器错误

(1)500 internal sever error,表示服务器在执行请求时发生了错误

(2)501 Not Implemented,表示服务器不支持当前请求所需要的某个功能

(3)503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求

11.什么是XSS攻击?如何防范XSS攻击?

XSS就是攻击者想尽一切办法将可以执行的代码注入到网页中。

按照类型来分:XSS可以分为存储型,反射型及DOM型。DOM型XSS攻击中,取出和执行恶意代码由浏览器端完成,属于前端JavaScript自身的安全漏洞,而其他两种XSS都属于服务端的安全漏洞。

防止XSS攻击可以通过:

1)转义字符

(1)如HTML元素的编码,JS编码,CSS编码,URL编码等

(2)避免拼接HTML;Vue/React技术栈,避免使用v-html/dangerouslySetInnerHTML

2)增加攻击难度配置CSP

(1)CSP本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。

(2)通常可以通过两种方式来开启CSP:

(3)设置HTTP Header中的Content-Security-Policy

  • 只允许加载本站资源(Content-Security-Policy:default-src ‘self’)

  • 只允许加载HTTPS协议图片(Content-Security-Policy:img-src https:)

  • 允许加载任何来源框架(Content-Security-Policy:child-src ‘none’)

设置meta标签的方式

<meta http-equiv="Content-Security-Policy" content="script-src 'self';object-src 'none';style-src cdn.example.org third-party.org;child-src https:" >

3)校验信息

(1)比如一些常见的数字、URL、电话号码、邮箱地址等做效验判断

(2)开启浏览器XSS防御:Http Only cookie,禁止JavaScript读取某些敏感Cookie,攻击者完成XSS注入后也无法窃取此Cookie

(3)验证码

12.浏览器为什么要阻止跨域请求?如何解决跨域?每次跨域请求都需要到达服务端吗?

1)浏览器阻止跨域请求的原因是”同源策略“,”同源策略“主要解决的问题是浏览器的安全问题,同源是协议、域名、端口都相同,非同源是只要协议、域名、端口有一个不同就会造成非同源。

非同源会造成:

(1)无法获取cookie、localstroage、indexedDB

(2)无法访问网页中DOM

(3)无法发送网络请求

2)解决跨域方式有很多种例如:

(1)jsonp跨域

(2)postMessage解决跨域

(3)跨域资源共享(CORS)

(4)nginx反向代理

(5)node.js中间件正向代理

(6)websocket协议跨域

3)跨域是浏览器出于安全策略阻止非同源请求,但是每次跨域请求其实都是正常发送的,服务器也会正常返回,只是被浏览器拦截起来。所以每次跨域请求都会到达服务端。

13.Token一般是存放在哪里?Token放在cookie和放在localStorage、sessionStorage中有什么不同?

Token其实就是访问资源的凭证。

一般是用户通过用户名和密码登录成功之后,服务器将登录凭证做数字签名,加密之后得到的字符串作为token。

三种存储方式的区别:

(1)共同点:都保存在浏览器,且同源,localStorage与sessionStorage,保存在浏览器,不参与服务器通信,大小为5M

(2)生命周期不同:localStorage永久保存,sessionStorage当前会话,都可手动清除

(3)作用域不同:不同浏览器不共享local和session,不同会话不共享session

(4)Cookie:设置的过期时间一直有效,大小4K。有个数限制,各浏览器不同,一般为20个,携带在HTTP头中,过多会有性能问题。

几种存储方式:

(1)存储在localStorage中,每次调用接口的时候都把它当成一个字段传给后台

(2)存储在cookie中,让它自动发送,不过缺点就是不能跨域

(3)拿到之后存储在localStorage中,每次调用接口的时候放在HTTP请求头的Authorization字段里

将token存放在webStorage中,可以通过同域的js来访问。容易受到XSS攻击,webStorage作为一种储存机制,在传输过程中不会执行任何安全标准。

将token存放在cookie中可以指定httponly,来防止被JavaScript读取,也可以指定secure,来保证token只在HTTPS下传输。缺点是不符合Resful最佳实践,容易受到CSRF攻击。

(1)CSRF跨站点请求伪造(Cross-Site Request Forgery),跟XSS攻击一样,存在巨大的危害性。简单来说就是恶意攻击者盗用已经认证过的用户信息,以用户信息名义进行一些操作(如发邮件、转账、购买商品等)。由于身份已经认证过,所以目标网站会认为操作都是真正用户操作的。CSRF并不能拿到用户信息,它只是盗用用户凭证去进行操作。

14.WebSocket是怎么实现点对点通信和广播通信的?

WebSocket是一种全双工通信协议。WebSocket让服务器和客户端通信变得简单。最大的特点是可以通过服务端主动推送消息到客户端。

广播通信:是类似广播一样给多个人进行广播消息。采取的是socket.io模块

(1)服务端监听socket链接

io.on("connection",(socket)=>{console.log("有socket连接")})

//通过监听链接过来的socket对象广播对应的信息
socket.on("addData",function(data){
 //广播除了自己之外的其他订阅者
 socket.broadcast.emit("addInputData",data)
})

(2)客户端连接及发送对应的socket请求

let socket = io.connet("ws://localhost:3000")//链接socket服务器
socket.emit("addData",JSON.stringify(info))//发送socket事件

点对点通信:就是一对一的通信。例如多人实时聊天,可以指定用户来发送消息。

  • 服务端记录每一个链接过来的socket对象,且和用户Id进行关联
  • 给指定的socket对象进行广播
  • 客户端监听点对点广播事件

15.客户端缓存有几种方式?浏览器出现from disk、from memory的策略是啥?

1)强缓存:服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。

  • Cache-control(相对值)/Expries(绝对值)

  • Expries是HTTP1.0的标准,到了HTTP1.1,Expries已经被Cache-controlt替代

ctx.set("Cache-control","max-age=3600")//设置缓存时间 3600s

(1)public: 所有内容都将被缓存(客户端和代理服务器都可缓存)

(2)private: 所有内容只有客户端可以缓存,Cache-control的默认取值

(3)no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定

(4)no-store:所有内容都不会被缓存,既不使用强制缓存,也不使用协商缓存

(5)max-age=xxx:缓存内容将在XXX秒后失效

  • Cache-control优先级比Expires高
  • from memory cache代表使用内存中的缓存,from disk cache则代表使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory->disk

2)协商缓存:让客户的和服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,将缓存信息中Etag和Last-Modified通过请求发送给服务器,由服务器效验,返回304状态码时,浏览器之间使用缓存。出现from disk、form memory 的策略是强缓存

3)缓存关系:强缓存由于协商缓存

16.说一下CORS的简单请求和复杂请求的区别?

CORS(Cross-origin resource sharing),跨域资源共享,是一份浏览器技术的规范,用来避开浏览器的同源策略。

CORS可以分成两种:简单请求和复杂请求

简单请求:HEAD、GET、POST

复杂请求:最先发送的是一种“预请求”,此时作为服务端,也需要返回“预回应”作为响应。(预请求实际上是对服务器的一种权限请求),只有当预请求成功返回,实际请求才开始执行。

(1)PUT

(2)Accept

(3)Accept-Language

(4)Content-Language

(5)Last-Event-ID

(6)Content-Type

(7)application/x-www-form-urlencoded

(8)multiparty/form-data

(9)text/plain

17.节流和防抖分别是什么?在什么场景下使用?请分别实现一个节流函数和一个防抖函数?

防抖(debounce)就是在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时。

const debounce = (fn,delay=500)=>{
    let timer
    return function(...args){
        clearTimeout(timer)//清空定时器
        timer = setTimeout(function(){
          fn.apply(this,args)//改变this指向
       },delay)//延迟一定时间执行
    }
}

截流(throttle)就是规定好一个单位时间,触发函数一次。想执行多次的话,只能等到下一个周期里。

//定时器版本,执行稍微靠后
const throttle1 = (fn,delay=500)=>{
    let timer
    return (...args)=>{
        if(!timer){
            timer = setTimeout(()=>{
                timer=null
                fn.apply(this,args)
            },delay)
        }
    }
}
//时间戳版本,执行稍微靠前
const throttle2 = (fn,delay=500)=>{
    let oldTime = Date.now()
    return (...args)=>{
        let newTime = Date.now()
        if(newTime-oldTime >=delay){
            oldTime = Date.now()
            fn.apply(this,args)
        }
    }
}

运用场景:按键快频率重复触发、拖拽场景、表单验证场景、resize、scroll、onmosemove等触发事件

18.怎么禁止让js读取cookie?怎么让cookie只在HTTPs下传输?

由于cookie会放在客户端,一般情况下会保存一些凭证及状态信息。为了防止cookie泄露造成安全问题,可以设置cookie得HttpOnly属性,JS脚本、Applet等将无法读取到cookie信息,能有效防止XSS攻击。

cookie中有个属性secure,设置为true时,表示创建得cookie会被以安全的形式向服务器传输,也就是只能在HTTPS连接中被浏览器传递到服务器端进行会话验证。

19.什么是纯函数?使用纯函数有什么好处?

纯函数是对给定的输入返还相同输出的函数。纯函数在函数式编程中被大量使用,在前端如reactjs、redux等库中大量被使用。

好处:

(1)可以产生可测试的代码

(2)可读性更强

(3)可缓存,通过缓存缓存执行结果

(4)易于组合,纯函数可以通过组合及管道组合出更复杂的功能

20.实现柯里化add(1)(2)(3)?

柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

const currying = function(fn){
 return curried = (...args) =>{
     return (args.length >= fn.length) ? fn(...args) : (...args2) => curried(...args.concat(args2))
 }
}

//使用
const add = (a,b) => a+b
const mycurry = currying(add)
let result = mycurry(1)(2)
console.log(result)

柯里化作用:

(1)参数复用

(2)延迟执行

(3)某些语言及特定环境下只能接受一个参数

21.了解函数式编程中的compose吗?动手实现一下?

compost的作用就是组合函数的,将函数串联起来执行。将多个函数组合起来,一个函数的输出结果是另一个函数的输入函数,一旦第一个函数开始执行,就会像多米诺骨牌一样推倒执行。

两个参数的compost(从右到左执行函数,与之相反的是:pipe管道从左到右执行函数)

 const compost = (a,b) => c => (a(b(c)))

多个参数的compost

const compost = (...fns)=>val=>fns.reverse().reduce((acc,fn)=>fn(acc),val)

22.按要求实现go函数?

//示例
go("l"); //gol
go()("l"); //gool
go()("l"); //goool

这里主要是高阶函数和闭包及递归的运用

function go(...args){
 let o = "o"
 const fn = (...args) => {
     if(typeof args[0] === "undefined"){
         return (...args) => {
             o += "o"
             return fn(...args)
         } 
     } else {
         return "g" + o + args[0]
     }
 }
 return fn(...args)
}

23.实现一个myMap函数,实现类似map功能?

forEach 、some 、every等声明式编程不关注实现细节,只关注结果,纯函数实现

命令式编程更关注实现的细节,主要思想是关注计算机执行的步骤

这是通过声明式编程方式抽象循环逻辑

Array.prototype.myMap = function(fn){
 let resultArr = []
 for (let i = 0; i < this.length; i++){
     resultArr.push(this[i],i,this)
 }
 return resultArr
}

//例子
const array = [,,,,,]
let newArr = array.myMap((item)=>{
    return item = {name:'1'}
})
console.log(newArr)

关于各位大哥投稿PR。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值