共计 10038 个字符,预计需要花费 26 分钟才能阅读完成。
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
标签由 name
和 content
属性定义,用来形容网页文档的属性 ,比方网页的作者,网页形容,关键词等,除了 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 预处理器 / 后处理器是什么?为什么要应用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译 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 缓存资源的过程:
- 浏览器通过 DNS 对域名进行解析(就是下面的 DNS 解析过程),顺次失去此域名对应的 IP 地址
- 浏览器依据失去的 IP 地址,向域名的服务主机发送数据申请
- 服务器向浏览器返回响应数据
(2)用户应用 CDN 缓存资源的过程:
- 对于点击的数据的 URL,通过本地 DNS 零碎的解析,发现该 URL 对应的是一个 CDN 专用的 DNS 服务器,DNS 零碎就会将域名解析权交给 CNAME 指向的 CDN 专用的 DNS 服务器。
- CND 专用 DNS 服务器将 CND 的全局负载平衡设施 IP 地址返回给用户
- 用户向 CDN 的全局负载平衡设施发动数据申请
- CDN 的全局负载平衡设施依据用户的 IP 地址,以及用户申请的内容 URL,抉择一台用户所属区域的区域负载平衡设施,通知用户向这台设施发动申请
- 区域负载平衡设施抉择一台适合的缓存服务器来提供服务,将该缓存服务器的 IP 地址返回给全局负载平衡设施
- 全局负载平衡设施把服务器的 IP 地址返回给用户
- 用户向该缓存服务器发动申请,缓存服务器响应用户的申请,将用户所需内容发送至用户终端。
如果缓存服务器没有用户想要的内容,那么缓存服务器就会向它的上一级缓存服务器申请内容,以此类推,直到获取到须要的资源。最初如果还是没有,就会回到本人的服务器去获取资源。
CNAME(意为:别名):在域名解析中,实际上解析进去的指定域名对应的 IP 地址,或者该域名的一个 CNAME,而后再依据这个 CNAME 来查找对应的 IP 地址。