乐趣区

关于前端:校招前端二面面试题合集

iframe 有那些长处和毛病?

iframe 元素会创立蕴含另外一个文档的内联框架(即行内框架)。

长处:

  • 用来加载速度较慢的内容(如广告)
  • 能够使脚本能够并行下载
  • 能够实现跨子域通信

毛病:

  • iframe 会阻塞主页面的 onload 事件
  • 无奈被一些搜索引擎索辨认
  • 会产生很多页面,不容易治理

compose

题目形容: 实现一个 compose 函数

// 用法如下:
function fn1(x) {return x + 1;}
function fn2(x) {return x + 2;}
function fn3(x) {return x + 3;}
function fn4(x) {return x + 4;}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1+4+3+2+1=11

实现代码如下:

function compose(...fn) {if (!fn.length) return (v) => v;
  if (fn.length === 1) return fn[0];
  return fn.reduce((pre, cur) =>
      (...args) =>
        pre(cur(...args))
  );
}

如何防止回流与重绘?

缩小回流与重绘的措施:

  • 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
  • 不要应用 table 布局,一个小的改变可能会使整个 table 进行从新布局
  • 应用 CSS 的表达式
  • 不要频繁操作元素的款式,对于动态页面,能够批改类名,而不是款式。
  • 应用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其余元素
  • 防止频繁操作 DOM,能够创立一个文档片段documentFragment,在它下面利用所有 DOM 操作,最初再把它增加到文档中
  • 将元素先设置display: none,操作完结后再把它显示进去。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
  • 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于 浏览器的渲染队列机制

浏览器针对页面的回流与重绘,进行了本身的优化——渲染队列

浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了肯定的数量或者到了肯定的工夫距离,浏览器就会对队列进行批处理。这样就会让屡次的回流、重绘变成一次回流重绘。

下面,将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,本来应该是触发屡次回流,变成了只触发一次回流。

常见的 HTTP 申请头和响应头

HTTP Request Header 常见的申请头:

  • Accept: 浏览器可能解决的内容类型
  • Accept-Charset: 浏览器可能显示的字符集
  • Accept-Encoding:浏览器可能解决的压缩编码
  • Accept-Language:浏览器以后设置的语言
  • Connection:浏览器与服务器之间连贯的类型
  • Cookie:以后页面设置的任何 Cookie
  • Host:发出请求的页面所在的域
  • Referer:发出请求的页面的 URL
  • User-Agent:浏览器的用户代理字符串

HTTP Responses Header 常见的响应头:

  • Date:示意音讯发送的工夫,工夫的形容格局由 rfc822 定义
  • server: 服务器名称
  • Connection:浏览器与服务器之间连贯的类型
  • Cache-Control:管制 HTTP 缓存
  • content-type: 示意前面的文档属于什么 MIME 类型

常见的 Content-Type 属性值有以下四种:

(1)application/x-www-form-urlencoded:浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 形式提交数据。该种形式提交的数据放在 body 外面,数据依照 key1=val1&key2=val2 的形式进行编码,key 和 val 都进行了 URL 转码。

(2)multipart/form-data:该种形式也是一个常见的 POST 提交形式,通常表单上传文件时应用该种形式。

(3)application/json:服务器音讯主体是序列化后的 JSON 字符串。

(4)text/xml:该种形式次要用来提交 XML 格局的数据。

代码输入后果

f = function() {return true;};   
g = function() {return false;};   
(function() {if (g() && [] == ![]) {f = function f() {return false;};   
      function g() {return true;}   
   }   
})();   
console.log(f());

输入后果:false

这里首先定义了两个变量 f 和 g,咱们晓得变量是能够从新赋值的。前面是一个匿名自执行函数,在 if 条件中调用了函数 g(),因为在匿名函数中,又从新定义了函数 g,就笼罩了内部定义的变量 g,所以,这里调用的是外部函数 g 办法,返回为 true。第一个条件通过,进入第二个条件。

第二个条件是[] == ![],先看 ![],在 JavaScript 中,当用于布尔运算时,比方在这里,对象的非空援用被视为 true,空援用 null 则被视为 false。因为这里不是一个 null, 而是一个没有元素的数组,所以 [] 被视为 true, 而 ![] 的后果就是 false 了。当一个布尔值参加到条件运算的时候,true 会被看作 1, 而 false 会被看作 0。当初条件变成了 [] == 0 的问题了,当一个对象参加条件比拟的时候,它会被求值,求值的后果是数组成为一个字符串,[] 的后果就是 ”,而 ” 会被当作 0,所以,条件成立。

