2020,前端面试都问了啥?(面试官角度分享)

图片

01javascript





javascript做用域与预解析html

什么是预解析?前端

分两步执行:java

第一步:(代码尚未执行。预览页面以前,写完以后)node

找程序中var关键字,若是找到了提早给var定义的变量赋值undefinedwebpack

找程序中的普通函数,若是找到了,函数提高,将整个函数赋值给函数名。web

若是找的var的名字和函数名字相同,函数优先。面试

第二步: 逐行解析代码。按照上下顺序。若是碰到函数定义,忽略。ajax

重点:函数内部一样适用于js预解析。json


咱们经过几道面试题,来了解下做用域和和预解析的原理

图片

猜一猜此题中输出的结果是?可能并非你想的结果,why?


代码分析以下:

1-5行定义函数fun

6行定义变量n

7行执行函数并传入变量n

注意:fun函数内部有预解析。


预解析及执行步骤:

1. Fun函数开始执行前,将var n提早执行,初始化为undefined。

2. 因为函数传入参数n并无使用,忽略。

3. 开始执行第2行,输出为undefined。

4. 执行第3行,此时即n = 456,即将n值重置为456。

5. 执行第4行,输出改变后的n。

经过以上步骤分析,便可得知预解析的原理了(针对有var的变量提早赋初始值)


下面再看一题,看看函数预解析

图片

猜一猜此题中输出的结果是?可能并非你想的结果,why?


代码分析以下:

29行定义一个全局变量

30-32行定义一个函数f1

33-36行定义一个函数f2

37行执行函数f2

38行输出结果n

预解析及执行步骤:

1.代码执行前,预解析先初始化变量n, f1, f2,将它们都置为undefined.

2.接着执行第29行,为变量n赋值

3.接着执行第30-32行,为函数变量f1赋值,即f1为函数了

4.接着执行第33-36行,为函数变量f2赋值

5.执行第37行,即执行f2函数。

6.f2函数执行前,一样预解析,先将n初始化为undefined,接着把n赋值为456,接着调用f1函数执行。

7.f1在f2中执行,那f1的做用域应该是f2,应该输出456?

8.35行执行f1函数时无调用者,即f1函数为全局做用域,输出全局n为123

9.第38行直接输出全局变量n,即123


继续深刻,再来一题:

图片

猜一猜此题中输出的结果是?可能并非你想的结果,why?


代码分析以下:

预解析只针对var和function定义的变量


预解析及执行步骤:

1.预解析先初始化变量length, obj, f1并赋值为undefined

2.接着为变量length赋值为100

3.接着为函数变量f1赋值为函数

4.接着为变量obj赋值为对象

5.第52行,调用对象obj的f2函数执行,传入形参f1和1

6.第47行,f2函数接收实参为f1, 接着执行f1函数

7.同上,f1函数执行无调用者,做用域为全局,this指向window,输出全局变量length,即100

8.第47行,f2函数接收实参f1,若要取到全部实参则须要arguments对象,第一个参数arguments[0]==f1,第二个arguments[1]==1,依此类推。

9.第49行,arguments[0]()看上去是f1(),那也应该输出100?

10.注意arguments[0]做用域与f1的做用域并不相同,第48行直接执行f1,无调用者,做用域为全局做用域,但arguments[0]做用域为arguments对象,即this为arguments,则应输出2,由于arguments对象的属性length为2。




前端如何处理跨域

一、为何会出现跨域问题

同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,若是缺乏了同源策略,则浏览器的正常功能可能都会受到影响。能够说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具备相同的协议(protocol),主机(host)和端口号(port)。


二、什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不一样即为跨域。


当前页面url

被请求页面url

是否跨域

缘由

http://www.test.com/

http://www.test.com/index.html

同源

http://www.test.com/

https://www.test.com/index.html

跨域

