每日十题 金三银四面试题(七)
一、Proxy 和 Object.defineProperty 有哪些区别?
参考解答:
API: Proxy 劫持整个对象,包括对象属性的增删改查。Object.defineProperty 劫持的对象的某一个属性的访问和修改,无法监听新增和删除。
兼容性:Object.defineProperty支持IE9+和主流浏览器。Proxy支持主流浏览器不支持IE。
性能:Proxy 比 Object.defineProperty 要慢,但vue3的vue2响应式性能要好,好在初始化阶段。vue2是递归响应式,vue3是延时响应式
参考:https://mp.weixin.qq.com/s/HjHv1i59Lczn88VNXWsqDQ
二、实现一个冒泡排序算法
参考解答:
时间复杂度O(n^2), 空间复杂度O(1)
function bubbleSort (arr) {
if (!Array.isArray(arr) || arr.length < 2) return;
let lastIndex = arr.length - 1;
while (lastIndex > 0) {
let flag = true, k = lastIndex;
for (let j = 0; j < k; j++) {
if (arr[j] > arr[j + 1]) {
flag = false;
lastIndex = j;
// 数组结构, 交换元素
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
}
三、css 实现一个三角形
参考解答:
利用了元素边框连接处的等分原理
.triggle {
width: 0;
height: 0;
border-width: 100px;
border-style: solid;
// 对应 上 右 下 左
border-color: tomato transparent transparent transparent;
}
四、css三栏布局的实现(左中右,中间自适应)
参考解答:
// 绝对定位
.outer {
position: relative;
height: 100px;
}
.left {
position: absolute;
width: 100px;
height: 100px;
// 添加个颜色显示
background: tomato;
}
.right {
position: absolute;
top: 0;
right: 0;
width: 200px;
height: 100px;
// 添加个颜色显示
background: gold;
}
.center {
margin-left: 100px;
margin-right: 200px;
height: 100px;
background: lightgreen;
}
// flex 布局
.outer {
display: flex;
height: 100px;
}
.left {
flex: 0 0 100px;
// 添加个颜色显示
background: tomato;
}
.right {
flex: 0 0 200px;
// 添加个颜色显示
background: gold;
}
.center {
flex: auto;
background: lightgreen;
}
// 浮动
.outer {
height: 100px;
}
.left {
float: left;
width: 100px;
height: 100px;
// 添加个颜色显示
background: tomato;
}
.right {
float: right;
width: 200px;
height: 100px;
// 添加个颜色显示
background: gold;
}
.center {
margin-left: 100px;
margin-right: 200px;
height: 100px;
background: lightgreen;
}
// 圣杯布局
.outer {
padding-left: 100px;
padding-right: 200px;
height: 100px;
}
.left {
position: relative;
left: -100px;
float: left;
margin-left: -100%;
width: 100px;
height: 100px;
// 添加个颜色显示
background: tomato;
}
.right {
position: relative;
left: 200px;
float: right;
margin-left: -200px;
width: 200px;
height: 100px;
// 添加个颜色显示
background: gold;
}
.center {
float: left;
width: 100%;
height: 100px;
background: lightgreen;
}
// 双飞翼布局
.outer {
height: 100px;
}
.left {
float: left;
margin-left: -100%;
width: 100px;
height: 100px;
// 添加个颜色显示
background: tomato;
}
.right {
float: left;
margin-left: -200px;
width: 200px;
height: 100px;
// 添加个颜色显示
background: gold;
}
.wrapper {
float: left;
width: 100%;
height: 100px;
background: lightgreen;
// sass嵌套体现结构
.center {
margin-left: 100px;
margin-right: 200px;
height: 100px;
}
}
五、说一下JavaScript原型,原型链。
参考解答:
原型:当我们使用new 关键字实例化一个类生成一个对象,这个对象内部有个指针指向构造函数的 prototype 属性对应的值,这个指针被称为原型。
原型链:当我们访问一个对象的属性时,如果对象内部不存在这个属性,那么就会去它的原型对象上找这个属性,该原型对象又会有自己的原型,这种寻找的方式,看起来就像是链子一样,这便是原型链。
对象obj原型的获取方法有:obj.__proto__ 、obj.constructor.prototype、Object.getPrototypeOf(obj)
六、什么是Promise对象?什么是Promises/A+规范?
参考解答:
Promise对象是异步编程的一种解决方案,最早由社区提出。Promises/A+规范是JavaScript Promise的标准,规定了一个Promise所必须具有的特性。
Promise是一个构造函数,接收一个函数作为参数,返回一个Promise实例。一个Promise实例有三种状态,分别是pending、resolved和rejected,分别代表进行中、已成功和已失败。
Promise是立即执行函数,创建既执行。
状态改变是单向的,实例的状态只能由pending转变为resolved 或者 rejected 状态,一经改变,状态就永久变化。
状态是通过resolve() 和 reject() 函数来实现的。
在Promise对象的原型上有个then方法,该方法会为Promise对象的注册回调函数。这个回调函数属于微任务,会在本轮事件循环的末尾执行。
七、浏览器的渲染原理
参考解答:
首先解析收到的文档,根据文档定义构建一棵DOM树,DOM树是由DOM元素及属性节点组成的。
然后对css进行解析,生成CSSOM规则树。
根据DOM树和CSSOM规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含颜色和大小等各类属性的矩形,渲染对象和DOM元素相对应,但这种对应关系不是一对一的,不可见的DOM元素不会被插入渲染树。还有一些DOM元素对应几个可见对象,他们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
当渲染对象被创建并添加到渲染树中,他们并没有位置和大小,所以当浏览器生成渲染树后,就会根据渲染树来进行布局(回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小(自动重排)。
布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用UI基础组件。
八、说一下JavaScript的EventLoop事件循环
参考解答:
JavaScript是单线程运行的。在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
在执行同步代码的时候,如果遇到异步事件,js引擎并不会一直等待其返回结果,而是将这个事件挂起,继续执行执行栈中的其他任务。
当异步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行。
任务队列可以分为宏任务队列和微任务队列,当前执行栈中的事件执行完毕后,js引擎首先会判断微任务队列中是否有任务可以执行,如果有就将微任务队首的事件压入执行栈中执行。当微任务队列中的任务都执行完成后再去判断宏任务队列中的任务。
微任务:promise的回调、node的process.nextTick、对DOM变化监听的MutationObserver。
宏任务:script脚本执行、setTimeout、setInterval、setImmediate、I/O操作、UI渲染等。
九、EventEmitter 实现
参考解答:
class EventEmitter {
constructor () {
this.events = {};
}
on (event, callback) {
let callbacks = this.events[event] || [];
callbacks.push(callback);
this.events[event] = callbacks;
return this;
}
off (event, callback) {
let callbacks = this.events[event];
this.events[event] = callbacks && callbacks.filter(fn => fn !== callback);
return this;
}
emit (event, ...args) {
let callbacks = this.events[event];
callbacks.forEach(fn => fn(...args))
return this;
}
once (event, callback) {
let wrapFun = function (...args) {
callback(...args);
this.off(event, wrapFun)
}
this.on(event, wrapFun);
return this;
}
}
十、Service Worker 是什么?
参考解答:
Service workers
本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理。它们旨在(除其他之外)使得能够创建有效的离线体验,拦截网络请求并基于网络是否可用以及更新的资源是否驻留在服务器上来采取适当的动作。他们还允许访问推送通知和后台同步API。
目前该技术通常用来做缓存文件,提高首屏速度。