关于前端:前端高频面试题附答案

39次阅读

共计 7845 个字符,预计需要花费 20 分钟才能阅读完成。

[]()

说一下 HTTP 3.0

HTTP/ 3 基于 UDP 协定实现了相似于 TCP 的多路复用数据流、传输可靠性等性能,这套性能被称为 QUIC 协定。

  1. 流量管制、传输可靠性性能:QUIC 在 UDP 的根底上减少了一层来保障数据传输可靠性,它提供了数据包重传、拥塞管制、以及其余一些 TCP 中的个性。
  2. 集成 TLS 加密性能:目前 QUIC 应用 TLS1.3,缩小了握手所破费的 RTT 数。
  3. 多路复用:同一物理连贯上能够有多个独立的逻辑数据流,实现了数据流的独自传输,解决了 TCP 的队头阻塞问题。
  4. 疾速握手:因为基于 UDP,能够实现应用 0 ~ 1 个 RTT 来建设连贯。

箭头函数与一般函数的区别

(1)箭头函数比一般函数更加简洁

  • 如果没有参数,就间接写一个空括号即可
  • 如果只有一个参数,能够省去参数的括号
  • 如果有多个参数,用逗号宰割
  • 如果函数体的返回值只有一句,能够省略大括号
  • 如果函数体不须要返回值,且只有一句话,能够给这个语句后面加一个 void 关键字。最常见的就是调用一个函数:
let fn = () => void doesNotReturn();
复制代码

(2)箭头函数没有本人的 this

箭头函数不会创立本人的 this,所以它没有本人的 this,它只会在本人作用域的上一层继承 this。所以箭头函数中 this 的指向在它在定义时曾经确定了,之后不会扭转。

(3)箭头函数继承来的 this 指向永远不会扭转

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){console.log(this.id);
  },
  b: () => {console.log(this.id);
  }
};
obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor
复制代码

对象 obj 的办法 b 是应用箭头函数定义的,这个函数中的 this 就永远指向它定义时所处的全局执行环境中的 this,即使这个函数是作为对象 obj 的办法调用,this 仍旧指向 Window 对象。须要留神,定义对象的大括号 {} 是无奈造成一个独自的执行环境的,它仍旧是处于全局执行环境中。

(4)call()、apply()、bind()等办法不能扭转箭头函数中 this 的指向

var id = 'Global';
let fun1 = () => {console.log(this.id)
};
fun1();                     // 'Global'
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'
复制代码

(5)箭头函数不能作为构造函数应用

构造函数在 new 的步骤在下面曾经说过了,实际上第二步就是将函数中的 this 指向该对象。然而因为箭头函数时没有本人的 this 的,且 this 指向外层的执行环境,且不能扭转指向,所以不能当做构造函数应用。

(6)箭头函数没有本人的 arguments

箭头函数没有本人的 arguments 对象。在箭头函数中拜访 arguments 实际上取得的是它外层函数的 arguments 值。

(7)箭头函数没有 prototype

(8)箭头函数不能用作 Generator 函数,不能应用 yeild 关键字

Loader 和 Plugin 有什么区别

Loader:直译为 ” 加载器 ”。Webpack 将所有文件视为模块,然而 webpack 原生是只能解析 js 文件,如果想将其余文件也打包的话,就会用到loader。所以 Loader 的作用是让 webpack 领有了加载和解析非 JavaScript 文件的能力。Plugin:直译为 ” 插件 ”。Plugin 能够扩大 webpack 的性能,让 webpack 具备更多的灵活性。在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。

setTimeout、Promise、Async/Await 的区别

(1)setTimeout

console.log('script start')    //1. 打印 script start
setTimeout(function(){console.log('settimeout')    // 4. 打印 settimeout
})    // 2. 调用 setTimeout 函数,并定义其实现后执行的回调函数
console.log('script end')    //3. 打印 script start
// 输入程序:script start->script end->settimeout
复制代码

(2)Promise

Promise 自身是 同步的立刻执行函数,当在 executor 中执行 resolve 或者 reject 的时候, 此时是异步操作,会先执行 then/catch 等,当主栈实现后,才会去调用 resolve/reject 中寄存的办法执行,打印 p 的时候,是打印的返回后果,一个 Promise 实例。