两个条件都成立,所以会执行条件中的代码,f 在定义是没有应用 var,所以他是一个全局变量。因而,这里会通过闭包拜访到内部的变量 f, 从新赋值,当初执行 f 函数返回值曾经成为 false 了。而 g 则不会有这个问题,这里是一个函数内定义的 g,不会影响到内部的 g 函数。所以最初的后果就是 false。

new 一个函数产生了什么

结构调用:

  • 发明一个全新的对象
  • 这个对象会被执行 [[Prototype]] 连贯,将这个新对象的 [[Prototype]] 链接到这个构造函数.prototype 所指向的对象
  • 这个新对象会绑定到函数调用的 this
  • 如果函数没有返回其余对象,那么 new 表达式中的函数调用会主动返回这个新对象

<script src=’xxx’’xxx’/> 内部 js 文件先加载还是 onload 先执行,为什么?

onload 是所以加载实现之后执行的

实现一个三角形

CSS 绘制三角形次要用到的是 border 属性,也就是边框。

平时在给盒子设置边框时,往往都设置很窄,就可能误以为边框是由矩形组成的。实际上,border 属性是右三角形组成的,上面看一个例子:

div {
    width: 0;
    height: 0;
    border: 100px solid;
    border-color: orange blue red green;
}

将元素的长宽都设置为 0

(1)三角 1

div {width: 0;    height: 0;    border-top: 50px solid red;    border-right: 50px solid transparent;    border-left: 50px solid transparent;}

(2)三角 2

div {
    width: 0;
    height: 0;
    border-bottom: 50px solid red;
    border-right: 50px solid transparent;
    border-left: 50px solid transparent;
}

(3)三角 3

div {
    width: 0;
    height: 0;
    border-left: 50px solid red;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
}

(4)三角 4

div {
    width: 0;
    height: 0;
    border-right: 50px solid red;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
}

(5)三角 5

div {
    width: 0;
    height: 0;
    border-top: 100px solid red;
    border-right: 100px solid transparent;
}

还有很多,就不一一实现了,总体的准则就是通过上下左右边框来管制三角形的方向,用边框的宽度比来管制三角形的角度。

类数组转化为数组的办法

题目形容: 类数组领有 length 属性 能够应用下标来拜访元素 然而不能应用数组的办法 如何把类数组转化为数组?

实现代码如下:

const arrayLike=document.querySelectorAll('div')

// 1. 扩大运算符
[...arrayLike]
// 2.Array.from
Array.from(arrayLike)
// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)
// 4.Array.apply
Array.apply(null, arrayLike)
// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)

对 Promise 的了解

Promise 是异步编程的一种解决方案,它是一个对象,能够获取异步操作的音讯,他的呈现大大改善了异步编程的窘境,防止了天堂回调,它比传统的解决方案回调函数和事件更正当和更弱小。

所谓 Promise,简略说就是一个容器,外面保留着某个将来才会完结的事件(通常是一个异步操作)的后果。从语法上说,Promise 是一个对象,从它能够获取异步操作的音讯。Promise 提供对立的 API,各种异步操作都能够用同样的办法进行解决。

(1)Promise 的实例有 三个状态:

  • Pending(进行中)
  • Resolved(已实现)
  • Rejected(已回绝)

当把一件事件交给 promise 时,它的状态就是 Pending,工作实现了状态就变成了 Resolved、没有实现失败了就变成了 Rejected。

(2)Promise 的实例有 两个过程

  • pending -> fulfilled : Resolved(已实现)
  • pending -> rejected:Rejected(已回绝)

留神:一旦从进行状态变成为其余状态就永远不能更改状态了。

