2020前端面试真题( JS )

JS部分
HTML + CSS
React
Vue
ES6
webpack,node.js,Git等

1.为什么JavaScript是单线程?

作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,浏览器不知道该以哪个线程为主,单线程这已经成了这门语言的核心特征。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

2.instanceof 的原理是什么?

链接
使用instanceof判断某个数据是否是数组并不完善

  • 可靠的检测数组方式
    1.利用Object的toString方法
    var list = [1,2,3];Object.prototype.toString.call(list); //[object Array]
    2.利用ES6的Array.isArray()方法
    var list = [1,2,3];Array.isArray(list); //true

数组方法集合

3.虚拟DOM为什么会提高性能

虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中。当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异,把所记录的差异应用到所构建的真正的 DOM 树上,视图就更新了

4.bind、apply、call 的用法和和区别 链接

5.什么是柯里化,并说明柯里化的好处?

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术

JS柯里化封装

		function curry(fn){
            var arr=[];
            return function(){
                if(arguments.length>0){
                    arr=arr.concat(Array.from(arguments));
                    return arguments.callee;
                }else{
                    // fn.apply(null,arr);
                   return fn(arr);
                }
            }
        }
        var fn=curry(function(arr){
            return arr.reduce((value,item)=>value+item);
        });

6.闭包

7. JS 的基本数据类型判断方法

  • typeof:其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 8 种:number、boolean、symbol、string、object、undefined、function等。
    但是 typeof 有一定的缺陷。typeof NaN 的返回值是 number,typeof [] 返回值是 object,typeof null 结果也是 object。所以用typeof除了string和boolean其他的都不太靠谱
  • instanceof:一般用来判断引用数据类型的判断,比如 Object、Function、Array、Date、RegExp等等。实现原理主要就是判断右边变量的 prototype 在不在左边变量的原型链上,判断对象和构造函数在原型链上是否有关系,如果有关系,返回真,否则返回假。所以使用 instanceof 来判断类型的话只能知道是不是要判断的这个类型,如果不是就不能知道其准确的类型。
  • Object.prototype.toString.call:toString 是 Object 原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString 运行时 this 指向的对象类型, 返回的类型格式为 [object,xxx],xxx 是具体的数据类型,包括 String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument。判断是否为数组的最佳方法

8.构造函数,实例,原型对象三者的关系

我们可以通过构造函数来创建一个实例对象 ,只要我们new了构造函数就可以产生一个实例对象,所以我们构造函数就可以指向实例对象 ,实例对象里面也有一个原型为__proto__ ,这个__proto__指向原型对象 ,并且__proto__里也有一个constructor指向构造函数,这个指回构造函数是因为实例对象的__proto__指向原型对象,原型对象的constructor可以指回构造函数,所以实例对象的constructor可以通过原型对象指回构造函数
在这里插入图片描述

9. ajax和axios、fetch的区别

10.异步解决方案有哪些?

回调函数就成为异步的第一个解决方案
• 在callback中,如果当前网络请求需要依赖前一个网络请求,我们就有可能掉进回调地狱,这样代码不好阅读而且不好维护,于是promise的出现为我们解决了这个问题,简单说,Promise的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
• ES7的asnyc/await号称是异步的终极解决方案,让我们以同步的方式来书写异步代码,这样看起来更简洁,逻辑更清晰。函数前面多了一个aync关键字。await关键字只能用在aync定义的函数内。async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。

11.介绍下事件代理,主要解决什么问题。

事件代理的靠的是事件的冒泡机制实现的。当给父元素添加事件监听器时,事件监听器会分析从子元素冒泡上来的事件,找到到底是哪个子元素的事件。
优点

  1. 可以大量节省内存占用,减少事件注册。
  2. 可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为合适

缺点

  1. 利用事件冒泡的原理,不支持不冒泡的事件;
  2. 层级过多,冒泡过程中,可能会被某层阻止掉。
  3. 理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在ol上代理li,而不是在document上代理li。
  4. 把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。

12.如何避免回调地狱