console.log('script start')
let promise1 = new Promise(function (resolve) {console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {console.log('promise2')
})
setTimeout(function(){console.log('settimeout')
})
console.log('script end')
// 输入程序: script start->promise1->promise1 end->script end->promise2->settimeout
复制代码

当 JS 主线程执行到 Promise 对象时:

  • promise1.then() 的回调就是一个 task
  • promise1 是 resolved 或 rejected: 那这个 task 就会放入以后事件循环回合的 microtask queue
  • promise1 是 pending: 这个 task 就会放入 事件循环的将来的某个 (可能下一个) 回合的 microtask queue 中
  • setTimeout 的回调也是个 task,它会被放入 macrotask queue 即便是 0ms 的状况

(3)async/await

async function async1(){console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2(){console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输入程序:script start->async1 start->async2->script end->async1 end
复制代码

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作实现,再执行函数体内前面的语句。能够了解为,是让出了线程,跳出了 async 函数体。

例如:

async function func1() {return 1}
console.log(func1())
复制代码

func1 的运行后果其实就是一个 Promise 对象。因而也能够应用 then 来解决后续逻辑。

func1().then(res => {console.log(res);  // 30
})
复制代码

await 的含意为期待,也就是 async 函数须要期待 await 后的函数执行实现并且有了返回后果(Promise 对象)之后,能力继续执行上面的代码。await 通过返回一个 Promise 对象来实现同步的成果。

浏览器是如何对 HTML5 的离线贮存资源进行治理和加载?

  • 在线的状况下,浏览器发现 html 头部有 manifest 属性,它会申请 manifest 文件,如果是第一次拜访页面,那么浏览器就会依据 manifest 文件的内容下载相应的资源并且进行离线存储。如果曾经拜访过页面并且资源曾经进行离线存储了,那么浏览器就会应用离线的资源加载页面,而后浏览器会比照新的 manifest 文件与旧的 manifest 文件,如果文件没有产生扭转,就不做任何操作,如果文件扭转了,就会从新下载文件中的资源并进行离线存储。
  • 离线的状况下,浏览器会间接应用离线存储的资源。

vuex

vuex 是一个专为 vue.js 利用程序开发的状态管理器,它采纳集中式存储管理利用的所有组件的状态,并且以相
应的规定保障状态以一种能够预测的形式发生变化。state: vuex 应用繁多状态树,用一个对象就蕴含来全副的利用层级状态

mutation: 更改 vuex 中 state 的状态的惟一办法就是提交 mutation

action: action 提交的是 mutation,而不是间接变更状态,action 能够蕴含任意异步操作

getter: 相当于 vue 中的 computed 计算属性
复制代码

代码输入后果

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}
复制代码

代码的执行过程如下:

  1. 首先会进入 Promise,打印出 3,之后进入上面的 Promise,打印出 7;
  2. 遇到了定时器,将其退出宏工作队列;
  3. 执行 Promise  p 中的 resolve,状态变为 resolved,返回值为 1;
  4. 执行 Promise first 中的 resolve,状态变为 resolved,返回值为 2;
  5. 遇到 p.then,将其退出微工作队列,遇到 first().then,将其退出工作队列;
  6. 执行里面的代码,打印出 4;
  7. 这样第一轮宏工作就执行完了,开始执行微工作队列中的工作,先后打印出 1 和 2;
  8. 这样微工作就执行完了,开始执行下一轮宏工作,宏工作队列中有一个定时器,执行它,打印出 5,因为执行曾经变为 resolved 状态,所以 resolve(6) 不会再执行;
  9. 最初 console.log(p) 打印出Promise{<resolved>: 1}

setTimeout 模仿 setInterval

形容 :应用setTimeout 模仿实现 setInterval 的性能。

实现

const mySetInterval(fn, time) {
    let timer = null;
    const interval = () => {timer = setTimeout(() => {fn();  // time 工夫之后会执行真正的函数 fn
            interval();  // 同时再次调用 interval 自身}, time)
    }
    interval();  // 开始执行
    // 返回用于敞开定时器的函数
    return () => clearTimeout(timer);
}

// 测试
const cancel = mySetInterval(() => console.log(1), 400);
setTimeout(() => {cancel();
}, 1000);  
// 打印两次 1
复制代码

函数中的 arguments 是数组吗?类数组转数组的办法理解一下?

是类数组,是属于鸭子类型的领域,长得像数组,

  • … 运算符
  • Array.from
  • Array.prototype.slice.apply(arguments)

实现一个扇形

用 CSS 实现扇形的思路和三角形基本一致,就是多了一个圆角的款式,实现一个 90°的扇形:

div{
    border: 100px solid transparent;
    width: 0;
    heigt: 0;
    border-radius: 100px;
    border-top-color: red;
}
复制代码

如果一个构造函数,bind 了一个对象,用这个构造函数创立出的实例会继承这个对象的属性吗?为什么?

不会继承,因为依据 this 绑定四大规定,new 绑定的优先级高于 bind 显示绑定,通过 new 进行结构函数调用时,会创立一个新对象,这个新对象会代替 bind 的对象绑定,作为此函数的 this,并且在此函数没有返回对象的状况下,返回这个新建的对象

什么是尾调用,应用尾调用有什么益处?

尾调用指的是函数的最初一步调用另一个函数。代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留以后的执行上下文,而后再新建另外一个执行上下文退出栈中。应用尾调用的话,因为曾经是函数的最初一步,所以这时能够不用再保留以后的执行上下文,从而节俭了内存,这就是尾调用优化。然而 ES6 的尾调用优化只在严格模式下开启,失常模式是有效的。

CSS 预处理器 / 后处理器是什么?为什么要应用它们?

预处理器, 如:lesssassstylus,用来预编译 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 代码,能够利用到老我的项目中

new 一个构造函数,如果函数返回 return {}return nullreturn 1return true 会产生什么状况?

如果函数返回一个对象,那么 new 这个函数调用返回这个函数的返回对象,否则返回 new 创立的新对象

什么是 margin 重叠问题?如何解决?

问题形容: 两个块级元素的上外边距和下外边距可能会合并(折叠)为一个外边距,其大小会取其中外边距值大的那个,这种行为就是外边距折叠。须要留神的是,浮动的元素和相对定位 这种脱离文档流的元素的外边距不会折叠。重叠只会呈现在 垂直方向

计算准则: 折叠合并后外边距的计算准则如下:

  • 如果两者都是负数,那么就去最大者
  • 如果是一正一负,就会正值减去负值的绝对值
  • 两个都是负值时,用 0 减去两个中绝对值大的那个

解决办法: 对于折叠的状况,次要有两种:兄弟之间重叠 父子之间重叠(1)兄弟之间重叠

  • 底部元素变为行内盒子:display: inline-block
  • 底部元素设置浮动:float
  • 底部元素的 position 的值为absolute/fixed

(2)父子之间重叠

  • 父元素退出:overflow: hidden
  • 父元素增加通明边框:border:1px solid transparent
  • 子元素变为行内盒子:display: inline-block
  • 子元素退出浮动属性或定位

CSS 选择器及其优先级

选择器 格局 优先级权重
id 选择器 #id 100
类选择器 #classname 10
属性选择器 a[ref=“eee”] 10
伪类选择器 li:last-child 10
标签选择器 div 1
伪元素选择器 li:after 1
相邻兄弟选择器 h1+p 0
子选择器 ul>li 0
后辈选择器 li a 0
通配符选择器 * 0

对于选择器的 优先级

  • 标签选择器、伪元素选择器:1
  • 类选择器、伪类选择器、属性选择器:10
  • id 选择器:100
  • 内联款式:1000

注意事项:

  • !important 申明的款式的优先级最高;
  • 如果优先级雷同,则最初呈现的款式失效;
  • 继承失去的款式的优先级最低;
  • 通用选择器(*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以它们的权值都为 0;
  • 样式表的起源不同时,优先级程序为:内联款式 > 外部款式 > 内部款式 > 浏览器用户自定义款式 > 浏览器默认款式。

Proxy 能够实现什么性能?

在 Vue3.0 中通过 Proxy 来替换本来的 Object.defineProperty 来实现数据响应式。

Proxy 是 ES6 中新增的性能,它能够用来自定义对象中的操作。

let p = new Proxy(target, handler)
复制代码

target 代表须要增加代理的对象,handler 用来自定义对象中的操作,比方能够用来自定义 set 或者 get 函数。

上面来通过 Proxy 来实现一个数据响应式:

let onWatch = (obj, setBind, getLogger) => {
  let handler = {get(target, property, receiver) {getLogger(target, property)
      return Reflect.get(target, property, receiver)
    },
    set(target, property, value, receiver) {setBind(value, property)
      return Reflect.set(target, property, value)
    }
  }
  return new Proxy(obj, handler)
}
let obj = {a: 1}
let p = onWatch(
  obj,
  (v, property) => {console.log(` 监听到属性 ${property}扭转为 ${v}`)
  },
  (target, property) => {console.log(`'${property}' = ${target[property]}`)
  }
)
p.a = 2 // 监听到属性 a 扭转
p.a // 'a' = 2
复制代码

在上述代码中,通过自定义 setget 函数的形式,在本来的逻辑中插入了咱们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。

当然这是简略版的响应式实现,如果须要实现一个 Vue 中的响应式,须要在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要应用 Proxy 替换本来的 API 起因在于 Proxy 无需一层层递归为每个属性增加代理,一次即可实现以上操作,性能上更好,并且本来的实现有一些数据更新不能监听到,然而 Proxy 能够完满监听到任何形式的数据扭转,惟一缺点就是浏览器的兼容性不好。

说下对 JS 的理解吧

是基于原型的动静语言,次要独特个性有 this、原型和原型链。

JS 严格意义上来说分为:语言规范局部(ECMAScript)+ 宿主环境局部

语言规范局部

2015 年公布 ES6,引入诸多新个性使得可能编写大型项目变成可能,规范自 2015 之后以年号代号,每年一更

宿主环境局部

  • 在浏览器宿主环境包含 DOM + BOM 等
  • 在 Node,宿主环境包含一些文件、数据库、网络、与操作系统的交互等

正文完
 0