共计 9576 个字符,预计需要花费 24 分钟才能阅读完成。
如何提取高度嵌套的对象里的指定属性?
有时会遇到一些嵌套水平十分深的对象:
const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
}
}
}
像此处的 name 这个变量,嵌套了四层,此时如果依然尝试老办法来提取它:
const {name} = school
显然是不见效的,因为 school 这个对象自身是没有 name 这个属性的,name 位于 school 对象的“儿子的儿子”对象外面。要想把 name 提取进去,一种比拟笨的办法是逐层解构:
const {classes} = school
const {stu} = classes
const {name} = stu
name // 'Bob'
然而还有一种更规范的做法,能够用一行代码来解决这个问题:
const {classes: { stu: { name} }} = school
console.log(name) // 'Bob'
能够在解构进去的变量名右侧,通过冒号 +{指标属性名}这种模式,进一步解构它,始终解构到拿到指标数据为止。
行内元素有哪些?块级元素有哪些?空 (void) 元素有那些?
- 行内元素有:
a b span img input select strong
; - 块级元素有:
div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p
;
空元素,即没有内容的 HTML 元素。空元素是在开始标签中敞开的,也就是空元素没有闭合标签:
- 常见的有:
<br>
、<hr>
、<img>
、<input>
、<link>
、<meta>
; - 鲜见的有:
<area>
、<base>
、<col>
、<colgroup>
、<command>
、<embed>
、<keygen>
、<param>
、<source>
、<track>
、<wbr>
。
同步和异步的区别
- 同步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,那么这个过程会始终期待上来,直到音讯返回为止再持续向下执行。
- 异步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,这个时候过程会持续往下执行,不会阻塞期待音讯的返回,当音讯返回时零碎再告诉过程进行解决。
代码输入后果
Promise.reject('err!!!')
.then((res) => {console.log('success', res)
}, (err) => {console.log('error', err)
}).catch(err => {console.log('catch', err)
})
输入后果如下:
error err!!!
咱们晓得,.then
函数中的两个参数:
- 第一个参数是用来解决 Promise 胜利的函数
- 第二个则是解决失败的函数
也就是说 Promise.resolve('1')
的值会进入胜利的函数,Promise.reject('2')
的值会进入失败的函数。
在这道题中,谬误间接被 then
的第二个参数捕捉了,所以就不会被 catch
捕捉了,输入后果为:error err!!!'
然而,如果是像上面这样:
Promise.resolve()
.then(function success (res) {throw new Error('error!!!')
}, function fail1 (err) {console.log('fail1', err)
}).catch(function fail2 (err) {console.log('fail2', err)
})
在 then
的第一参数中抛出了谬误,那么他就不会被第二个参数不活了,而是被前面的 catch
捕捉到。
代码输入后果
function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))
输入后果如下:
1
2
3
[1, 2, 3]
首先,定义了一个 Promise,来异步执行函数 runAsync,该函数传入一个值 x,而后距离一秒后打印出这个 x。
之后再应用 Promise.all
来执行这个函数,执行的时候,看到一秒之后输入了 1,2,3,同时输入了数组[1, 2, 3],三个函数是同步执行的,并且在一个回调函数中返回了所有的后果。并且后果和函数的执行程序是统一的。
代码输入后果
var a, b
(function () {console.log(a);
console.log(b);
var a = (b = 3);
console.log(a);
console.log(b);
})()
console.log(a);
console.log(b);
输入后果:
undefined
undefined
3
3
undefined
3
这个题目和下面题目考查的知识点相似,b 赋值为 3,b 此时是一个全局变量,而将 3 赋值给 a,a 是一个局部变量,所以最初打印的时候,a 仍旧是 undefined。
参考 前端进阶面试题具体解答
什么是 XSS 攻打?
(1)概念
XSS 攻打指的是跨站脚本攻打,是一种代码注入攻打。攻击者通过在网站注入歹意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
XSS 的实质是因为网站没有对恶意代码进行过滤,与失常的代码混合在一起了,浏览器没有方法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击者能够通过这种攻击方式能够进行以下操作:
- 获取页面的数据,如 DOM、cookie、localStorage;
- DOS 攻打,发送正当申请,占用服务器资源,从而使用户无法访问服务器;
- 毁坏页面构造;
- 流量劫持(将链接指向某网站);
(2)攻打类型
XSS 能够分为存储型、反射型和 DOM 型:
- 存储型指的是歹意脚本会存储在指标服务器上,当浏览器申请数据时,脚本从服务器传回并执行。
- 反射型指的是攻击者诱导用户拜访一个带有恶意代码的 URL 后,服务器端接收数据后处理,而后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终实现 XSS 攻打。
- DOM 型指的通过批改页面的 DOM 节点造成的 XSS。
1)存储型 XSS 的攻打步骤:
- 攻击者将恶意代码提交到⽬标⽹站的数据库中。
- ⽤户关上⽬标⽹站时,⽹站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
这种攻打常⻅于带有⽤户保留数据的⽹站性能,如论坛发帖、商品评论、⽤户私信等。
2)反射型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL 时,⽹站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库⾥,反射型 XSS 的恶意代码存在 URL ⾥。
反射型 XSS 破绽常⻅于通过 URL 传递参数的性能,如⽹站搜寻、跳转等。因为须要⽤户被动关上歹意的 URL 能力⽣效,攻击者往往会联合多种⼿段诱导⽤户点击。
3)DOM 型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL。
- ⽤户浏览器接管到响应后解析执⾏,前端 JavaScript 取出 URL 中的恶意代码并执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻打中,取出和执⾏恶意代码由浏览器端实现,属于前端 JavaScript ⾃身的安全漏洞,⽽其余两种 XSS 都属于服务端的安全漏洞。
代码输入后果
function Person(name) {this.name = name}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null 前面没有了,报错
console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null 前面没有了,报错
console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2 是实例,没有 prototype 属性
console.log(Person.constructor)//Function 一个空函数
console.log(Person.prototype)// 打印出 Person.prototype 这个对象里所有的办法和属性
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)// Object.prototype
console.log(Person.__proto__) //Function.prototype
console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Function.prototype
console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null
这道义题目考查原型、原型链的根底,记住就能够了。
浏览器渲染过程的线程有哪些
浏览器的渲染过程的线程总共有五种:(1)GUI 渲染线程 负责渲染浏览器页面,解析 HTML、CSS,构建 DOM 树、构建 CSSOM 树、构建渲染树和绘制页面;当界面须要 重绘 或因为某种操作引发 回流 时,该线程就会执行。
留神:GUI 渲染线程和 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保留在一个队列中等到 JS 引擎闲暇时立刻被执行。
(2)JS 引擎线程 JS 引擎线程也称为 JS 内核,负责解决 Javascript 脚本程序,解析 Javascript 脚本,运行代码;JS 引擎线程始终期待着工作队列中工作的到来,而后加以解决,一个 Tab 页中无论什么时候都只有一个 JS 引擎线程在运行 JS 程序;
留神:GUI 渲染线程与 JS 引擎线程的互斥关系,所以如果 JS 执行的工夫过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。
(3)工夫触发线程 工夫触发线程 属于浏览器而不是 JS 引擎,用来管制事件循环;当 JS 引擎执行代码块如 setTimeOut 时(也可是来自浏览器内核的其余线程, 如鼠标点击、AJAX 异步申请等),会将对应工作增加到事件触发线程中;当对应的事件合乎触发条件被触发时,该线程会把事件增加到待处理队列的队尾,期待 JS 引擎的解决;
留神:因为 JS 的单线程关系,所以这些待处理队列中的事件都得排队期待 JS 引擎解决(当 JS 引擎闲暇时才会去执行);
(4)定时器触发过程 定时器触发过程 即 setInterval 与 setTimeout 所在线程;浏览器定时计数器并不是由 JS 引擎计数的,因为 JS 引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因而应用独自线程来计时并触发定时器,计时结束后,增加到事件队列中,期待 JS 引擎闲暇后执行,所以定时器中的工作在设定的工夫点不肯定可能准时执行,定时器只是在指定工夫点将工作增加到事件队列中;
留神:W3C 在 HTML 规范中规定,定时器的定时工夫不能小于 4ms,如果是小于 4ms,则默认为 4ms。
(5)异步 http 申请线程
- XMLHttpRequest 连贯后通过浏览器新开一个线程申请;
- 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,期待 JS 引擎闲暇后执行;
为什么须要浏览器缓存?
对于浏览器的缓存,次要针对的是前端的动态资源,最好的成果就是,在发动申请之后,拉取相应的动态资源,并保留在本地。如果服务器的动态资源没有更新,那么在下次申请的时候,就间接从本地读取即可,如果服务器的动态资源曾经更新,那么咱们再次申请的时候,就到服务器拉取新的资源,并保留在本地。这样就大大的缩小了申请的次数,进步了网站的性能。这就要用到浏览器的缓存策略了。
所谓的 浏览器缓存 指的是浏览器将用户申请过的动态资源,存储到电脑本地磁盘中,当浏览器再次拜访时,就能够间接从本地加载,不须要再去服务端申请了。
应用浏览器缓存,有以下长处:
- 缩小了服务器的累赘,进步了网站的性能
- 放慢了客户端网页的加载速度
- 缩小了多余网络数据传输
点击刷新按钮或者按 F5、按 Ctrl+F5(强制刷新)、地址栏回车有什么区别?
- 点击刷新按钮或者按 F5: 浏览器间接对本地的缓存文件过期,然而会带上 If-Modifed-Since,If-None-Match,这就意味着服务器会对文件查看新鲜度,返回后果可能是 304,也有可能是 200。
- 用户按 Ctrl+F5(强制刷新): 浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前素来没有申请过,返回后果是 200。
- 地址栏回车:浏览器发动申请,依照失常流程,本地查看是否过期,而后服务器查看新鲜度,最初返回内容。
代码输入后果
async function async1() {console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {console.log('timer1')
}, 0)
}
async function async2() {setTimeout(() => {console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {console.log('timer3')
}, 0)
console.log("start")
输入后果如下:
async1 start
async2
start
async1 end
timer2
timer3
timer1
代码的执行过程如下:
- 首先进入
async1
,打印出async1 start
; - 之后遇到
async2
,进入async2
,遇到定时器timer2
,退出宏工作队列,之后打印async2
; - 因为
async2
阻塞了前面代码的执行,所以执行前面的定时器timer3
,将其退出宏工作队列,之后打印start
; - 而后执行 async2 前面的代码,打印出
async1 end
,遇到定时器 timer1,将其退出宏工作队列; - 最初,宏工作队列有三个工作,先后顺序为
timer2
,timer3
,timer1
,没有微工作,所以间接所有的宏工作依照先进先出的准则执行。
大数相加
题目形容: 实现一个 add 办法实现两个大数相加
let a = "9007199254740991";
let b = "1234567899999999999";
function add(a ,b){//...}
实现代码如下:
function add(a ,b){
// 取两个数字的最大长度
let maxLength = Math.max(a.length, b.length);
// 用 0 去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
// 定义加法过程中须要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t/10);
sum = t%10 + sum;
}
if(f!==0){sum = '' + f + sum;}
return sum;
}
异步任务调度器
形容:实现一个带并发限度的异步调度器 Scheduler,保障同时运行的工作最多有 limit
个。
实现:
class Scheduler {queue = []; // 用队列保留正在执行的工作
runCount = 0; // 计数正在执行的工作个数
constructor(limit) {this.maxCount = limit; // 容许并发的最大个数}
add(time, data){const promiseCreator = () => {return new Promise((resolve, reject) => {setTimeout(() => {console.log(data);
resolve();}, time);
});
}
this.queue.push(promiseCreator);
// 每次增加的时候都会尝试去执行工作
this.request();}
request() {
// 队列中还有工作才会被执行
if(this.queue.length && this.runCount < this.maxCount) {
this.runCount++;
// 执行先退出队列的函数
this.queue.shift()().then(() => {
this.runCount--;
// 尝试进行下一次工作
this.request();});
}
}
}
// 测试
const scheduler = new Scheduler(2);
const addTask = (time, data) => {scheduler.add(time, data);
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输入后果 2 3 1 4
计算属性和 watch 有什么区别? 以及它们的使用场景?
// 区别
computed 计算属性:依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值。watch 侦听器:更多的是察看的作用, 无缓存性, 相似与某些数据的监听回调, 每当监听的数据变动时都会执行回调进行后续操作
// 使用场景
当须要进行数值计算, 并且依赖与其它数据时, 应该应用 computed, 因为能够利用 computed 的缓存属性, 防止每次获取值时都要从新计算。当须要在数据变动时执行异步或开销较大的操作时, 应该应用 watch, 应用 watch 选项容许执行异步操作(拜访一个 API), 限度执行该操作的频率,并在失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。
怎么加事件监听,两种
onclick 和 addEventListener
什么是同源策略
跨域问题其实就是浏览器的同源策略造成的。
同源策略限度了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在歹意文件的重要的平安机制。同源指的是:协定 、 端口号 、 域名 必须统一。
同源策略:protocol(协定)、domain(域名)、port(端口)三者必须统一。
同源政策次要限度了三个方面:
- 以后域下的 js 脚本不可能拜访其余域下的 cookie、localStorage 和 indexDB。
- 以后域下的 js 脚本不可能操作拜访操作其余域下的 DOM。
- 以后域下 ajax 无奈发送跨域申请。
同源政策的目标次要是为了保障用户的信息安全,它只是对 js 脚本的一种限度,并不是对浏览器的限度,对于个别的 img、或者 script 脚本申请都不会有跨域的限度,这是因为这些操作都不会通过响应后果来进行可能呈现平安问题的操作。
一个 tcp 连贯能发几个 http 申请?
如果是 HTTP 1.0 版本协定,个别状况下,不反对长连贯,因而在每次申请发送结束之后,TCP 连贯即会断开,因而一个 TCP 发送一个 HTTP 申请,然而有一种状况能够将一条 TCP 连贯放弃在沉闷状态,那就是通过 Connection 和 Keep-Alive 首部,在申请头带上 Connection: Keep-Alive,并且能够通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都反对,那么其实也能够发送多条,不过此形式也有限度,能够关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连贯的限度和规定。
而如果是 HTTP 1.1 版本协定,反对了长连贯,因而只有 TCP 连接不断开,便能够始终发送 HTTP 申请,继续一直,没有下限;同样,如果是 HTTP 2.0 版本协定,反对多用复用,一个 TCP 连贯是能够并发多个 HTTP 申请的,同样也是反对长连贯,因而只有一直开 TCP 的连贯,HTTP 申请数也是能够没有下限地继续发送
什么是 CSRF 攻打?
(1)概念
CSRF 攻打指的是 跨站申请伪造攻打,攻击者诱导用户进入一个第三方网站,而后该网站向被攻打网站发送跨站申请。如果用户在被攻打网站中保留了登录状态,那么攻击者就能够利用这个登录状态,绕过后盾的用户验证,假冒用户向服务器执行一些操作。
CSRF 攻打的 实质是利用 cookie 会在同源申请中携带发送给服务器的特点,以此来实现用户的假冒。
(2)攻打类型
常见的 CSRF 攻打有三种:
- GET 类型的 CSRF 攻打,比方在网站中的一个 img 标签里构建一个申请,当用户关上这个网站的时候就会主动发动提交。
- POST 类型的 CSRF 攻打,比方构建一个表单,而后暗藏它,当用户进入页面时,主动提交这个表单。
- 链接类型的 CSRF 攻打,比方在 a 标签的 href 属性里构建一个申请,而后诱导用户去点击。
webpack3 和 webpack4 区别
1.mode
webpack 减少了一个 mode 配置,只有两种值 development | production。对不同的环境他会启用不同的配置。2.CommonsChunkPlugin
CommonChunksPlugin 曾经从 webpack4 中移除。可应用 optimization.splitChunks 进行模块划分(提取专用代码)。然而须要留神一个问题,默认配置只会对异步申请的模块进行提取拆分,如果要对 entry 进行拆分
须要设置 optimization.splitChunks.chunks = 'all'。3.webpack4 应用 MiniCssExtractPlugin 取代 ExtractTextWebpackPlugin。4. 代码宰割。应用动静 import,而不是用 system.import 或者 require.ensure
5.vue-loader。应用 vue-loader 插件为.vue 文件中的各局部应用绝对应的 loader,比方 css-loader 等
6.UglifyJsPlugin
当初也不须要应用这个 plugin 了,只须要应用 optimization.minimize 为 true 就行,production mode 上面主动为 true
optimization.minimizer 能够配置你本人的压缩程序