对 rest 参数的了解
扩大运算符被用在函数形参上时,它还能够把一个拆散的参数序列整合成一个数组:
function mutiple(...args) {
let result = 1;
for (var val of args) {result *= val;}
return result;
}
mutiple(1, 2, 3, 4) // 24
这里,传入 mutiple 的是四个拆散的参数,然而如果在 mutiple 函数里尝试输入 args 的值,会发现它是一个数组:
function mutiple(...args) {console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
这就是 … rest 运算符的又一层威力了,它能够把函数的多个入参收敛进一个数组里。这一点 常常用于获取函数的多余参数,或者像下面这样解决函数参数个数不确定的状况。
PWA 应用过吗?serviceWorker 的应用原理是啥?
渐进式网络应用(PWA)
是谷歌在 2015 年底提出的概念。基本上算是 web 应用程序,但在外观和感觉上与 原生 app
相似。反对 PWA
的网站能够提供脱机工作、推送告诉和设施硬件拜访等性能。
Service Worker
是浏览器在后盾独立于网页运行的脚本,它关上了通向不须要网页或用户交互的性能的大门。当初,它们已包含如推送告诉和后盾同步等性能。未来,Service Worker
将会反对如定期同步或天文围栏等其余性能。本教程探讨的外围性能是拦挡和解决网络申请,包含通过程序来治理缓存中的响应。
ES6 之前应用 prototype 实现继承
Object.create() 会创立一个“新”对象,而后将此对象外部的 [[Prototype]] 关联到你指定的对象(Foo.prototype)。Object.create(null) 创立一个空 [[Prototype]] 链接的对象,这个对象无奈进行委托。
function Foo(name) {this.name = name;}
Foo.prototype.myName = function () {return this.name;}
// 继承属性,通过借用结构函数调用
function Bar(name, label) {Foo.call(this, name);
this.label = label;
}
// 继承办法,创立备份
Bar.prototype = Object.create(Foo.prototype);
// 必须设置回正确的构造函数,要不然在会产生判断类型出错
Bar.prototype.constructor = Bar;
// 必须在上一步之后
Bar.prototype.myLabel = function () {return this.label;}
var a = new Bar("a", "obj a");
a.myName(); // "a"
a.myLabel(); // "obj a"
代码输入后果
async function async1() {console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {console.log("async2");
}
console.log("script start");
setTimeout(function() {console.log("setTimeout");
}, 0);
async1();
new Promise(resolve => {console.log("promise1");
resolve();}).then(function() {console.log("promise2");
});
console.log('script end')
输入后果如下:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
代码执行过程如下:
- 结尾定义了 async1 和 async2 两个函数,然而并未执行,执行 script 中的代码,所以打印出 script start;
- 遇到定时器 Settimeout,它是一个宏工作,将其退出到宏工作队列;
- 之后执行函数 async1,首先打印出 async1 start;
- 遇到 await,执行 async2,打印出 async2,并阻断前面代码的执行,将前面的代码退出到微工作队列;
- 而后跳出 async1 和 async2,遇到 Promise,打印出 promise1;
- 遇到 resolve,将其退出到微工作队列,而后执行前面的 script 代码,打印出 script end;
- 之后就该执行微工作队列了,首先打印出 async1 end,而后打印出 promise2;
- 执行完微工作队列,就开始执行宏工作队列中的定时器,打印出 setTimeout。
JS 数据类型
根本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020)
援用类型:Object,对象子类型(Array,Function)
说一下 HTTP 和 HTTPS 协定的区别?
1、HTTPS 协定须要 CA 证书, 费用较高; 而 HTTP 协定不须要
2、HTTP 协定是超文本传输协定, 信息是明文传输的,HTTPS 则是具备安全性的 SSL 加密传输协定;
3、应用不同的连贯形式, 端口也不同,HTTP 协定端口是 80,HTTPS 协定端口是 443;
4、HTTP 协定连贯很简略, 是无状态的;HTTPS 协定是具备 SSL 和 HTTP 协定构建的可进行加密传输、身份认证的网络协议, 比 HTTP 更加平安
参考 前端进阶面试题具体解答
JSONP
JSONP 外围原理:script 标签不受同源策略束缚,所以能够用来进行跨域申请,长处是兼容性好,然而只能用于 GET 申请;
const jsonp = ({url, params, callbackName}) => {const generateUrl = () => {
let dataSrc = ''
for (let key in params) {if (params.hasOwnProperty(key)) {dataSrc += `${key}=${params[key]}&`
}
}
dataSrc += `callback=${callbackName}`
return `${url}?${dataSrc}`
}
return new Promise((resolve, reject) => {const scriptEle = document.createElement('script')
scriptEle.src = generateUrl()
document.body.appendChild(scriptEle)
window[callbackName] = data => {resolve(data)
document.removeChild(scriptEle)
}
})
}
事件总线(公布订阅模式)
class EventEmitter {constructor() {this.cache = {}
}
on(name, fn) {if (this.cache[name]) {this.cache[name].push(fn)
} else {this.cache[name] = [fn]
}
}
off(name, fn) {let tasks = this.cache[name]
if (tasks) {const index = tasks.findIndex(f => f === fn || f.callback === fn)
if (index >= 0) {tasks.splice(index, 1)
}
}
}
emit(name, once = false, ...args) {if (this.cache[name]) {
// 创立正本,如果回调函数内持续注册雷同事件,会造成死循环
let tasks = this.cache[name].slice()
for (let fn of tasks) {fn(...args)
}
if (once) {delete this.cache[name]
}
}
}
}
// 测试
let eventBus = new EventEmitter()
let fn1 = function(name, age) {console.log(`${name} ${age}`)
}
let fn2 = function(name, age) {console.log(`hello, ${name} ${age}`)
}
eventBus.on('aaa', fn1)
eventBus.on('aaa', fn2)
eventBus.emit('aaa', false, '布兰', 12)
// '布兰 12'
// 'hello, 布兰 12'
如何优化动画?
对于如何优化动画,咱们晓得,个别状况下,动画须要频繁的操作 DOM,就就会导致页面的性能问题,咱们能够将动画的 position
属性设置为 absolute
或者fixed
,将动画脱离文档流,这样他的回流就不会影响到页面了。
10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路
这个问题置信很多人会第一工夫想到 Promise.all
,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路
// 以下是不残缺代码,着重于思路 非 Promise 写法
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {if (success) {
success++
if (success + errorCount === 10) {console.log(datas)
} else {datas.push(res.data)
}
} else {
errorCount++
if (errorCount > 3) {
// 失败次数大于 3 次就应该报错了
throw Error('失败三次')
}
}
})
// Promise 写法
let errorCount = 0
let p = new Promise((resolve, reject) => {if (success) {resolve(res.data)
} else {
errorCount++
if (errorCount > 3) {
// 失败次数大于 3 次就应该报错了
reject(error)
} else {resolve(error)
}
}
})
Promise.all([p]).then(v => {console.log(v);
});
代码输入后果
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
这道义题目考查原型、原型链的根底,记住就能够了。
数据类型判断
typeof 能够正确辨认:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,然而对于其余的都会认为是 object,比方 Null、Date 等,所以通过 typeof 来判断数据类型会不精确。然而能够应用 Object.prototype.toString 实现。
function typeOf(obj) {- let res = Object.prototype.toString.call(obj).split(' ')[1]
- res = res.substring(0, res.length - 1).toLowerCase()
- return res
// 更好的写法
+ return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}
typeOf([]) // 'array'
typeOf({}) // 'object'
typeOf(new Date) // 'date'
浏览器渲染优化
(1)针对 JavaScript: JavaScript 既会阻塞 HTML 的解析,也会阻塞 CSS 的解析。因而咱们能够对 JavaScript 的加载形式进行扭转,来进行优化:
(1)尽量将 JavaScript 文件放在 body 的最初
(2)body 两头尽量不要写 <script>
标签
(3)<script>
标签的引入资源形式有三种,有一种就是咱们罕用的间接引入,还有两种就是应用 async 属性和 defer 属性来异步引入,两者都是去异步加载内部的 JS 文件,不会阻塞 DOM 的解析(尽量应用异步加载)。三者的区别如下:
- script 立刻进行页面渲染去加载资源文件,当资源加载结束后立刻执行 js 代码,js 代码执行结束后持续渲染页面;
- async 是在下载实现之后,立刻异步加载,加载好后立刻执行,多个带 async 属性的标签,不能保障加载的程序;
- defer 是在下载实现之后,立刻异步加载。加载好后,如果 DOM 树还没构建好,则先等 DOM 树解析好再执行;如果 DOM 树曾经筹备好,则立刻执行。多个带 defer 属性的标签,依照程序执行。
(2)针对 CSS:应用 CSS 有三种形式:应用link、@import、内联款式,其中 link 和 @import 都是导入内部款式。它们之间的区别:
- link:浏览器会派发一个新等线程 (HTTP 线程) 去加载资源文件,与此同时 GUI 渲染线程会持续向下渲染代码
- @import:GUI 渲染线程会临时进行渲染,去服务器加载资源文件,资源文件没有返回之前不会持续渲染(妨碍浏览器渲染)
- style:GUI 间接渲染
内部款式如果长时间没有加载结束,浏览器为了用户体验,会应用浏览器会默认款式,确保首次渲染的速度。所以 CSS 个别写在 headr 中,让浏览器尽快发送申请去获取 css 款式。
所以,在开发过程中,导入内部款式应用 link,而不必 @import。如果 css 少,尽可能采纳内嵌款式,间接写在 style 标签中。
(3)针对 DOM 树、CSSOM 树: 能够通过以下几种形式来缩小渲染的工夫:
- HTML 文件的代码层级尽量不要太深
- 应用语义化的标签,来防止不规范语义化的非凡解决
- 缩小 CSSD 代码的层级,因为选择器是从左向右进行解析的
(4)缩小回流与重绘:
- 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
- 不要应用
table
布局,一个小的改变可能会使整个table
进行从新布局 - 应用 CSS 的表达式
- 不要频繁操作元素的款式,对于动态页面,能够批改类名,而不是款式。
- 应用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其余元素
- 防止频繁操作 DOM,能够创立一个文档片段
documentFragment
,在它下面利用所有 DOM 操作,最初再把它增加到文档中 - 将元素先设置
display: none
,操作完结后再把它显示进去。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。 - 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于 浏览器的渲染队列机制。
浏览器针对页面的回流与重绘,进行了本身的优化——渲染队列
浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了肯定的数量或者到了肯定的工夫距离,浏览器就会对队列进行批处理。这样就会让屡次的回流、重绘变成一次回流重绘。
将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,本来应该是触发屡次回流,变成了只触发一次回流。
怎么解决白屏问题
1、加 loading
2、骨架屏
代码输入后果
async function async1 () {console.log('async1 start');
await new Promise(resolve => {console.log('promise1')
resolve('promise1 resolve')
}).then(res => console.log(res))
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
这里是对下面一题进行了革新,加上了 resolve。
输入后果如下:
script start
async1 start
promise1
script end
promise1 resolve
async1 success
async1 end
僵尸过程和孤儿过程是什么?
- 孤儿过程 :父过程退出了,而它的一个或多个过程还在运行,那这些子过程都会成为孤儿过程。孤儿过程将被 init 过程(过程号为 1) 所收养,并由 init 过程对它们实现状态收集工作。
- 僵尸过程:子过程比父过程先完结,而父过程又没有开释子过程占用的资源,那么子过程的过程描述符依然保留在零碎中,这种过程称之为僵死过程。
如何进攻 XSS 攻打?
能够看到 XSS 危害如此之大,那么在开发网站时就要做好进攻措施,具体措施如下:
- 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
- 应用 CSP,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
- CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
- 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
- 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。
什么是文档的预解析?
Webkit 和 Firefox 都做了这个优化,当执行 JavaScript 脚本时,另一个线程解析剩下的文档,并加载前面须要通过网络加载的资源。这种形式能够使资源并行加载从而使整体速度更快。须要留神的是,预解析并不扭转 DOM 树,它将这个工作留给主解析过程,本人只解析内部资源的援用,比方内部脚本、样式表及图片。
代码输入后果
const promise = Promise.resolve().then(() => {return promise;})
promise.catch(console.err)
输入后果如下:
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
这里其实是一个坑,.then
或 .catch
返回的值不能是 promise 自身,否则会造成死循环。
forEach 和 map 办法有什么区别
这办法都是用来遍历数组的,两者区别如下:
- forEach()办法会针对每一个元素执行提供的函数,对数据的操作会扭转原数组,该办法没有返回值;
- map()办法不会扭转原数组的值,返回一个新数组,新数组中的值为原数组调用函数解决之后的值;