(1)拆解function:过多的嵌套(缩进)会极大的影响代码的可读性。基于这一点,
可以进行一个最简单的优化----将各个步骤拆解为单个function,该方法非常简单,具有一定的效果,但是缺少通用性。
(2)事件发布/监听模式:我们可以监听某一件事情,当事情发生的时候,进行相应的回调操作;另一方面,当某些操作完成后,通过发布事件触发回调。这样就可以将原本捆绑在一起的代码解耦。events 模块是node原生模块,用node实现这种模式只需要一个事件发布/监听的库。
(3)Promise:Promise是es6的规范首先,我们需要将异步方法改写成Promise,对于符合node规范的回调函数(第一个参数必须是Error),可以使用bluebird的promisify方法。该方法接受一个标准的异步方法并返回一个Promise对象
(4)generator:在function关键字后添加*即可将函数变为generator。执行generator将会返回一个遍历器对象,用于遍历generator内部的状态。generator函数有一个最大的特点,可以在内部执行的过程中交出程序的控制权,yield相当于起到了一个暂停的作用;而当一定的情况下,外部又将控制权再移交回来。我们用generator来封装代码,在异步任务处使用yield关键词,此时generator会将程序执行权交给其他代码,而在异步任务完成后,调用next方法来恢复yield下方代码的执行。
(5)async/await:function拆分的方式其实仅仅只是拆分代码块,时常会不利于后续的维护;事件发布/监听方式模糊了异步方法之间的流程关系;Promise虽然使得多个嵌套的异步调用能通过链式API进行操作,但是过多的then也增加了代码的冗余,也对阅读代码中各个阶段的异步任务产生了一定的干扰;通过generator虽然能提供较好的语法结构,但是毕竟generator与yield的语境用在这里多少还有点不太贴切。

13.谈谈socket的三种常见使用方式

第一种方式是 netSocket,主要使用的是node中的net模块。服务端通过 new net.createServer(); 创建服务,使用 on(‘connection’) 方法建立连接,在回调函数中即可获取到客户端发送的信息。客户端通过new net.Socket(); 创建Socket,通过 connect 连接指定端口和域名后,即可调用 write 方法发送数据;
第二种方式是 webSocket,服务端引入第三方插件 ws 创建socket服务,客户端使用H5新增API new WebSocket 连接服务端,通过 send 方法发送数据,onmessage 方法接收数据;第三种方式是 socket.io,服务端引入 socket.io’ 模块创建服务,客户端引入 socket.io.js 文件,建立连接后,客户端和服务端都是通过on方法接收数据,都是使用 emit 方法发送数据。
平常主要使用的是socket.io,原因是其兼容性好,适用于多平台多浏览器,且可靠性高速度快。

14.前后端数据交互的几种常见方式

方式1:get请求的查询字符串:扩展首部中显示 Query String Parameters
方式2:post请求的json字符串:扩展首部中显示 Request Payload,需要设置Content-Type: application/json
方式3:post请求的表单数据:扩展首部中显示 Form Data,需要设置Content-Type: application/x-www-form-urlencoded
方式4:cookie:前端可以通过cookie相关插件设置或获取cookie,后端可以使用 req.set(‘set-cookie’, ‘’)对set-cookie字段设置cookie,在req的header字段中获取cookie
方式5:自定义首部:前端在header中设置以X开头的字段作为自定义首部,后端可在req的header字段中获取,

15.new 的原理是什么?通过 new 的方式创建对象和通过字面量创建有什么区别?

new的过程主要完成了以下操作

  1. 创建一个空对象
  2. 获取构造函数
  3. 设置空对象的原型
  4. 绑定 this 并执行构造函数

通过new创建的对象和通过字面量创建的对象本质上是一样的,字面量内部也是使用new来完成的。但是通过字面量的方式比较简单,也不用调用构造函数,性能较好,所以推荐使用字面量的方式。

16.GET和POST区别

  • 请求参数

1.GET参数请求方式是通过URL传递的,多个参数以&连接,POST请求放在request body中 ;

2.GET只接受ASCII字符数据类型的参数,而POST没有限制 ;

3.GET请求在URL中传送的参数是有长度限制的,而POST没有。

  • 缓存

1.GET请求会被自动缓存,会被完整保留在浏览历史记录里, 地址支持收藏为书签, 而POST请求不可以。

  • 安全性

POST比GET安全,

1.GET请求参数直接暴露在URL上,所以不能用来传递敏感信息;

2.GET在浏览器回退时是无害的,而POST会再次请求。

  • 编码方式

GET请求只能进行url编码,而POST支持多种编码方式 。

  • 数据包

GET产生一个TCP数据包;POST产生两个TCP数据包 。

详情:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。(托货物例子)

作用:在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,发送两次包的TCP更有利于验证数据包完整性。

注意:并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值