Promise 的特点:

  • 对象的状态不受外界影响。promise 对象代表一个异步操作,有三种状态,pending(进行中)、fulfilled(已胜利)、rejected(已失败)。只有异步操作的后果,能够决定以后是哪一种状态,任何其余操作都无奈扭转这个状态,这也是 promise 这个名字的由来——“承诺”;
  • 一旦状态扭转就不会再变,任何时候都能够失去这个后果。promise 对象的状态扭转,只有两种可能:从 pending 变为 fulfilled,从pending 变为rejected。这时就称为resolved(已定型)。如果扭转曾经产生了,你再对 promise 对象增加回调函数,也会立刻失去这个后果。这与事件(event)齐全不同,事件的特点是:如果你错过了它,再去监听是得不到后果的。

Promise 的毛病:

  • 无奈勾销 Promise,一旦新建它就会立刻执行,无奈中途勾销。
  • 如果不设置回调函数,Promise 外部抛出的谬误,不会反馈到内部。
  • 当处于 pending 状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。

总结: Promise 对象是异步编程的一种解决方案,最早由社区提出。Promise 是一个构造函数,接管一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,别离是 pending、resolved 和 rejected,别离代表了进行中、已胜利和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经扭转,就凝固了,无奈再被扭转了。

状态的扭转是通过 resolve() 和 reject() 函数来实现的,能够在异步操作完结后调用这两个函数扭转 Promise 实例的状态,它的原型上定义了一个 then 办法,应用这个 then 办法能够为两个状态的扭转注册回调函数。这个回调函数属于微工作,会在本轮事件循环的开端执行。

留神: 在结构 Promise 的时候,构造函数外部的代码是立刻执行的

对于原型的继承咱们借助寄生组合继承

function Person(obj) {
    this.name = obj.name
    this.age = obj.age
}
Person.prototype.add = function(value){console.log(value)
}
var p1 = new Person({name:"番茄", age: 18})

function Person1(obj) {Person.call(this, obj)
    this.sex = obj.sex
}
// 这一步是继承的要害
Person1.prototype = Object.create(Person.prototype)
Person1.prototype.play = function(value){console.log(value)
}
var p2 = new Person1({name:"鸡蛋", age: 118, sex: "男"})

深拷贝浅拷贝

浅拷贝:浅拷贝通过 ES6 新个性 Object.assign()或者通过扩大运算法... 来达到浅拷贝的目标,浅拷贝批改
正本,不会影响原数据,但毛病是浅拷贝只能拷贝第一层的数据,且都是值类型数据,如果有援用型数据,批改
正本会影响原数据。深拷贝:通过利用 JSON.parse(JSON.stringify())来实现深拷贝的目标,但利用 JSON 拷贝也是有毛病的,当要拷贝的数据中含有 undefined/function/symbol 类型是无奈进行拷贝的,当然咱们想我的项目开发中须要
深拷贝的数据个别不会含有以上三种类型,如有须要能够本人在封装一个函数来实现。

常⽤的 meta 标签有哪些

meta 标签由 namecontent 属性定义,用来形容网页文档的属性 ,比方网页的作者,网页形容,关键词等,除了 HTTP 规范固定了一些name 作为大家应用的共识,开发者还能够自定义 name。

罕用的 meta 标签:
(1)charset,用来形容 HTML 文档的编码类型:

<meta charset="UTF-8" >

(2)keywords,页面关键词:

<meta name="keywords" content="关键词" />

(3)description,页面形容:

<meta name="description" content="页面形容内容" />

(4)refresh,页面重定向和刷新:

<meta http-equiv="refresh" content="0;url=" />

(5)viewport,适配挪动端,能够管制视口的大小和比例:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

其中,content 参数有以下几种:

  • width viewport:宽度(数值 /device-width)
  • height viewport:高度(数值 /device-height)
  • initial-scale:初始缩放比例
  • maximum-scale:最大缩放比例
  • minimum-scale:最小缩放比例
  • user-scalable:是否容许用户缩放(yes/no)

(6)搜索引擎索引形式:

<meta name="robots" content="index,follow" />

其中,content 参数有以下几种:

  • all:文件将被检索,且页面上的链接能够被查问;
  • none:文件将不被检索,且页面上的链接不能够被查问;
  • index:文件将被检索;
  • follow:页面上的链接能够被查问;
  • noindex:文件将不被检索;
  • nofollow:页面上的链接不能够被查问。

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 代码,能够利用到老我的项目中

实现模板字符串解析性能

题目形容:

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄 18,性别 undefined

实现代码如下:

