1、 [1,2,3].map(parseInt)输出结果?
答案:1,NaN,NaN
解析:
array.map(function callback(currentValue,index,array){}[,thisArg])方法的作用是遍历array中的每一项,并为每个元素都执行callback函数;
parseInt()方法有两个作用:①当参数为一个时,将其他类型数据转换成字符串
②当参数为两个时,以第二个参数解析数据,进行进制的数据转换(第二个参数需满足大于2小于36,0表示10进制)
这里,第一个参数为数组中的每个元素,第二个参数为index,即是考察parseInt的第二个用法所以表示为:
parseInt(1,0);parseInt(2,1));parseInt(3,2)
得1,NaN,NaN
2、手写一个完整的继承
答案:
3、eventLoop事件循环和事件队列
答:js的异步机制由事件循环和任务队列构成。(js本身是单线程)
javascript主线程拥有一个执行栈以及一个任务队列,主线程会依次执行代码,当遇到函数时,会先将函数入栈,函数运行完毕后会再将该函数出栈,直到所有代码执行完毕。
遇到异步操作,例如:setTimeout、ajax时,异步操作会由浏览器执行,浏览器会再这些任务完成后,将事先定义的回调函数推入主线程的任务队列中,当主线程的任务栈清空后会读取任务队列中的回调函数,然后去执行,从而进入一个无线循环,这就是事件循环。
一个浏览器环境只能拥有一个事件循环,而一个事件循环可以多个任务队列,每个任务都有一个任务源。任务队列是一个先进先出的队列
4、https的工作原理
解析:https在传输数据之前需要客户端(浏览器)和服务器之间进行一次握手,在握手过程中确立双方加密传输数据的密码信息。TLS/SSL是一条加密传输协议,使用了对称加密,非对称加密以及hash算法。
握手的过程:浏览器将自己支持的一套加密规则发送给服务器;网站从汇总选出一组加密算法与hash算法,并将自己的身份信息以整数的形式发送给浏览器。证书里包含了网址,加密公钥,以及证书的颁发机构等信息,浏览器获得证书后要验证证书的合法性,如果证书不受信任会给出提示,如果证书受信任,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。使用约定好的hash算法发计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
网站接受浏览器发送来的数据要做一下的操作:使用自己的私钥将信息解密,取出密码,使用密码解密浏览器发来的握手信息,并验证hash是否一致。使用密码加密一段握手消息发送给浏览器。
客户端如何验证证书的合法性:校验证书的颁发机构是否受客户端信任;通过CRL或OCSP的方式校验证书是否被吊销;对比系统时间,校验证书是否在有效期内;通过校验对方是否存在证书的私钥,判断证书的网站域名是否与证书颁发的域名一致;
5、https和http的区别
答:https比较安全,经过TLS/SSL加密,需要ca证书,需要收费;
https和http的端口号不同,https默认端口号是443,http默认端口号是80;
http是超文本传输协议,信息是明文传旭,https是具有安全性的加密传输协议;
http连接很简单,是无状态的;https协议是由ssl+http协议构建的可加密传输,身份认证的网络协议比http协议安全;
6、http2 和http1 的区别
答:http2采用二进制格式而非文本格式,解析起来更高效;
http2是完全多路复用的,即一个tcp连接上同时跑多个http请求
http2使用报头压缩,降低了开销;
http2让服务器可以将响应主动推送到客户端缓存中,支持服务端推送,就是服务器可以对一个客户请求发送多个响应;
7、请求报头举例
答:Accept:客户端可识别的内容类型列表,用于指定客户端接受那些类型的消息;
cache-control:用于指定缓存指令,缓存指令是单向的且是独立的,一个消息的缓存指令不会影响另一个消息的缓存指令
host:请求主机名;
user-agent:发送请求的浏览器类型、操作系统等信息
Accept-Language:表示浏览器支持的语言类型;
8、输入URL后网页到底做了什么
答:通过域名(DNS)解析IP地址,查找域名对应的ip地址
根据ip地址建立TCP的三次握手
①客户端发送syn包到服务器,等待服务器确认:syn_send;
②服务器手动syn包,确认客户端的syn并发送自己的syn包:syn_recv;
③客户端收到服务器端发送的syn+ack包,向服务器发送确认包ack,此包发送完毕:established;完成三次握手
浏览器与服务器通信:浏览器发起请求,服务器响应请求
①浏览器根据URL内容生成http请求;
②服务器收到请求后会根据内容获取相应的html文件,将得到的HTML发送给客户端;
③在浏览器还没有完全接收html文件时便开始渲染,显示网页;
④在执行html文件时,根据需要请求图片,css,js等文件
浏览器与服务器断开链接(TCP的四次挥手)
计算机网络体系结构:7层模型
应用层(HTTP、SMTP、FTP、POP3);
传输层(TCP、UDP);
网络层(IP、路由器):
数据链路层(网桥 ppp CD);
物理层(集线器);
9、浏览器渲染展示网页的过程:
答:html代码转化为DOM树,css代码转化成css object model;结合dom和cssom生成一颗渲染树;生成布局,将所有渲染树的节点进行平面合成;将布局绘制到屏幕上;使用JavaScript脚本来动态的修改dom,以便给web应用带来动态行为;最后,web应用执行;
回流:当render tree中的一部分或全部因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,这就称之为回流。每个页面至少有一次回流,也就是页面第一次加载的时候。完成回流后,浏览器会重新绘制受影响的部分到屏幕中去,这就称之为重绘。
触发回流的几种情况:
1.添加或者删除可见的dom元素;
2.元素位置改变;
3.元素尺寸改变-边距、填充、边框、宽度、高度;
4.页面渲染初始化;
5.内容改变-文本改变或者图片大小改变,引起计算值宽度,高度的改变;
6.浏览器尺寸大小的改变-resize事件发生时;
重绘:回流必将引起重绘,重绘不一定会引起回流
减少重绘回流的实现:
1.css :使用transform代替top;
使用visibility代替display:none;
避免使用table布局
尽可能在dom树的最末端改变class,限制了回流的范围,使其影响尽可能少的节点;
避免设置多层内联样式;避免层级过多;
避免使用css表达式,将频繁重绘或者回流的节点设置为图层;
2.javascript:避免频繁操作样式,最好一次性重写style样式;
避免频繁操作DOM,最好创建一个documentFragment,在它上面应用所有的DOM操作,最后再把它添加到文档中。
避免频繁读取会影响重绘回流的属性,用一个变量缓存起来;
对一个复杂动画的元素使用绝对定位,使他脱离文档流,否则会引起父元素以及后续元素频繁回流;
10、xss攻击和csrf攻击防御
答:xss的防御:1.对用户输入的内容进行解析和过滤或编码(过滤有危险的dom节点,如script img link style iframe等)
2.对cookie做保护,设置httpOnly,防止客户端通过document.cookie读取cookie;
csrf的防御:1.尽量减少使用get请求;
2.使用验证码;
3.referer check;
4.使用token;
11、数组去重
解析:
12、跨域
jsonp的原理:动态创建一个script标签,动态加载一个js文件,载入成功后会执行在URL参数中指定的函数,并把需要的json数据所谓参数传入;
优缺点:只能传递get请求,有一定得限制;
cros跨域资源共享:(原理)服务器对于cros的支持,设置Access-Control-Origin为你传输的origin的值,浏览器监测到响应的设置,支持跨域;
优缺点:支持所有类型的请求 get、post、put、delete、update等;
node中间件代理跨域:通过启用一个代理服务器,实现数据的转发;node+vue+webpack-dev-server大力接口跨域,在开发模式下,只需要设置devServer=>proxy即可
14、this的使用场景+apply、call、bind的区别,手动封装
this的使用场景:this的值会随着函数的使用场景的不同而发生变化,但是一个原则就是,this指的是调用函数的那个对象,即谁调用指向谁;
①作为对象的方法调用;
②构造器调用;
③function.prototype.call 或 function.prototype.apply调用;
④作为普通函数调用;
call和apply的用途:①改变this的指向;②借用其他对象的方法
call的另外一个作用就是:将伪数组转换为数组:Array.prototype.slice.call(divs);---divs为要转换的伪数组
call 和apply 的区别:都是用来改变this的指向,都可以借用其他对象的方法,不同是apply的第二个参数传值方式必须是集合,即伪数组或者数组,call的第二个参数传值是依次传入各个值,用逗号分隔;他们的第一个参数都代表的是函数体内this的指向;
bind和call、apply的区别:call和apply是立即执行函数,bind非立即执行,为函数体,执行时在后面加小括号,小括号中传值
15、ES6的重要知识点:
1> Generator函数是es6提供的异步编程解决方案,可以理解为是一个状态机,封装了多个内部状态;
2>Generator函数会返回一个遍历器对象;可以依次遍历函数内部的内一个状态,特征:function关键字与函数名之间有一个*号,二是,函数体内部使用yield表达式,定义不同的内部状态;
3>Generator函数是分段执行的,yield表达式是暂停执行的标记,next方法每次执行,内部指针就从函数头部或者上次停下来的地方开始执行,直到遇到下一个yield表达式为止。
4>es6中let const命令:不存在变量提升,不允许声明前使用,会报错;let声明的是块级作用域;这时候会存在一个临时死区,即{}中间都为临时死区,不受外部的影响。let不允许相同作用域内重复声明一个变量。
5>为什么需要块级作用域?---场景① 内层变量可能会覆盖外层变量(由于存在变量提升);场景②用来计数逇循环变量泄露为全局变量;
6>内层作用域可以定义外层作用域同名的变量,块级作用域的出现替换掉了原来的匿名函数IIFE;函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明。块级作用域必须有大括号,没有就报错;严格模式下,函数声明只能在当前作用域的顶层;
7>const命令声明的变量不能改变值,这意味着,一旦声明立即执行初始化,不能留到以后赋值,只声明不赋值就会报错;
8>es6提供了新的数据结构Set。类似于数组,但是成员的值都是唯一的,没有重复的值;本身是一个构造函数,用来生成set数据结构。
9>字符串去重:
10>Set实例的属性和方法:
①Set.prototype.constructor:构造函数,默认就是Set函数;
②Set.prototype.size:返回Set实例的成员总数;
③Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
④Set.prototype.clear():清楚所有成员,没有返回值;
⑤Set.prototype.keys():返回键名的遍历器;
⑥Set.prototype.values():返回键值得遍历器;可省略valuse方法,直接用for...of 循环遍历set。
⑦Set.prototype.entries():返回键值对的遍历器;
⑧Set.prototype.forEach():使用回调函数遍历每个成员;语法:forEach(function(value,key,集合本身--可省略));
⑨
11>promise对象:
promise是异步编程的一种解决方案,可以获取异步操作的消息,提供统一的API,是一个容器,保存着未来才会结束的事件;对象的状态不受外界的影响,一旦状态改变,就不会在变,从pending(进行中)变成fulfilled(已成功) 和从 pending变为rejected(已失败)
缺点:一旦新建就会立即执行,无法中途取消。如果不设置回调函数,promise内部会抛出错误,但是不会反应到外部,pending状态时无法无法得知进展到哪一个阶段;
promise构造函数接收一个函数作为参数,改函数有两个参数,resolve 和reject,他们是两个函数由JavaScript引擎提供,不用自己部署;resolve函数的作用是将promise对象的状态从未完成变为成功(pending--resolved);reject函数的作用是将promise对象的状态从未完成变成失败(pending--rejected)
16、VUE项目中给列表组件绑定key的作用:
答:key是给每一个vnode的唯一id,可以依靠key更准确,更快的拿到原来节点中对应节点,否则如果对节点进行了增删操作,节点无变化,但是节点的内容发生了更新;绑定唯一的id后key就无法复用了,利用key的唯一性生成map对象来获取对应节点;
17、防抖节流的概念,有什么区别,如何实现
防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算事件;
实现:每次触发事件时,都取消之前的延时调用方法,或者清定时器;
节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
实现:每次触发事件时都判断当前是否由等待执行的延时函数;
18、Set、Map、WeakSet、WeakMap之间的区别
Set:成员唯一、无序且不重复;可以遍历,方法有add、delete、has; [value,value],键值与键名是一致的,可以理解为没有键名;
WeakSet:成员都是对象;成员都是弱引用,可以被垃圾回收机制回收,可以用来保存dom节点,不容易造成内存泄漏;不能遍历,方法有add、delete、has
Map:本质上是键值对的集合,类似集合;可以遍历,方法很多,可以跟个数据格式转换;
WeakMap:只接受对象作为键名,null除外,不接受其他类型的值作为键名;弱引用,键值任意,键名指向的对象可以被垃圾回收机制回收,不能遍历;
19、事件循环中函数的执行顺序问题(async、await、setTimeout、Promise函数)
任务队列:js分为同步任务和异步任务;
同步任务都在主线程上执行,形成一个执行栈,主线程外事件触发线程管理一个任务队列,异步任务就会注册回调函数,存放到任务队列中去,一旦主线程执行栈中的所有同步任务执行完毕,就会读取任务队列,将可运行的异步任务添加到可执行栈中执行;
一个eventloop中可以有一个或者多个taskqueue 一个任务队列就是一个任务集合;每一个任务都有一个任务源,源自同一个tasksource 的task必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise等API便是任务源,进入任务队列的就是他们制定的具体执行任务;
宏任务:每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行);浏览器为了能够是的js内部的宏任务与dom任务能够有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面进行重新渲染
宏任务主要包含:script整体代码、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate
微任务:可以理解是当前task执行结束后立即执行的任务,也就是在当前task任务后,下一个task之前,在渲染之前;所以微任务的响应速度相比setTimeout会更快,无需等渲染,也就是说,在某一个宏任务执行完毕后,就会在它执行期间产生的所有微任务都执行完毕(渲染前)。
微任务主要含:Promise.then 、MutationObserver、Process.nextTick
运行机制:每进行一次循环操作称之tick,每一次tic的步骤如下:
执行一个宏任务,栈中没有就从任务队列中获取;
执行过程中如果遇到微任务,就将它添加到微任务的任务队列中;
宏任务执行完毕后,立即执行当前微任务队列中的所有微任务;
当前微任务执行完毕开始检查渲染,然后GUI线程接管渲染;
渲染完成后,js线程继续接管,开始下一个宏任务
Promise和async中的立即执行:Promise中的异步体现在then和catch中,所以写在Promise中的代码是被当做同步任务立即执行的,而在async/await中,在await出现前,其中的代码也是立即执行的,那么出现await,后面的表达式会先执行一遍,将await后面的代码加入到微任务中,然后就会跳出整个async函数来执行后面的代码。(await其实就是微任务)
执行顺序为:宏任务>执行栈>微任务
script代码先执行,遇见async/await时,await前的代码会立即执行,awit后的表达式会先执行,awit后的代码会加入到微任务队列中去,接着会跳出async;
20、js的异步发展历程以及优缺点
答:①回调函数:缺点:不能用try catch捕获错误,不能return;
②promise 为了解决callback的问题产生的。实现链式调用,每次then返回的都是一个全新的promise,如果我们在then中return,return的结果会被promise的resolve()包装。缺点:无法取消promise,错误需要回调函数来捕捉;
③Generator:可以控制函数的执行,可以配合co函数库使用
④Async/await异步的终极解决方案:await会导致性能上的降低;
⑤promise构造函数是同步执行的,then方法是异步执行的;
21、npm安装机制,为什么输入npm install 就可以自动安装对应的模块?
答:npm安装机制:发出npm install命令后会查询node_modules目录之中是否已经存在指定的模块,若存在,则不需要重新安装,若不存在,npm向registry查询模块压缩的网址;放在根目录.npm目录里;解压压缩到当前node_modules目录;
npm实现原理:执行工程自身preinstall;确定首层依赖模块,也就是dependencies和devdependencies属性中直接指定的模块;接着获取模块,首先要确定其版本,package.json中往往是语义化版本,如果有信息直接拿即可,如果没有,则从仓库中获取最新版本;
22、判断数组类型的三种方法优劣分析:
解析:Object.prototype.toString.call() 每一个继承Object的对象都有一个toString的方法,如果toString方法没有被重写,会返回object type 其中type为对象的类型。但是当除了object对象外,其他类型直接使用toString方法会直接返回内容的字符串,所以需要使用call或者apply方法来改变toString方法的执行上下文;基本的数据类型都能判断,null 和undefined都可以--用于判断浏览器内置对象;
instanceof的内部机制是通过判断对象的原型链中是不是能找到类型的prototype。判断一个对象是否为数组,会判断这个对象的原型链上是否会找到对应的Array的原型,找到返回true,否者返回false;只能用来判断对象类型,原始类型不可以;并且所有对象类型instanceof object都是返回true;
Array.isArray()是es5新增的方法,当不存在的时候,可用object.prototype.toString.call()方法;性能最好;
23、观察者模式 vs 发布订阅者模式:
答:观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方vuex来实现调度的,发布者和订阅者之间互不感知;发布订阅者模式更适合于复杂场景;
24、cookie和token为什么token不会被劫持
答:cookie登录后后端生成一个sessionid放在cookie中返回给客户端,并且服务器一直记录着这个sessionid,客户端以后每次请求都会带上这个sessionid,服务端通过这个sessionid来验证身份之类的操作哦,所以别人拿到了cookie也就是拿到了存放的sessionid,就可以完全替代;
token:登录后后端返回一个token给客户端,客户端将这个token存储起来,每次客户端发送请求都需要开发者手动将token放到header中去,服务端每次只需要对这个token进行验证就能使用token中的信息来进行下一步操作了;
25、vue的双向数据绑定,以及view如何改变model,model如何改变view
答:vue双向数据绑定的核心是利用es5的object.defineProperty,这也就是为什么vue不能兼容ie8以下的浏览器的原因;Object.defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象现有的属性,并返回这个对象;
Object.defineProperty(obj,prop,descriptor)---obj定义属性的对象,prop要定义或者要修改的属性;descriptor将被定义或者修改属性的描述【核心】;
observe的功能就是监测数据的变化。它是一个类,它的作用是给对象添加getter和setter,用于依赖收集和派发更新;
依赖收集getter:*const dep = new Dep()//实例化一个dep实例
*在get函数中通过dep.depend做依赖收集;
派发更新setter: *childOb = !shallow && observe(newval) //如果shllow为false的情况,会对新设置的值编程一个响应式对象;
*dep.notify() //通知所有订阅者
派发的过程:当我们的组件中对响应的数据做了修改,就会触发setter的逻辑,最后调用dep.notify()方法,他是dep的一个实例方法,具体做法是遍历依赖收集中建立的subs,然后调用每个watcher的update方法; vue在做派发更新时的一个优点,它并不会每次数据改变都会触发watcher回调,而是把这些watcher先添加到一个队列中,然后再nextTick(属于微任务)后执行watcher的run函数
vue中子组件不可以修改父组件传递的prop的值,原因是:为了保证单向数据流,易于监测数据的流动,出错容易定位,如果修改了,会触发props的set方法时发现更新,会给出警告;
Object.defineProperty的缺点:无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应
Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
26、改造下面代码,使之输入0-9,写出你能想到的所有解法。
27、BFC的概念及其应用
答:BFC就是块级格式上下文,是页面盒模型布局的一种渲染模式,相当于一个独立的容器,里面的元素和外面的元素相互不影响,创建bfc的方式有: html根元素、float浮动、绝对定位、overflow不为visiable; display为表格布局或者弹性布局;
BFC的主要作用:清浮动、防止同一bfc容器中的相邻元素间的外边距重叠问题
28、发送数据埋点使用1X1的透明gif图片的原因:
答:①没有跨域问题,img天然支持跨域;②不会阻塞页面加载,不影响用户体验;性能更好;③gif的最低合法体积最好,相比较bmp、png、jpg而言
29、让一个div水平垂直居中:
方法一:
方法二:
方法三:
方法四:
30、对MVVM开发模式的理解:
31、canvas画布与svg的区别:
答:canvas为标量图,是通过JavaScript绘制的2D图形,不支持事件处理器,文本渲染力弱,最适合图形密集型的游戏;svg为矢量图,是通过xml描述的2D图形,支持事件处理器,不适合游戏的应用,适合带有大型渲染区域的应用程序,如百度地图,谷歌地图等;
32、px、em和rem三者的区别与联系:
答: px即Pixel像素,是长度单位,可以理解为绝对像素,像素是相对于显示器屏幕而言,IE无法调整那些使用px作为单位的字体大小;
em可以理解为相对长度,相对于当前对象内文本的字体尺寸; 10px=1em ,所以px除以10加em就可以转换为em;em的值并不是固定的,会继承父级字体的大小 ;
rem是css3中新增的一个相对单位,相对于根html元素;修改根元素的大小就成比例的调整所有字体大小;可以从浏览器字体设置中继承字体大小;
33、git和svn的区别:
答:最核心的区别git是分布式的,svn不是,即使没有网络也可以commit,查看历史版本记录,创建项目分支等操作,等网络连接再push到server端;git没有一个全局版本号,git的内容完整性要优于svn;git把内容用元数据方式存储,svn是按文件,git clone效率高;
34、webpack.json 文件中必备字段:
答:两种依赖:dependencies(生产环境包的依赖:正常运行该包所需要的依赖项) 和 devDependencies(开发环境包的依赖:开发的时候需要的依赖项)
35、遍历数组的方法:
答案:① 使用for循环遍历数组;
② 使用foreach((item,index,array)=>{回调函数}); -------没有返回值,对原数组无影响,支持IE;
③ 使用array.map(function (value,index,array){回调函数 return XXX});有返回值,可以return出来,不影响原来的数组;
④ 使用 for of ;
⑤filter 进行遍历