协议不一样(http/https

http://www.test.com/

http://www.baidu.com/

跨域

主域名不一样(test/baidu

http://www.test.com/

http://blog.test.com

跨域

子域名不一样(www/blog

http://www.test.com:8080/

http://www.test.com:7001

跨域

端口号不一样


三、跨域解决方法

【1】设置document.domain解决没法读取非同源网页的 Cookie问题

【2】跨文档通讯 API:window.postMessage()

【3】JSONP

【4】CORS

【5】Proxy

做为开发人员,最关心的跨域通常是2种交互的跨域,即Proxy和CORS,不少开发只图一时方便,使用了Proxy,在打包后就发现又有跨域了,不知道怎么解决,下面咱们经过实例一点点给你们解析。


跨域出现

首先,须要重现跨域,先用node写一个简单的接口,以下

图片

使用命令node启动这个服务,则搭建了一个最简单的后端服务接口,而后使用前端ajax来请求这个接口,以下

图片

建立一个简单的html页面,再加上上面的简单ajax请求,在浏览器控制台就看到了跨域error了

图片

从日志上看出现了“Access-Control-Allow-Origin”,表示是访问源未被许可,即跨域了。


跨域解决之Proxy

如今项目通常都使用脚手架,即便用webpack,那可使用webpack自带的proxy特性来处理跨域,下面咱们来配置一个简单的webpack项目,以下


1.建立配置文件webpack.config.js

图片

配置文件说明项目入口文件在src中index.js,打包输出目录为dist,使用proxy处理跨域,即前端全部请求会自动跳转到target指定的url

注意这里有一个前缀,若没有能够不写。


2.建立src目录及index.js

图片

3.建立工程依赖文件package.json

图片

依赖文件中配置了webpack启动命令

Npm run dev  启动服务

Npm run start  启动服务

Npm run build  打包命令

当启动服务后,打开浏览器输入 http://localhost:8080 ,便可看到一个空白页面,打开控制台能够看到ajax请求

图片

拿到交互的数据了。

这种方式是开发最经常使用的,可是打包后就有问题了,由于打包后就不存在proxy了,跨域仍是会存在,那应该怎么解决?


跨域解决之CORS

这种方式是在后端配置,配置CORS后,前端无需任何处理便可访问后端的接口,不管是在开发时仍是部署时都是OK的。

下面,咱们把proxy注释掉,使用CORS方式处理,以下:

图片

配置了cors后,接口就能够随便访问了。

此时,还须要把前端请求地址改一下,改成直接请求后端接口,以下

图片

刷新页面,打开控制台能够看到请求地址为

图片 图片

经过此种方式,在开发阶段或部署都没有问题,这也是开发中最经常使用的2种方式。

03





什么是闭包?如何理解

闭包(closure)是javascript的一大难点,也是它的特点。不少高级应用都要依靠闭包来实现。

要理解闭包,首先要理解javascript的全局变量和局部变量。

javascript语言的特别之处就在于:函数内部能够直接读取全局变量,可是在函数外部没法读取函数内部的局部变量。

图片

如何从外部读取函数内部的局部变量?

咱们有时候须要获取到函数内部的局部变量,正常状况下,这是办不到的!只有经过变通的方法才能实现。那就是在函数内部,再定义一个函数。


一、闭包的概念

上面代码中的f2函数,就是闭包。

各类专业文献的闭包定义都很是抽象,个人理解是: 闭包就是可以读取其余函数内部变量的函数。

因为在javascript中,只有函数内部的子函数才能读取局部变量,因此说,闭包能够简单理解成“定义在一个函数内部的函数“。

因此,在本质上,闭包是将函数内部和函数外部链接起来的桥梁。


二、闭包的用途

闭包能够用在许多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。

为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。


在咱们平时的代码中常常会用到闭包,好比在构造函数中

图片

//另外一种写法

图片

三、常见闭包的写法

图片

另外一种调用方法

图片

//定义函数并当即调用

图片

四、闭包的实际应用

使用闭包,咱们能够作不少事情。好比模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提高代码的执行效率。


封装

图片

经过person.name是没法获取到name的值,若是要获取到name的值能够经过

Console.log(person.getName());   //直接获取到 张三

person.setName("李四");     //从新设置新的名字

print(person.getName());      //获取 李四


继承

图片

总结:闭包就是一个函数引用另一个函数的变量,由于变量被引用着因此不会被回收,所以能够用来封装一个私有变量。这是优势也是缺点,没必要要的闭包只会徒增内存消耗!