对作用域、作用域链的了解
1)全局作用域和函数作用域
(1)全局作用域
- 最外层函数和最外层函数里面定义的变量领有全局作用域
- 所有未定义间接赋值的变量主动申明为全局作用域
- 所有 window 对象的属性领有全局作用域
- 全局作用域有很大的弊病,过多的全局作用域变量会净化全局命名空间,容易引起命名抵触。
(2)函数作用域
- 函数作用域申明在函数外部的变零,个别只有固定的代码片段能够拜访到
- 作用域是分层的,内层作用域能够拜访外层作用域,反之不行
2)块级作用域
- 应用 ES6 中新增的 let 和 const 指令能够申明块级作用域,块级作用域能够在函数中创立也能够在一个代码块中的创立(由
{}
包裹的代码片段) - let 和 const 申明的变量不会有变量晋升,也不能够反复申明
- 在循环中比拟适宜绑定块级作用域,这样就能够把申明的计数器变量限度在循环外部。
作用域链: 在以后作用域中查找所需变量,然而该作用域没有这个变量,那这个变量就是自在变量。如果在本人作用域找不到该变量就去父级作用域查找,顺次向下级作用域查找,直到拜访到 window 对象就被终止,这一层层的关系就是作用域链。
作用域链的作用是 保障对执行环境有权拜访的所有变量和函数的有序拜访,通过作用域链,能够拜访到外层环境的变量和函数。
作用域链的实质上是一个指向变量对象的指针列表。变量对象是一个蕴含了执行环境中所有变量和函数的对象。作用域链的前端始终都是以后执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最初一个对象。
当查找一个变量时,如果以后执行环境中没有找到,能够沿着作用域链向后查找。
前端进阶面试题具体解答
Virtual DOM 的工作原理是什么
- 虚构 DOM 的工作原理是
通过 JS 对象模仿 DOM 的节点
。在 Facebook 构建 React 初期时,思考到要晋升代码形象能力、防止人为的 DOM 操作、升高代码整体危险等因素,所以引入了虚构 DOM - 虚构 DOM 在实现上通常是
Plain Object
,以 React 为例,在render
函数中写的JSX
会在Babel
插件的作用下,编译为React.createElement
执行JSX
中的属性参数 React.createElement
执行后会返回一个Plain Object
,它会形容本人的tag
类型、props
属性以及children
状况等。这些Plain Object
通过树形构造组成一棵虚构DOM
树。当状态产生变更时,将变更前后的虚构DOM
树进行差别比拟,这个过程称为diff
,生成的后果称为patch
。计算之后,会渲染Patch
实现对实在DOM
的操作。- 虚构 DOM 的长处次要有三点:
改善大规模
DOM操作的性能
、躲避 XSS 危险
、能以较低的老本实现跨平台开发
。 -
虚构 DOM 的毛病在社区中次要有两点
- 内存占用较高,因为须要模仿整个网页的实在
DOM
- 高性能利用场景存在难以优化的状况,相似像 Google Earth 一类的高性能前端利用在技术选型上往往不会抉择 React
- 内存占用较高,因为须要模仿整个网页的实在
除了渲染页面,虚构 DOM 还有哪些利用场景?
这个问题考验面试者的想象力。通常而言,咱们只是将虚构 DOM 与渲染绑定在一起,但实际上虚构 DOM 的利用更为广大。比方,只有你记录了实在 DOM 变更,它甚至能够利用于埋点统计与数据记录等。
SSR 原理
借助虚构 dom, 服务器中没有 dom 概念的,react 奇妙的借助虚构 dom,而后能够在服务器中 nodejs 能够运行起来 react 代码。
DNS 同时应用 TCP 和 UDP 协定?
DNS 占用 53 号端口,同时应用 TCP 和 UDP 协定。(1)在区域传输的时候应用 TCP 协定
- 辅域名服务器会定时(个别 3 小时)向主域名服务器进行查问以便理解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。区域传送应用 TCP 而不是 UDP,因为数据同步传送的数据量比一个申请应答的数据量要多得多。
- TCP 是一种牢靠连贯,保障了数据的准确性。
(2)在域名解析的时候应用 UDP 协定
- 客户端向 DNS 服务器查问域名,个别返回的内容都不超过 512 字节,用 UDP 传输即可。不必通过三次握手,这样 DNS 服务器负载更低,响应更快。实践上说,客户端也能够指定向 DNS 服务器查问时用 TCP,但事实上,很多 DNS 服务器进行配置的时候,仅反对 UDP 查问包。
数组有哪些原生办法?
- 数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 办法能够指定转换为字符串时的分隔符。
- 数组尾部操作的办法 pop() 和 push(),push 办法能够传入多个参数。
- 数组首部操作的办法 shift() 和 unshift() 重排序的办法 reverse() 和 sort(),sort() 办法能够传入一个函数来进行比拟,传入前后两个值,如果返回值为负数,则替换两个参数的地位。
- 数组连贯的办法 concat(),返回的是拼接好的数组,不影响原数组。
- 数组截取方法 slice(),用于截取数组中的一部分返回,不影响原数组。
- 数组插入方法 splice(),影响原数组查找特定项的索引的办法,indexOf() 和 lastIndexOf() 迭代办法 every()、some()、filter()、map() 和 forEach() 办法
- 数组归并办法 reduce() 和 reduceRight() 办法
map 和 foreach 有什么区别
foreach()办法会针对每一个元素执行提供得函数, 该办法没有返回值, 是否会扭转原数组取决与数组元素的类型是根本类型还是援用类型
map()办法不会扭转原数组的值, 返回一个新数组, 新数组中的值为原数组调用函数解决之后的值:
和谐阶段 setState 外部干了什么
- 当调用 setState 时,React 会做的第一件事件是将传递给 setState 的对象合并到组件的以后状态
- 这将启动一个称为和解(
reconciliation
)的过程。和解(reconciliation
)的最终目标是以最无效的形式,依据这个新的状态来更新UI
。为此,React
将构建一个新的React
元素树(您能够将其视为UI
的对象示意) - 一旦有了这个树,为了弄清 UI 如何响应新的状态而扭转,React 会将这个新树与上一个元素树相比拟(diff)
通过这样做,React 将会晓得产生的确切变动,并且通过理解产生什么变动,只需在相对必要的状况下进行更新即可最小化 UI 的占用空间
实现一个 add 办法
题目形容: 实现一个 add 办法 使计算结果可能满足如下预期:
add(1)(2)(3)()=6
add(1,2,3)(4)()=10
其实就是考函数柯里化
实现代码如下:
function add(...args) {let allArgs = [...args];
function fn(...newArgs) {allArgs = [...allArgs, ...newArgs];
return fn;
}
fn.toString = function () {if (!allArgs.length) {return;}
return allArgs.reduce((sum, cur) => sum + cur);
};
return fn;
}
XSS 和 CSRF
1. XSS
涉及面试题:什么是
XSS
攻打?如何防备XSS
攻打?什么是CSP
?
XSS
简略点来说,就是攻击者想尽一切办法将能够执行的代码注入到网页中。XSS
能够分为多种类型,然而总体上我认为分为两类:长久型和非长久型。- 长久型也就是攻打的代码被服务端写入进数据库中,这种攻打危害性很大,因为如果网站访问量很大的话,就会导致大量失常拜访页面的用户都受到攻打。
举个例子,对于评论性能来说,就得防备长久型
XSS
攻打,因为我能够在评论中输出以下内容
- 这种状况如果前后端没有做好进攻的话,这段评论就会被存储到数据库中,这样每个关上该页面的用户都会被攻打到。
- 非长久型相比于前者危害就小的多了,个别通过批改
URL
参数的形式退出攻打代码,诱导用户拜访链接从而进行攻打。
举个例子,如果页面须要从
URL
中获取某些参数作为内容的话,不通过过滤就会导致攻打代码被执行
<!-- http://www.domain.com?name=<script>alert(1)</script> -->
<div>{{name}}</div>
然而对于这种攻击方式来说,如果用户应用
Chrome
这类浏览器的话,浏览器就能主动帮忙用户进攻攻打。然而咱们不能因而就不进攻此类攻打了,因为我不能确保用户都应用了该类浏览器。
对于
XSS
攻打来说,通常有两种形式能够用来进攻。
- 转义字符
首先,对于用户的输出应该是永远不信赖的。最广泛的做法就是本义输入输出的内容,对于引号、尖括号、斜杠进行本义
function escape(str) {str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g,'&quto;')
str = str.replace(/'/g,''')
str = str.replace(/`/g, '`')
str = str.replace(/\//g, '/')
return str
}
通过本义能够将攻打代码
<script>alert(1)</script>
变成
// -> <script>alert(1)</script>
escape('<script>alert(1)</script>')
然而对于显示富文本来说,显然不能通过下面的方法来本义所有字符,因为这样会把须要的格局也过滤掉。对于这种状况,通常采纳白名单过滤的方法,当然也能够通过黑名单过滤,然而思考到须要过滤的标签和标签属性切实太多,更加举荐应用白名单的形式
const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
// -> <h1>XSS Demo</h1><script>alert("xss");</script>
console.log(html)
以上示例应用了
js-xss
来实现,能够看到在输入中保留了h1
标签且过滤了script
标签
- CSP
CSP
实质上就是建设白名单,开发者明确通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡是由浏览器本人实现的。咱们能够通过这种形式来尽量减少XSS
攻打。
通常能够通过两种形式来开启 CSP:
- 设置
HTTP Header
中的Content-Security-Policy
- 设置
meta
标签的形式<meta http-equiv="Content-Security-Policy">
这里以设置 HTTP Header
来举例
只容许加载本站资源
Content-Security-Policy: default-src‘self’
只容许加载 HTTPS 协定图片
Content-Security-Policy: img-src https://*
容许加载任何起源框架
Content-Security-Policy: child-src 'none'
当然能够设置的属性远不止这些,你能够通过查阅 文档 (opens new window)的形式来学习,这里就不过多赘述其余的属性了。
对于这种形式来说,只有开发者配置了正确的规定,那么即便网站存在破绽,攻击者也不能执行它的攻打代码,并且
CSP
的兼容性也不错。
2 CSRF
跨站申请伪造(英语:
Cross-site request forgery
),也被称为one-click attack
或者session riding
,通常缩写为CSRF
或者XSRF
,是一种挟制用户在以后已登录的Web
应用程序上执行非本意的操作的攻打办法
CSRF
就是利用用户的登录态发动歹意申请
如何攻打
假如网站中有一个通过 Get 申请提交用户评论的接口,那么攻击者就能够在钓鱼网站中退出一个图片,图片的地址就是评论接口
<img src="http://www.domain.com/xxx?comment='attack'"/>
res.setHeader('Set-Cookie', `username=poetry2;sameSite = strict;path=/;httpOnly;expires=${getCookirExpires()}`)
在 B 网站,危险网站向 A 网站发动申请
<!DOCTYPE html>
<html>
<body>
<!-- 利用 img 主动发送申请 -->
<img src="http://localhost:8000/api/user/login" />
</body>
</html>
会带上 A 网站的 cookie
// 在 A 网站下发 cookie 的时候,加上 sameSite=strict,这样 B 网站在发送 A 网站申请,不会主动带上 A 网站的 cookie,保障了平安
// NAME=VALUE 赋予 Cookie 的名称及对应值
// expires=DATE Cookie 的有效期
// path=PATH 赋予 Cookie 的名称及对应值
// domain= 域名 作为 Cookie 实用对象的域名(若不指定则默认为创立 Cookie 的服务器的域名)(个别不指定)
// Secure 仅在 HTTPS 平安通信时才会发送 Cookie
// HttpOnly 加以限度,使 Cookie 不能被 JavaScript 脚本拜访
// SameSite Lax|Strict|None 它容许您申明该 Cookie 是否仅限于第一方或者同一站点上下文
res.setHeader('Set-Cookie', `username=poetry;sameSite=strict;path=/;httpOnly;expires=${getCookirExpires()}`)
如何进攻
Get
申请不对数据进行批改- 不让第三方网站拜访到用户
Cookie
- 阻止第三方网站申请接口
- 申请时附带验证信息,比方验证码或者
token
SameSite Cookies
: 只能以后域名的网站收回的 http 申请,携带这个Cookie
。当然,因为这是新的 cookie 属性,在兼容性上必定会有问题
CSRF 攻打,仅仅是利用了 http 携带 cookie 的个性进行攻打的,然而攻打站点还是无奈失去被攻打站点的 cookie。这个和 XSS 不同,XSS 是间接通过拿到 Cookie 等信息进行攻打的
在 CSRF 攻打中,就 Cookie 相干的个性:
- http 申请,会主动携带 Cookie。
- 携带的 cookie,还是 http 申请所在域名的 cookie。
3 明码平安
加盐
对于明码存储来说,必然是不能明文存储在数据库中的,否则一旦数据库泄露,会对用户造成很大的损失。并且不倡议只对明码单纯通过加密算法加密,因为存在彩虹表的关系
- 通常须要对明码加盐,而后进行几次不同加密算法的加密
// 加盐也就是给原明码增加字符串,减少原明码长度
sha256(sha1(md5(salt + password + salt)))
然而加盐并不能阻止他人盗取账号,只能确保即便数据库泄露,也不会裸露用户的实在明码。一旦攻击者失去了用户的账号,能够通过暴力破解的形式破解明码。对于这种状况,通常应用验证码减少延时或者限度尝试次数的形式。并且一旦用户输出了谬误的明码,也不能间接提醒用户输错明码,而应该提醒账号或明码谬误
前端加密
尽管前端加密对于平安防护来说意义不大,然而在遇到中间人攻打的状况下,能够防止明文明码被第三方获取
4. 总结
XSS
:跨站脚本攻打,是一种网站应用程序的安全漏洞攻打,是代码注入的一种。常见形式是将恶意代码注入非法代码里暗藏起来,再诱发恶意代码,从而进行各种各样的非法活动
防备:记住一点“所有用户输出都是不可信的”,所以得做输出过滤和本义
CSRF
:跨站申请伪造,也称XSRF
,是一种挟制用户在以后已登录的Web
应用程序上执行非本意的操作的攻打办法。与XSS
相比,XSS
利用的是用户对指定网站的信赖,CSRF
利用的是网站对用户网页浏览器的信赖。
防备:用户操作验证(验证码),额定验证机制(
token
应用)等
TCP 的牢靠传输机制
TCP 的牢靠传输机制是基于间断 ARQ 协定和滑动窗口协定的。
TCP 协定在发送方维持了一个发送窗口,发送窗口以前的报文段是曾经发送并确认了的报文段,发送窗口中蕴含了曾经发送但 未确认的报文段和容许发送但还未发送的报文段,发送窗口当前的报文段是缓存中还不容许发送的报文段。当发送方向接管方发 送报文时,会顺次发送窗口内的所有报文段,并且设置一个定时器,这个定时器能够了解为是最早发送但未收到确认的报文段。如果在定时器的工夫内收到某一个报文段的确认答复,则滑动窗口,将窗口的首部向后滑动到确认报文段的后一个地位,此时如 果还有已发送但没有确认的报文段,则从新设置定时器,如果没有了则敞开定时器。如果定时器超时,则从新发送所有曾经发送 但还未收到确认的报文段,并将超时的距离设置为以前的两倍。当发送方收到接管方的三个冗余的确认应答后,这是一种批示,阐明该报文段当前的报文段很有可能产生失落了,那么发送方会启用疾速重传的机制,就是以后定时器完结前,发送所有的已发 送但确认的报文段。
接管方应用的是累计确认的机制,对于所有按序达到的报文段,接管方返回一个报文段的必定答复。如果收到了一个乱序的报文 段,那么接方会间接抛弃,并返回一个最近的按序达到的报文段的必定答复。应用累计确认保障了返回的确认号之前的报文段都 曾经按序达到了,所以发送窗口能够挪动到已确认报文段的前面。
发送窗口的大小是变动的,它是由接管窗口残余大小和网络中拥塞水平来决定的,TCP 就是通过管制发送窗口的长度来管制报文 段的发送速率。
然而 TCP 协定并不齐全和滑动窗口协定雷同,因为许多的 TCP 实现会将失序的报文段给缓存起来,并且产生重传时,只会重 传一个报文段,因而 TCP 协定的牢靠传输机制更像是窗口滑动协定和抉择重传协定的一个混合体。
代码输入问题
function A(){}
function B(a){this.a = a;}
function C(a){if(a){this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);
输入后果:1 undefined 2
解析:
- console.log(new A().a),new A()为构造函数创立的对象,自身没有 a 属性,所以向它的原型去找,发现原型的 a 属性的属性值为 1,故该输入值为 1;
- console.log(new B().a),ew B()为构造函数创立的对象,该构造函数有参数 a,但该对象没有传参,故该输入值为 undefined;
- console.log(new C(2).a),new C()为构造函数创立的对象,该构造函数有参数 a,且传的实参为 2,执行函数外部,发现 if 为真,执行 this.a = 2, 故属性 a 的值为 2。
说一下 SPA 单页面有什么优缺点?
长处:1. 体验好,不刷新,缩小 申请 数据 ajax 异步获取 页面流程;2. 前后端拆散
3. 加重服务端压力
4. 共用一套后端程序代码,适配多端
毛病:1. 首屏加载过慢;2.SEO 不利于搜索引擎抓取
Set,Map 解构
ES6 提供了新的数据结构 Set。它相似于数组,然而成员的值都是惟一的,没有反复的值。Set 自身是一个构造函数,用来生成 Set 数据结构。ES6 提供了 Map 数据结构。它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。
冒泡排序 – 工夫复杂度 n^2
题目形容: 实现一个冒泡排序
实现代码如下:
function bubbleSort(arr) {
// 缓存数组长度
const len = arr.length;
// 外层循环用于管制从头到尾的比拟 + 替换到底有多少轮
for (let i = 0; i < len; i++) {
// 内层循环用于实现每一轮遍历过程中的反复比拟 + 替换
for (let j = 0; j < len - 1; j++) {
// 若相邻元素后面的数比前面的大
if (arr[j] > arr[j + 1]) {
// 替换两者
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
// 返回数组
return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));
CSS 预处理器 / 后处理器是什么?为什么要应用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译 sass
或者 less
,减少了css
代码的复用性。层级,mixin
,变量,循环,函数等对编写以及开发 UI 组件都极为不便。
后处理器, 如:postCss
,通常是在实现的样式表中依据 css
标准解决 css
,让其更加无效。目前最常做的是给css
属性增加浏览器公有前缀,实现跨浏览器兼容性的问题。
css
预处理器为 css
减少一些编程个性,无需思考浏览器的兼容问题,能够在 CSS
中应用变量,简略的逻辑程序,函数等在编程语言中的一些根本的性能,能够让 css
更加的简洁,减少适应性以及可读性,可维护性等。
其它 css
预处理器语言:Sass(Scss)
, Less
, Stylus
, Turbine
, Swithch css
, CSS Cacheer
, DT Css
。
应用起因:
- 构造清晰,便于扩大
- 能够很不便的屏蔽浏览器公有语法的差别
- 能够轻松实现多重继承
- 完满的兼容了
CSS
代码,能够利用到老我的项目中
对类数组对象的了解,如何转化为数组
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,函数参数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
- 通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
- 通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
- 通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
- 通过 Array.from 办法来实现转换
Array.from(arrayLike);
页面有多张图片,HTTP 是怎么的加载体现?
- 在
HTTP 1
下,浏览器对一个域名下最大 TCP 连接数为 6,所以会申请屡次。能够用 多域名部署 解决。这样能够进步同时申请的数目,放慢页面图片的获取速度。 - 在
HTTP 2
下,能够一瞬间加载进去很多资源,因为,HTTP2 反对多路复用,能够在一个 TCP 连贯中发送多个 HTTP 申请。
HTTP 之 URL
URI
是用来惟一标记服务器上资源的一个字符串,通常也称为 URL;URI
通常由scheme
、host:port
、path
和query
四个局部组成,有的能够省略;scheme
叫“计划名”或者“协定名”,示意资源应该应用哪种协定来拜访;- “
host:port
”示意资源所在的主机名和端口号; path
标记资源所在的地位;query
示意对资源附加的额定要求;- 在
URI
里对“@&/
”等特殊字符和汉字必须要做编码,否则服务器收到HTTP
报文后会无奈正确处理
说一下 web worker
在 HTML 页面中,如果在执行脚本时,页面的状态是不可相应的,直到脚本执行实现后,页面才变成可相应。web worker 是运行在后盾的 js,独立于其余脚本,不会影响页面的性能。并且通过 postMessage 将后果回传到主线程。这样在进行简单操作的时候,就不会阻塞主线程了。
如何创立 web worker:
- 检测浏览器对于 web worker 的支持性
- 创立 web worker 文件(js,回传函数等)
- 创立 web worker 对象
Promise.all
形容:所有 promise
的状态都变成 fulfilled
,就会返回一个状态为 fulfilled
的数组(所有promise
的 value
)。只有有一个失败,就返回第一个状态为 rejected
的 promise
实例的 reason
。
实现:
Promise.all = function(promises) {return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return resolve(promises);
let result = [];
let count = 0;
promises.forEach((item, index) => {Promise.resolve(item).then(
value => {
count++;
result[index] = value;
if(count === promises.length) resolve(result);
},
reason => reject(reason)
);
})
}
else return reject(new TypeError("Argument is not iterable"));
});
}
说一说正向代理和反向代理
正向代理
咱们常说的代理也就是指正向代理,正向代理的过程,它暗藏了实在的申请客户端,服务端不晓得实在的客户端是谁,客户端申请的服务都被代理服务器代替来申请。
反向代理
这种代理模式下,它暗藏了实在的服务端,当咱们向一个网站发动申请的时候,背地可能有成千上万台服务器为咱们服务,具体是哪一台,咱们不分明,咱们只须要晓得反向代理服务器是谁就行,而且反向代理服务器会帮咱们把申请转发到实在的服务器那里去,一般而言反向代理服务器个别用来实现负载平衡。
负载平衡的两种实现形式?
- 一种是应用反向代理的形式,用户的申请都发送到反向代理服务上,而后由反向代理服务器来转发申请到实在的服务器上,以此来实现集群的负载平衡。
- 另一种是 DNS 的形式,DNS 能够用于在冗余的服务器上实现负载平衡。因为当初个别的大型网站应用多台服务器提供服务,因而一个域名可能会对应多个服务器地址。当用户向网站域名申请的时候,DNS 服务器返回这个域名所对应的服务器 IP 地址的汇合,但在每个答复中,会循环这些 IP 地址的程序,用户个别会抉择排在后面的地址发送申请。以此将用户的申请平衡的调配到各个不同的服务器上,这样来实现负载平衡。这种形式有一个毛病就是,因为 DNS 服务器中存在缓存,所以有可能一个服务器呈现故障后,域名解析依然返回的是那个 IP 地址,就会造成拜访的问题。