代码输入问题
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。
一个 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 申请数也是能够没有下限地继续发送
代码输入后果
async function async1() {console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {console.log("async2");
}
async1();
console.log('start')
输入后果如下:
async1 start
async2
start
async1 end
代码的执行过程如下:
- 首先执行函数中的同步代码
async1 start
,之后遇到了await
,它会阻塞async1
前面代码的执行,因而会先去执行async2
中的同步代码async2
,而后跳出async1
; - 跳出
async1
函数后,执行同步代码start
; - 在一轮宏工作全副执行完之后,再来执行
await
前面的内容async1 end
。
这里能够了解为 await 前面的语句相当于放到了 new Promise 中,下一行及之后的语句相当于放在 Promise.then 中。
冒泡排序 – 工夫复杂度 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]));
参考:前端进阶面试题具体解答
内存泄露
- 意外的全局变量: 无奈被回收
- 定时器: 未被正确敞开,导致所援用的内部变量无奈被开释
- 事件监听: 没有正确销毁 (低版本浏览器可能呈现)
-
闭包
- 第一种状况是咱们因为应用未声明的变量,而意外的创立了一个全局变量,而使这个变量始终留在内存中无奈被回收。
- 第二种状况是咱们设置了 setInterval 定时器,而遗记勾销它,如果循环函数有对外部变量的援用的话,那么这个变量会被始终留在内存中,而无奈被回收。
- 第三种状况是咱们获取一个 DOM 元素的援用,而前面这个元素被删除,因为咱们始终保留了对这个元素的援用,所以它也无奈被回收。
- 第四种状况是不合理的应用闭包,从而导致某些变量始终被留在内存当中。
dom
援用:dom
元素被删除时,内存中的援用未被正确清空- 控制台
console.log
打印的货色
可用
chrome
中的timeline
进行内存标记,可视化查看内存的变动状况,找出异样点。
内存泄露排查办法(opens new window)
计算属性和 watch 有什么区别? 以及它们的使用场景?
// 区别
computed 计算属性:依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值。watch 侦听器:更多的是察看的作用, 无缓存性, 相似与某些数据的监听回调, 每当监听的数据变动时都会执行回调进行后续操作
// 使用场景
当须要进行数值计算, 并且依赖与其它数据时, 应该应用 computed, 因为能够利用 computed 的缓存属性, 防止每次获取值时都要从新计算。当须要在数据变动时执行异步或开销较大的操作时, 应该应用 watch, 应用 watch 选项容许执行异步操作(拜访一个 API), 限度执行该操作的频率,并在失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。
浅拷贝
// 这里只思考对象类型
function shallowClone(obj) {if(!isObject(obj)) return obj;
let newObj = Array.isArray(obj) ? [] : {};
// for...in 只会遍历对象本身的和继承的可枚举的属性(不含 Symbol 属性)for(let key in obj) {// obj.hasOwnProperty() 办法只思考对象本身的属性
if(obj.hasOwnProperty(key)) {newObj[key] = obj[key];
}
}
return newObj;
}
抉择排序 – 工夫复杂度 n^2
题目形容: 实现一个抉择排序
实现代码如下:
function selectSort(arr) {
// 缓存数组长度
const len = arr.length;
// 定义 minIndex,缓存以后区间最小值的索引,留神是索引
let minIndex;
// i 是以后排序区间的终点
for (let i = 0; i < len - 1; i++) {
// 初始化 minIndex 为以后区间第一个元素
minIndex = i;
// i、j 别离定义以后区间的上下界,i 是左边界,j 是右边界
for (let j = i; j < len; j++) {
// 若 j 处的数据项比以后最小值还要小,则更新最小值索引为 j
if (arr[j] < arr[minIndex]) {minIndex = j;}
}
// 如果 minIndex 对应元素不是目前的头部元素,则替换两者
if (minIndex !== i) {[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
}
return arr;
}
// console.log(quickSort([3, 6, 2, 4, 1]));
Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题?你能说说如下代码的实现原理么?
1)Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题
- Vue 应用了 Object.defineProperty 实现双向数据绑定
- 在初始化实例时对属性执行 getter/setter 转化
- 属性必须在 data 对象上存在能力让 Vue 将它转换为响应式的(这也就造成了 Vue 无奈检测到对象属性的增加或删除)
所以 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
2)接下来咱们看看框架自身是如何实现的呢?
Vue 源码地位:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
// target 为数组
if (Array.isArray(target) && isValidArrayIndex(key)) {// 批改数组的长度, 防止索引 > 数组长度导致 splcie()执行有误
target.length = Math.max(target.length, key)
// 利用数组的 splice 变异办法触发响应式
target.splice(key, 1, val)
return val
}
// key 曾经存在,间接批改属性值
if (key in target && !(key in Object.prototype)) {target[key] = val
return val
}
const ob = (target: any).__ob__
// target 自身就不是响应式数据, 间接赋值
if (!ob) {target[key] = val
return val
}
// 对属性进行响应式解决
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
咱们浏览以上源码可知,vm.$set 的实现原理是:
- 如果指标是数组,间接应用数组的 splice 办法触发相应式;
- 如果指标是对象,会先判读属性是否存在、对象是否是响应式,
- 最终如果要对属性进行响应式解决,则是通过调用 defineReactive 办法进行响应式解决
defineReactive 办法就是 Vue 在初始化对象时,给对象属性采纳 Object.defineProperty 动静增加 getter 和 setter 的性能所调用的办法
节流
节流(throttle
):触发高频事件,且 N 秒内只执行一次。这就好比公交车,10 分钟一趟,10 分钟内有多少人在公交站等我不论,10 分钟一到我就要发车走人!相似 qq 飞车的复位按钮。
核心思想 :应用 工夫戳或标记 来实现,立刻执行一次,而后每 N 秒执行一次。如果 N 秒内触发则间接返回。
利用:节流常利用于鼠标一直点击触发、监听滚动事件。
实现:
// 版本一:标记实现
function throttle(fn, wait){
let flag = true; // 设置一个标记
return function(...args){if(!flag) return;
flag = false;
setTimeout(() => {fn.call(this, ...args);
flag = true;
}, wait);
}
}
// 版本二:工夫戳实现
function throttle(fn, wait) {
let pre = 0;
return function(...args) {let now = new Date();
if(now - pre < wait) return;
pre = now;
fn.call(this, ...args);
}
}
代码输入后果
const first = () => (new Promise((resolve, reject) => {console.log(3);
let p = new Promise((resolve, reject) => {console.log(7);
setTimeout(() => {console.log(5);
resolve(6);
console.log(p)
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {console.log(arg);
});
}));
first().then((arg) => {console.log(arg);
});
console.log(4);
输入后果如下:
3
7
4
1
2
5
Promise{<resolved>: 1}
代码的执行过程如下:
- 首先会进入 Promise,打印出 3,之后进入上面的 Promise,打印出 7;
- 遇到了定时器,将其退出宏工作队列;
- 执行 Promise p 中的 resolve,状态变为 resolved,返回值为 1;
- 执行 Promise first 中的 resolve,状态变为 resolved,返回值为 2;
- 遇到 p.then,将其退出微工作队列,遇到 first().then,将其退出工作队列;
- 执行里面的代码,打印出 4;
- 这样第一轮宏工作就执行完了,开始执行微工作队列中的工作,先后打印出 1 和 2;
- 这样微工作就执行完了,开始执行下一轮宏工作,宏工作队列中有一个定时器,执行它,打印出 5,因为执行曾经变为 resolved 状态,所以
resolve(6)
不会再执行; - 最初
console.log(p)
打印出Promise{<resolved>: 1}
;
代码输入后果
console.log('1');
setTimeout(function() {console.log('2');
process.nextTick(function() {console.log('3');
})
new Promise(function(resolve) {console.log('4');
resolve();}).then(function() {console.log('5')
})
})
process.nextTick(function() {console.log('6');
})
new Promise(function(resolve) {console.log('7');
resolve();}).then(function() {console.log('8')
})
setTimeout(function() {console.log('9');
process.nextTick(function() {console.log('10');
})
new Promise(function(resolve) {console.log('11');
resolve();}).then(function() {console.log('12')
})
})
输入后果如下:
1
7
6
8
2
4
3
5
9
11
10
12
(1)第一轮事件循环流程剖析如下:
- 整体 script 作为第一个宏工作进入主线程,遇到
console.log
,输入 1。 - 遇到
setTimeout
,其回调函数被散发到宏工作 Event Queue 中。暂且记为setTimeout1
。 - 遇到
process.nextTick()
,其回调函数被散发到微工作 Event Queue 中。记为process1
。 - 遇到
Promise
,new Promise
间接执行,输入 7。then
被散发到微工作 Event Queue 中。记为then1
。 - 又遇到了
setTimeout
,其回调函数被散发到宏工作 Event Queue 中,记为setTimeout2
。
宏工作 Event Queue | 微工作 Event Queue |
---|---|
setTimeout1 | process1 |
setTimeout2 | then1 |
上表是第一轮事件循环宏工作完结时各 Event Queue 的状况,此时曾经输入了 1 和 7。发现了 process1
和then1
两个微工作:
- 执行
process1
,输入 6。 - 执行
then1
,输入 8。
第一轮事件循环正式完结,这一轮的后果是输入 1,7,6,8。
(2)第二轮工夫循环从 **setTimeout1**
宏工作开始:
- 首先输入 2。接下来遇到了
process.nextTick()
,同样将其散发到微工作 Event Queue 中,记为process2
。 new Promise
立刻执行输入 4,then
也散发到微工作 Event Queue 中,记为then2
。
宏工作 Event Queue | 微工作 Event Queue |
---|---|
setTimeout2 | process2 |
then2 |
第二轮事件循环宏工作完结,发现有 process2
和then2
两个微工作能够执行:
- 输入 3。
- 输入 5。
第二轮事件循环完结,第二轮输入 2,4,3,5。
(3)第三轮事件循环开始,此时只剩 setTimeout2 了,执行。
- 间接输入 9。
- 将
process.nextTick()
散发到微工作 Event Queue 中。记为process3
。 - 间接执行
new Promise
,输入 11。 - 将
then
散发到微工作 Event Queue 中,记为then3
。
宏工作 Event Queue | 微工作 Event Queue |
---|---|
process3 | |
then3 |
第三轮事件循环宏工作执行完结,执行两个微工作 process3
和then3
:
- 输入 10。
- 输入 12。
第三轮事件循环完结,第三轮输入 9,11,10,12。
整段代码,共进行了三次事件循环,残缺的输入为 1,7,6,8,2,4,3,5,9,11,10,12。
代码输入后果
var length = 10;
function fn() {console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {fn();
arguments[0]();}
};
obj.method(fn, 1);
输入后果:10 2
解析:
- 第一次执行 fn(),this 指向 window 对象,输入 10。
- 第二次执行 arguments[0],相当于 arguments 调用办法,this 指向 arguments,而这里传了两个参数,故输入 arguments 长度为 2。
代码输入后果
const p1 = new Promise((resolve) => {setTimeout(() => {resolve('resolve3');
console.log('timer1')
}, 0)
resolve('resovle1');
resolve('resolve2');
}).then(res => {console.log(res) // resolve1
setTimeout(() => {console.log(p1)
}, 1000)
}).finally(res => {console.log('finally', res)
})
执行后果为如下:
resolve1
finally undefined
timer1
Promise{<resolved>: undefined}
须要留神的是最初一个定时器打印出的 p1 其实是 .finally
的返回值,咱们晓得 .finally
的返回值如果在没有抛出谬误的状况下默认会是上一个 Promise 的返回值,而这道题中 .finally
上一个 Promise 是 .then()
,然而这个.then()
并没有返回值,所以 p1 打印进去的 Promise 的值会是undefined
,如果在定时器的上面加上一个return 1
,则值就会变成 1。
什么状况会阻塞渲染?
首先渲染的前提是生成渲染树,所以 HTML 和 CSS 必定会阻塞渲染。如果你想渲染的越快,你越应该升高一开始须要渲染的文件大小,并且扁平层级,优化选择器。而后当浏览器在解析到 script 标签时,会暂停构建 DOM,实现后才会从暂停的中央从新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都倡议将 script 标签放在 body 标签底部的起因。
当然在当下,并不是说 script 标签必须放在底部,因为你能够给 script 标签增加 defer 或者 async 属性。当 script 标签加上 defer 属性当前,示意该 JS 文件会并行下载,然而会放到 HTML 解析实现后程序执行,所以对于这种状况你能够把 script 标签放在任意地位。对于没有任何依赖的 JS 文件能够加上 async 属性,示意 JS 文件下载和解析不会阻塞渲染。
Vue 路由守卫有哪些,怎么设置,应用场景等
罕用的两个路由守卫:router.beforeEach 和 router.afterEach
每个守卫办法接管三个参数:to: Route: 行将要进入的指标 路由对象
from: Route: 以后导航正要来到的路由
next: Function: 肯定要调用该办法来 resolve 这个钩子。在我的项目中,个别在 beforeEach 这个钩子函数中进行路由跳转的一些信息判断。判断是否登录,是否拿到对应的路由权限等等。
instanceof
作用:判断对象的具体类型。能够区别 array
和 object
,null
和 object
等。
语法:A instanceof B
如何判断的?: 如果 B 函数的显式原型对象在 A 对象的原型链上,返回true
,否则返回false
。
留神:如果检测原始值,则始终返回 false
。
实现:
function myinstanceof(left, right) {
// 根本数据类型都返回 false,留神 typeof 函数 返回 "function"
if((typeof left !== "object" && typeof left !== "function") || left === null) return false;
let leftPro = left.__proto__; // 取右边的(隐式)原型 __proto__
// left.__proto__ 等价于 Object.getPrototypeOf(left)
while(true) {
// 判断是否到原型链顶端
if(leftPro === null) return false;
// 判断左边的显式原型 prototype 对象是否在右边的原型链上
if(leftPro === right.prototype) return true;
// 原型链查找
leftPro = leftPro.__proto__;
}
}
代码输入后果
Promise.resolve('1')
.then(res => {console.log(res)
})
.finally(() => {console.log('finally')
})
Promise.resolve('2')
.finally(() => {console.log('finally2')
return '我是 finally2 返回的值'
})
.then(res => {console.log('finally2 前面的 then 函数', res)
})
输入后果如下:
1
finally2
finally
finally2 前面的 then 函数 2
.finally()
个别用的很少,只有记住以下几点就能够了:
.finally()
办法不论 Promise 对象最初的状态如何都会执行.finally()
办法的回调函数不承受任何的参数,也就是说你在.finally()
函数中是无奈晓得 Promise 最终的状态是resolved
还是rejected
的- 它最终返回的默认会是一个上一次的 Promise 对象值,不过如果抛出的是一个异样则返回异样的 Promise 对象。
- finally 实质上是 then 办法的特例
.finally()
的谬误捕捉:
Promise.resolve('1')
.finally(() => {console.log('finally1')
throw new Error('我是 finally 中抛出的异样')
})
.then(res => {console.log('finally 前面的 then 函数', res)
})
.catch(err => {console.log('捕捉谬误', err)
})
输入后果为:
'finally1'
'捕捉谬误' Error: 我是 finally 中抛出的异样
说一下 vue3.0 你理解多少?
<!-- 响应式原理的扭转 Vue3.x 应用 Proxy 取代 Vue2.x 版本的 Object.defineProperty -->
<!-- 组件选项申明形式 Vue3.x 应用 Composition API setup 是 Vue3.x 新增的一个选项,他
是组件内应用 Composition API 的入口 -->
<!-- 模板语法变动 slot 具名插槽语法 自定义指令 v-model 降级 -->
<!-- 其它方面的更改 Suspense 反对 Fragment(多个根节点) 和 Protal (在 dom 其余局部渲染组建内容)组件
针对一些非凡的场景做了解决。基于 treeshaking 优化,提供了更多的内置性能。-->
常见的图片格式及应用场景
(1)BMP,是无损的、既反对索引色也反对间接色的点阵图。这种图片格式简直没有对数据进行压缩,所以 BMP 格局的图片通常是较大的文件。
(2)GIF是无损的、采纳索引色的点阵图。采纳 LZW 压缩算法进行编码。文件小,是 GIF 格局的长处,同时,GIF 格局还具备反对动画以及通明的长处。然而 GIF 格局仅反对 8bit 的索引色,所以 GIF 格局实用于对色调要求不高同时须要文件体积较小的场景。
(3)JPEG是有损的、采纳间接色的点阵图。JPEG 的图片的长处是采纳了间接色,得益于更丰盛的色调,JPEG 非常适合用来存储照片,与 GIF 相比,JPEG 不适宜用来存储企业 Logo、线框类的图。因为有损压缩会导致图片含糊,而间接色的选用,又会导致图片文件较 GIF 更大。
(4)PNG-8是无损的、应用索引色的点阵图。PNG 是一种比拟新的图片格式,PNG- 8 是十分好的 GIF 格局替代者,在可能的状况下,应该尽可能的应用 PNG- 8 而不是 GIF,因为在雷同的图片成果下,PNG- 8 具备更小的文件体积。除此之外,PNG- 8 还反对透明度的调节,而 GIF 并不反对。除非须要动画的反对,否则没有理由应用 GIF 而不是 PNG-8。
(5)PNG-24是无损的、应用间接色的点阵图。PNG-24 的长处在于它压缩了图片的数据,使得同样成果的图片,PNG-24 格局的文件大小要比 BMP 小得多。当然,PNG24 的图片还是要比 JPEG、GIF、PNG- 8 大得多。
(6)SVG是无损的矢量图。SVG 是矢量图意味着 SVG 图片由直线和曲线以及绘制它们的办法组成。当放大 SVG 图片时,看到的还是线和曲线,而不会呈现像素点。这意味着 SVG 图片在放大时,不会失真,所以它非常适合用来绘制 Logo、Icon 等。
(7)WebP是谷歌开发的一种新图片格式,WebP 是同时反对有损和无损压缩的、应用间接色的点阵图。从名字就可以看进去它是为 Web 而生的,什么叫为 Web 而生呢?就是说雷同品质的图片,WebP 具备更小的文件体积。当初网站上充斥了大量的图片,如果可能升高每一个图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,进而升高拜访提早,晋升拜访体验。目前只有 Chrome 浏览器和 Opera 浏览器反对 WebP 格局,兼容性不太好。
- 在无损压缩的状况下,雷同品质的 WebP 图片,文件大小要比 PNG 小 26%;
- 在有损压缩的状况下,具备雷同图片精度的 WebP 图片,文件大小要比 JPEG 小 25%~34%;
- WebP 图片格式反对图片透明度,一个无损压缩的 WebP 图片,如果要反对透明度只须要 22% 的分外文件大小。