function render(template, data) {let computed = template.replace(/\{\{(\w+)\}\}/g, function (match, key) {return data[key];
  });
  return computed;
}

浏览器渲染优化

(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 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于 浏览器的渲染队列机制

浏览器针对页面的回流与重绘,进行了本身的优化——渲染队列

浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了肯定的数量或者到了肯定的工夫距离,浏览器就会对队列进行批处理。这样就会让屡次的回流、重绘变成一次回流重绘。

将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,本来应该是触发屡次回流,变成了只触发一次回流。

对事件循环的了解

因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保障代码的有序执行。在执行同步代码时,如果遇到异步事件,js 引擎并不会始终期待其返回后果,而是会将这个事件挂起,继续执行执行栈中的其余工作。当异步事件执行结束后,再将异步事件对应的回调退出到一个工作队列中期待执行。工作队列能够分为宏工作队列和微工作队列,当以后执行栈中的事件执行结束后,js 引擎首先会判断微工作队列中是否有工作能够执行,如果有就将微工作队首的事件压入栈中执行。当微工作队列中的工作都执行实现后再去执行宏工作队列中的工作。

Event Loop 执行程序如下所示:

  • 首先执行同步代码,这属于宏工作
  • 当执行完所有同步代码后,执行栈为空,查问是否有异步代码须要执行
  • 执行所有微工作
  • 当执行完所有微工作后,如有必要会渲染页面
  • 而后开始下一轮 Event Loop,执行宏工作中的异步代码

CDN 的原理

CDN 和 DNS 有着密不可分的分割,先来看一下 DNS 的解析域名过程,在浏览器输出的解析过程如下:
(1)查看浏览器缓存
(2)查看操作系统缓存,常见的如 hosts 文件
(3)查看路由器缓存
(4)如果前几步都没没找到,会向 ISP(网络服务提供商) 的 LDNS 服务器查问
(5)如果 LDNS 服务器没找到,会向根域名服务器(Root Server) 申请解析,分为以下几步:

  • 根服务器返回顶级域名 (TLD) 服务器如 .com.cn.org 等的地址,该例子中会返回 .com 的地址
  • 接着向顶级域名服务器发送申请,而后会返回次级域名 (SLD) 服务器的地址,本例子会返回 .test 的地址
  • 接着向次级域名服务器发送申请,而后会返回通过域名查问到的指标 IP,本例子会返回 www.test.com 的地址
  • Local DNS Server 会缓存后果,并返回给用户,缓存在零碎中

CDN 的工作原理:(1)用户未应用 CDN 缓存资源的过程:

  1. 浏览器通过 DNS 对域名进行解析(就是下面的 DNS 解析过程),顺次失去此域名对应的 IP 地址
  2. 浏览器依据失去的 IP 地址,向域名的服务主机发送数据申请
  3. 服务器向浏览器返回响应数据

(2)用户应用 CDN 缓存资源的过程:

  1. 对于点击的数据的 URL,通过本地 DNS 零碎的解析,发现该 URL 对应的是一个 CDN 专用的 DNS 服务器,DNS 零碎就会将域名解析权交给 CNAME 指向的 CDN 专用的 DNS 服务器。
  2. CND 专用 DNS 服务器将 CND 的全局负载平衡设施 IP 地址返回给用户
  3. 用户向 CDN 的全局负载平衡设施发动数据申请
  4. CDN 的全局负载平衡设施依据用户的 IP 地址,以及用户申请的内容 URL,抉择一台用户所属区域的区域负载平衡设施,通知用户向这台设施发动申请
  5. 区域负载平衡设施抉择一台适合的缓存服务器来提供服务,将该缓存服务器的 IP 地址返回给全局负载平衡设施
  6. 全局负载平衡设施把服务器的 IP 地址返回给用户
  7. 用户向该缓存服务器发动申请,缓存服务器响应用户的申请,将用户所需内容发送至用户终端。

如果缓存服务器没有用户想要的内容,那么缓存服务器就会向它的上一级缓存服务器申请内容,以此类推,直到获取到须要的资源。最初如果还是没有,就会回到本人的服务器去获取资源。

CNAME(意为:别名):在域名解析中,实际上解析进去的指定域名对应的 IP 地址,或者该域名的一个 CNAME,而后再依据这个 CNAME 来查找对应的 IP 地址。

退出移动版