资讯专栏INFORMATION COLUMN

长期维护更新,前端面试题整理

Xufc / 3023人阅读

摘要:网上找到的各种面试题整理,长期更新。大部分答案整理来自网络,有问题的地方,希望大家能指出,及时修改技术更新迭代,也会及时更新博客原地址前端前端性能优化清理文档,即超文本标记语言,几乎是所有网站的支柱。在最近更新的中,甚至可以创建图表。

网上找到的各种面试题整理,长期更新。大部分答案整理来自网络,有问题的地方,希望大家能指出,及时修改;技术更新迭代,也会及时更新
博客原地址:https://finget.github.io/2019...
前端 前端性能优化

1.清理 HTML 文档

HTML,即超文本标记语言,几乎是所有网站的支柱。HTML 为网页带来标题、子标题、列表和其它一些文档结构的格式。在最近更新的 HTML5 中,甚至可以创建图表。

HTML 很容易被网络爬虫识别,因此搜索引擎可以根据网站的内容在一定程度上实时更新。在写 HTML 的时候,你应该尝试让它简洁而有效。此外,在 HTML 文档中引用外部资源的时候也需要遵循一些最佳实践方法。

a.恰当放置 CSS

Web 设计者喜欢在网页建立起主要的 HTML 骨架之后再来创建样式表。这样一来,网页中的样式表往往会放在 HTML 的后面,接近文档结束的地方。然而推荐的做法是把 CSS 放在 HTML 的上面部分,文档头之内,这可以确保正常的渲染过程。

这个策略不能提高网站的加载速度,但它不会让访问者长时间看着空白屏幕或者无格式的文本(FOUT)等待。如果网页大部分可见元素已经加载出来了,访问者才更有可能等待加载整个页面,从而带来对前端的优化效果。这就是知觉性能

b.正确放置 Javascript

另一方面,如果将 JavaScript 放置在 head 标签内或 HTML 文档的上部,这会阻塞 HTML 和 CSS 元素的加载过程。这个错误会导致页面加载时间增长,增加用户等待时间,容易让人感到不耐烦而放弃对网站的访问。不过,您可以通过将 JavaScript 属性置于 HTML 底部来避免此问题。

此外,在使用 JavaScript 时,人们通常喜欢用异步脚本加载。这会阻止

压缩技术可以从文件中去掉多余的字符。你在编辑器中写代码的时候,会使用缩进和注释,这些方法无疑会让你的代码简洁而且易读,但它们也会在文档中添加多余的字节。

使用预先获取

预先获取可以在真正需要之前通过取得必需的资源和相关数据来改善访问用户的浏览体验,主要有3类预先获取:

链接预先获取

DNS 预先获取

预先渲染

在你离开当前 web 页面之前,使用预先获取方式,对应每个链接的 URL 地址,CSS,图片和脚本都会被预先获取。这保证了访问者能在最短时间内使用链接在画面间切换。

幸运的是,预先获取很容易实现。根据你想要使用的预先获取形式,你只需在网站 HTML 中的链接属性上增加 rel=”prefetch”,rel=”dns-prefetch”,或者 rel=”prerender” 标记。

6.使用 CDN 和缓存提高速度

内容分发网络能显著提高网站的速度和性能。使用 CDN 时,您可以将网站的静态内容链接到全球各地的服务器扩展网络。如果您的网站观众遍布全球,这项功能十分有用。 CDN 允许您的网站访问者从最近的服务器加载数据。如果您使用 CDN,您网站内的文件将自动压缩,以便在全球范围内快速分发。

CDN 是一种缓存方法,可极大改善资源的分发时间,同时,它还能实现一些其他的缓存技术,如,利用浏览器缓存。

合理地设置浏览器缓存,能让浏览器自动存储某些文件,以便加快传输速度。此方法的配置可以直接在源服务器的配置文件中完成。

7.压缩文件

虽然许多 CDN 服务可以压缩文件,但如果不使用 CDN,您也可以考虑在源服务器上使用文件压缩方法来改进前端优化。 文件压缩能使网站的内容轻量化,更易于管理。 最常用的文件压缩方法之一是 Gzip。 这是缩小文档、音频文件、PNG图像和等其他大文件的绝佳方法。

Brotli 是一个比较新的文件压缩算法,目前正变得越来越受欢迎。 此开放源代码算法由来自 Google 和其他组织的软件工程师定期更新,现已被证明比其他现有压缩方法更好用。 这种算法的支持目前还比较少,但作为后起之秀指日可待。

8.使用轻量级框架

除非你只用现有的编码知识构建网站,不然,你可以尝试使用一个好的前端框架来避免许多不必要的前端优化错误。虽然有一些更大,更知名的框架能提供更多功能和选项,但它们不一定适合你的 Web 项目。

所以说,不仅确定项目所需功能很重要,选择合适的框架也很重要——它要在提供所需功能的同时保持轻量。最近许多框架都使用简洁的 HTML,CSS 和 JavaScript 代码。

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

参考链接:
详细解读https://segmentfault.com/a/1190000006879700
详细解读https://mp.weixin.qq.com/s/jjL4iA7p6aYEAQyWhn4QbQ

输入地址
1.浏览器查找域名的 IP 地址
2.这一步包括 DNS 具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存…
3.浏览器向 web 服务器发送一个 HTTP 请求
4.服务器的永久重定向响应(从 http://example.com 到 http://www.example.com)
5.浏览器跟踪重定向地址
6.服务器处理请求
7.服务器返回一个 HTTP 响应
8.浏览器显示 HTML
9.浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)
10.浏览器发送异步请求

URL 到底是啥

URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址。
比如 http://www.w3school.com.cn/ht...,遵守以下的语法规则:

scheme://host.domain:port/path/filename
各部分解释如下:
scheme - 定义因特网服务的类型。常见的协议有 http、https、ftp、file,其中最常见的类型是 http,而 https 则是进行加密的网络传输。
host - 定义域主机(http 的默认主机是 www)
domain - 定义因特网域名,比如 w3school.com.cn
port - 定义主机上的端口号(http 的默认端口号是 80)
path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。
filename - 定义文档/资源的名称

讲tcp/ip网络层、三次握手,为什么不能两次握手
客服端和服务端在进行http请求和返回的工程中,需要创建一个TCP connection(由客户端发起),http不存在连接这个概念,它只有请求和响应。请求和响应都是数据包,它们之间的传输通道就是TCP connection。

位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)Sequence number(顺序号码) Acknowledge number(确认号码)

第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;(第一次握手,由浏览器发起,告诉服务器我要发送请求了)

第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)

第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功;(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

谢希仁著《计算机网络》中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

这种情况是:一端(client)A发出去的第一个连接请求报文并没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间才到达另一端(server)B。本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立。

问题的本质是,信道是不可靠的,但是我们要建立可靠的连接发送可靠的数据,也就是数据传输是需要可靠的。在这个时候三次握手是一个理论上的最小值,并不是说是tcp协议要求的,而是为了满足在不可靠的信道上传输可靠的数据所要求的。

这个网上转载的例子不错:

三次握手:
A:“喂,你听得到吗?”A->SYN_SEND
B:“我听得到呀,你听得到我吗?”应答与请求同时发出 B->SYN_RCVD | A->ESTABLISHED
A:“我能听到你,今天balabala……”B->ESTABLISHED

四次挥手:
A:“喂,我不说了。”A->FIN_WAIT1
B:“我知道了。等下,上一句还没说完。Balabala…..”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,说完了,我也不说了。”B->LAST_ACK
A:”我知道了。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSE

iframe有那些缺点?

iframe会阻塞主页面的Onload事件;

搜索引擎的检索程序无法解读这种页面,不利于SEO;

iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。

使用iframe之前需要考虑这两个缺点。如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题

websocket握手过程

在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。

客户端请求web socket连接时,会向服务器端发送握手请求

请求头大致内容:

请求包说明:

必须是有效的http request 格式;

HTTP request method 必须是GET,协议应不小于1.1 如: Get / HTTP/1.1;

必须包括Upgrade头域,并且其值为”websocket”;

必须包括”Connection” 头域,并且其值为”Upgrade”;

必须包括”Sec-WebSocket-Key”头域,其值采用base64编码的随机16字节长的字符序列;

如果请求来自浏览器客户端,还必须包括Origin头域 。 该头域用于防止未授权的跨域脚本攻击,服务器可以从Origin决定是否接受该WebSocket连接;

必须包括”Sec-webSocket-Version” 头域,当前值必须是13;

可能包括”Sec-WebSocket-Protocol”,表示client(应用程序)支持的协议列表,server选择一个或者没有可接受的协议响应之;

可能包括”Sec-WebSocket-Extensions”, 协议扩展, 某类协议可能支持多个扩展,通过它可以实现协议增强;

可能包括任意其他域,如cookie.

服务端响应如下:

应答包说明: 
*必须包括Upgrade头域,并且其值为”websocket”;
*必须包括Connection头域,并且其值为”Upgrade”;
*必须包括Sec-WebSocket-Accept头域,其值是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码,就是“Sec-WebSocket-Accept”的值;
*应答包中冒号后面有一个空格;
*最后需要两个空行作为应答包结束

参考链接:
Websocket协议之握手连接

跨域以及解决办法

同源

符合”协议+域名+端口”三者相同,就是同源

同源策略

同源策略,其初衷是为了浏览器的安全性,通过以下三种限制,保证浏览器不易受到XSS、CSFR等攻击。

- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- AJAX 请求不能发送

跨域解决方案

通过jsonp跨域

document.domain + iframe跨域

location.hash + iframe

window.name + iframe跨域

postMessage跨域

跨域资源共享(CORS)

nginx代理跨域

nodejs中间件代理跨域

WebSocket协议跨域

前端持久化的方式、区别

最容易想到的解决方案是:

1.使用前端cookie技术来保存本地化数据,如jquery.cookie.js;
2.使用html5提供的Web Storage技术来提供解决方案;

用cookie存储永久数据存在以下几个问题:
1.大小:cookie的大小被限制在4KB。
2.带宽:cookie是随HTTP事务一起被发送的,因此会浪费一部分发送cookie时使用的带宽。
3.复杂性:要正确的操纵cookie是很困难的。

针对这些问题,在HTML5中,重新提供了一种在客户端本地保存数据的功能,它就是Web Storage。
具体来说,Web Storage又分为两种:
1.sessionStorage:将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据。
2.localStorage:将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。

这两者的区别在于,sessionStorage为临时保存,而localStorage为永久保存。

前端持久化--evercookie

介绍http2.0

所有数据以二进制传输。HTTP1.x是基于文本的,无法保证健壮性,HTTP2.0绝对使用新的二进制格式,方便且健壮

同一个连接里面发送多个请求不再需要按照顺序来

头信息压缩以及推送等提高效率的功能

Http 2.0协议简介
HTTP 2.0 详细介绍,http2.0详细介绍
HTTP/2.0 相比1.0有哪些重大改进

通过什么做到并发请求

我能想到的只有Promise.all(),欢迎补充

b和strong的区别

粗体文本, 用于强调文本,他们的样式是一样的
有一种说法,是貌似在盲人用的机器上会读两遍。因为没有对应的测试条件,所以没做验证。

Access-Control-Allow-Origin在服务端哪里配置

header("Access-Control-Allow-Origin:*");

csrf跨站攻击怎么解决

CSRF,全称为Cross-Site Request Forgery,跨站请求伪造,是一种网络攻击方式,它可以在用户毫不知情的情况下,以用户的名义伪造请求发送给被攻击站点,从而在未授权的情况下进行权限保护内的操作。

具体来讲,可以这样理解CSRF。攻击者借用用户的名义,向某一服务器发送恶意请求,对服务器来讲,这一请求是完全合法的,但攻击者确完成了一个恶意操作,比如以用户的名义发送邮件,盗取账号,购买商品等等

一般网站防御CSRF攻击的方案:
(1)验证token值。
(2)验证HTTP头的Referer。
(3)在HTTP头中自定义属性并验证
(4)服务器端表单hash认证
在所有的表单里面随机生成一个hash,server在表单处理时去验证这个hash值是否正确,这样工作量比较大

CSRF(跨站请求伪造攻击)漏洞详解

CSS 清除浮动的方式
// 第一种
.ovh{
  overflow:hidden;
}
// 第二种
.clear{
  clear:both;
}
// 第三种
.clearfix:after{ 
 content:"";//设置内容为空
 height:0;//高度为0
 line-height:0;//行高为0
 display:block;//将文本转为块级元素
 visibility:hidden;//将元素隐藏
 clear:both//清除浮动
}
.clearfix{
 zoom:1;为了兼容IE
}

免费公开课带你彻底掌握 CSS 浮动

当给父元素设置"overflow:hidden"时,实际上创建了一个超级属性BFC,此超级属性反过来决定了"height:auto"是如何计算的。在“BFC布局规则”中提到:计算BFC的高度时,浮动元素也参与计算。因此,父元素在计算其高度时,加入了浮动元素的高度,“顺便”达成了清除浮动的目标,所以父元素就包裹住了子元素。
垂直居中的几种方式
// 第一种
.center {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
// 第二种
.center {
  width: 100px;
  height: 100px;
  position: absolute;
  left: 50%;
  top: 50%;
  margin-top: -50px;
  margin-left: -50px;
}
// 第三种
.center {
  position: absolute;
  margin:auto;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
// 第四种
.parent {
  display: flex;
  align-items: center;
  justify-content: center;
}
// 第五种
.parent{
  display: flex;
}

.content{
  margin: auto; /*自动相对于父元素水平垂直居中*/
}
// 第六种
.parent {
    display: table;
}

.child {
    display: table-cell;
    vertical-align: middle;
}
BFC是什么

BFC(Block Formatting Context),块级格式化上下文,是Web页面中盒模型布局的CSS渲染模式。它的定位体系属于常规文档流。

原理(渲染规则):

在BFC元素的垂直方向上的边距,会发生重叠

BFC的区域不会与浮动元素的box重叠

BFC在页面上是一个独立的容器,外面的元素不会影响里面的元素

计算BFC高度时,浮动元素也会参与计算


1

2

3



我是浮动元素

怎么创建BFC:

float的值不为none

position的值不为static或者relative

display的值为 table-cell, table-caption, inline-block, flex, 或者 inline-flex中的其中一个

overflow的值不为visible

浮动,绝对定位元素,inline-blocks, table-cells, table-captions,和overflow的值不为visible的元素,(除了这个值已经被传到了视口的时候)将创建一个新的块级格式化上下文。

上面的引述几乎总结了一个BFC是怎样形成的。但是让我们以另一种方式来重新定义以便能更好的去理解.

参考链接:
理解CSS中BFC

讲flex,手写出flex常用的属性,并且讲出作用

这个直接看 阮一峰:Flex 布局教程

介绍css3中position:sticky

单词sticky的中文意思是“粘性的”,position:sticky表现也符合这个粘性的表现。基本上,可以看出是position:relative和position:fixed的结合体——当元素在屏幕内,表现为relative,就要滚出显示器屏幕的时候,表现为fixed。

详细讲解的还是看大神的吧,张鑫旭:position:sticky

JavaScript js三座大山

原型与原型链,作用域及闭包,异步和单线程。
三座大山,真不是一两句可以说清楚的,只有靠大家多看,多用,多理解,放点链接吧。

原型,原型链,call/apply
JavaScript从初级往高级走系列————prototype
JavaScript从初级往高级走系列————异步
JavaScript的预编译过程
内存空间详解
作用域和闭包
JavaScript深入之词法作用域和动态作用域
JavaScript深入之作用域链
事件循环机制

什么是闭包

参考链接:
什么是闭包?https://mp.weixin.qq.com/s/OthfFRwf-rQmVbMnXAqnCg
作用域与闭包https://yangbo5207.github.io/wutongluo/ji-chu-jin-jie-xi-lie/si-3001-zuo-yong-yu-lian-yu-bi-bao.html

简言之,闭包是由函数引用其周边状态(词法环境)绑在一起形成的(封装)组合结构。在 JavaScript 中,闭包在每个函数被创建时形成。

这是基本原理,但为什么我们关心这些?实际上,由于闭包与它的词法环境绑在一起,因此闭包让我们能够从一个函数内部访问其外部函数的作用域。

要使用闭包,只需要简单地将一个函数定义在另一个函数内部,并将它暴露出来。要暴露一个函数,可以将它返回或者传给其他函数。

内部函数将能够访问到外部函数作用域中的变量,即使外部函数已经执行完毕。

在 JavaScript 中,闭包是用来实现数据私有的原生机制。当你使用闭包来实现数据私有时,被封装的变量只能在闭包容器函数作用域中使用。你无法绕过对象被授权的方法在外部访问这些数据。在 JavaScript 中,任何定义在闭包作用域下的公开方法才可以访问这些数据。

宏任务 与 微任务

参考链接:
js引擎执行机制https://segmentfault.com/a/1190000012806637
事件循环机制

一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。

任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。

// setTimeout中的回调函数才是进入任务队列的任务
setTimeout(function() {
    console.log("xxxx");
})
// 非常多的同学对于setTimeout的理解存在偏差。所以大概说一下误解:
// setTimeout作为一个任务分发器,这个函数会立即执行,而它所要分发的任务,也就是它的第一个参数,才是延迟执行

来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。

事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

promise里面和then里面执行有什么区别

promise里面的是宏任务,then后面的是微任务。

JS为什么要区分微任务和宏任务

这个问题本质就是为啥需要异步。如果js不是异步的话,由于js代码本身是自上而下执行的,那么如果上一行代码需要执行很久,下面的代码就会被阻塞,对用户来说,就是”卡死”,这样的话,会造成很差的用户体验。

JavaScript 实现异步编程的4种方法

你可能知道,Javascript语言的执行环境是"单线程"(single thread)。

所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

回调函数

假定有两个函数f1和f2,后者等待前者的执行结果。
如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数

function f1(callback){
 setTimeout(function () {
 // f1的任务代码
  callback();
 }, 1000);
}

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

事件监听

另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

f1.on("done", f2);

上面这行代码的意思是,当f1发生done事件,就执行f2。然后,对f1进行改写:

function f1(){
 setTimeout(function () {
 // f1的任务代码
  f1.trigger("done");
 }, 1000);
}

发布订阅

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。

jQuery.subscribe("done", f2);
function f1(){
 setTimeout(function () {
    // f1的任务代码
    jQuery.publish("done");
 }, 1000);
}

Promise

f1().then(f2).then(f3);
new 的过程

新生成了一个对象

链接到原型

绑定 this

返回新对象

function create() {
    // 创建一个空的对象
    let obj = new Object()
    // 获得构造函数
    let Con = [].shift.call(arguments)
    // 链接到原型
    obj.__proto__ = Con.prototype
    // 绑定 this,执行构造函数
    let result = Con.apply(obj, arguments)
    // 确保 new 出来的是个对象
    return typeof result === "object" ? result : obj
}
原型继承与类继承

JS原型继承和类式继承http://www.cnblogs.com/constantince/p/4754992.html

// 类继承
var father = function() {
  this.age = 52;
  this.say = function() {
    alert("hello i am "+ this.name " and i am "+this.age + "years old");
  }
}
 
var child = function() {
  this.name = "bill";
  father.call(this);
}
 
var man = new child();
man.say();
// 原型继承
var father = function() {
}
father.prototype.a = function() {
}
var child = function(){}
//开始继承
child.prototype = new father();
var man = new child();
man.a();

和原型对比起来,构造函数(类)式继承有什么不一样呢?首先,构造函数继承的方法都会存在父对象之中,每一次实例,都会将funciton保存在内存中,这样的做法毫无以为会带来性能上的问题。其次类式继承是不可变的。在运行时,无法修改或者添加新的方法,这种方式是一种固步自封的死方法。而原型继承是可以通过改变原型链接而对子类进行修改的。另外就是类式继承不支持多重继承,而对于原型继承来说,你只需要写好extend对对象进行扩展即可。

== 和 ===的区别,什么情况下用相等==

==是===类型转换(又称强制),==只需要值相等就会返回true,而===必须值和数据类型都相同才会返回true。

bind、call、apply的区别

1.每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
2.相同点:这两个方法的作用是一样的。
都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。
3.不同点:接收参数的方式不同。

apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。
说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。
call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
语法:call([thisObject[,arg1 [,arg2 [,…,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。
说明: call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。

bind和call、apply最大的区别就是,call、apply不仅改变this的指向,还会直接支持代码,而bind不会。

var cat = {
  name: "咪咪"
}
function beatTheMonster(){
  console.log(this.name);
}
beatTheMonster.call(cat);

// 1.call 改变了this的指向。改变到了cat上。
// 2.beatTheMonster函数/方法执行了
// 3.bind(),保存了方法,并没有直接调用它
图片预览



function showPreview(source) {
  var file = source.files[0];
  if(window.FileReader) {
      var fr = new FileReader();
      fr.onloadend = function(e) {
        document.getElementById("portrait").src = e.target.result;
      };
      fr.readAsDataURL(file);
  }
}
扁平化多维数组
var result = []
function unfold(arr){
     for(var i=0;i< arr.length;i++){
      if(typeof arr[i]=="object" && arr[i].length>1) {
       unfold(arr[i]);
     } else {        
       result.push(arr[i]);
     }
  }
}
var arr = [1,3,4,5,[6,[0,1,5],9],[2,5,[1,5]],[5]];
unfold(arr)
var c=[1,3,4,5,[6,[0,1,5],9],[2,5,[1,5]],[5]];
var b = c.toString().split(",")
var arr=[1,3,4,5,[6,[0,1,5],9],[2,5,[1,5]],[5]];
const flatten = arr => arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
var result = flatten(arr)
this的指向问题

参考链接:
归纳总结this的指向问题https://finget.github.io/2018/11/28/this/
ECMAScript规范解读thishttps://github.com/mqyqingfeng/Blog/issues/7

function foo() {
    console.log(this.a)
}
var a = 1
foo()

var obj = {
    a: 2,
    foo: foo
}
obj.foo()

// 以上两者情况 `this` 只依赖于调用函数前的对象,优先级是第二个情况大于第一个情况

// 以下情况是优先级最高的,`this` 只会绑定在 `c` 上,不会被任何方式修改 `this` 指向
var c = new foo()
c.a = 3
console.log(c.a)

// 还有种就是利用 call,apply,bind 改变 this,这个优先级仅次于 new

箭头函数中的this:

function a() {
    return () => {
        return () => {
            console.log(this)
        }
    }
}
console.log(a()()())

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。

async/await

理解 JavaScript 的 async/awaithttps://segmentfault.com/a/1190000007535316

async function async1() {
  console.log( "async1 start")
  await async2()
  console.log( "async1 end")
}
async function async2() {
  console.log( "async2")
}
async1()
console.log( "script start")

这里注意一点,可能大家都知道await会让出线程,阻塞后面的代码,那么上面例子中, async2script start 谁先打印呢?
是从左向右执行,一旦碰到await直接跳出,阻塞 async2() 的执行?
还是从右向左,先执行async2后,发现有await关键字,于是让出线程,阻塞代码呢?
实践的结论是,从右向左的。先打印async2,后打印的 script start。
之所以提一嘴,是因为我经常看到这样的说法,「一旦遇到await就立刻让出线程,阻塞后面的代码」。

Promise 和 async/await 和 callback的区别

我的理解:callback是解决异步的早期方案,但是会导致‘回调地狱’,然后就出现了Promise,利用.then优化了回调地狱的问题,而async/await是在promise 进一步封装,利用看似同步的方式解决异步问题。Promise和async/await都是语法糖。就是写起来更简单,阅读性和维护性增强。

Promise 和 async/await在执行时都干了什么,推荐看看:8 张图帮你一步步看清 async/await 和 promise 的执行顺序

手写实现promise

直接粘贴大神的代码:

// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收一个函数参数,该函数会立即执行
function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  // 用于保存 then 中的回调,只有当 promise
  // 状态为 pending 时才会缓存,并且每个实例至多缓存一个
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function (value) {
    if (value instanceof MyPromise) {
      // 如果 value 是个 Promise,递归执行
      return value.then(_this.resolve, _this.reject)
    }
    setTimeout(() => { // 异步执行,保证执行顺序
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb => cb());
      }
    })
  };

  _this.reject = function (reason) {
    setTimeout(() => { // 异步执行,保证执行顺序
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb => cb());
      }
    })
  }
  // 用于解决以下问题
  // new Promise(() => throw Error("error))
  try {
    fn(_this.resolve, _this.reject);
  } catch (e) {
    _this.reject(e);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  var self = this;
  // 规范 2.2.7,then 必须返回一个新的 promise
  var promise2;
  // 规范 2.2.onResolved 和 onRejected 都为可选参数
  // 如果类型不是函数需要忽略,同时也实现了透传
  // Promise.resolve(4).then().then((value) => console.log(value))
  onResolved = typeof onResolved === "function" ? onResolved : v => v;
  onRejected = typeof onRejected === "function" ? onRejected : r => throw r;

  if (self.currentState === RESOLVED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      // 规范 2.2.4,保证 onFulfilled,onRjected 异步执行
      // 所以用了 setTimeout 包裹下
      setTimeout(function () {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      });
    }));
  }

  if (self.currentState === REJECTED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      setTimeout(function () {
        // 异步执行onRejected
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      });
    }));
  }

  if (self.currentState === PENDING) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      self.resolvedCallbacks.push(function () {
        // 考虑到可能会有报错,所以使用 try/catch 包裹
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {
          reject(r);
        }
      });

      self.rejectedCallbacks.push(function () {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {
          reject(r);
        }
      });
    }));
  }
};
// 规范 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
  if (promise2 === x) {
    return reject(new TypeError("Error"));
  }
  // 规范 2.3.2
  // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(function (value) {
        // 再次调用该函数是为了确认 x resolve 的
        // 参数是什么类型,如果是基本类型就再次 resolve
        // 把值传给下个 then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  // 规范 2.3.3.3.3
  // reject 或者 resolve 其中一个执行过得话,忽略其他的
  let called = false;
  // 规范 2.3.3,判断 x 是否为对象或者函数
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    // 规范 2.3.3.2,如果不能取出 then,就 reject
    try {
      // 规范 2.3.3.1
      let then = x.then;
      // 如果 then 是函数,调用 x.then
      if (typeof then === "function") {
        // 规范 2.3.3.3
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            // 规范 2.3.3.3.1
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e => {
            if (called) return;
            called = true;
            reject(e);
          }
        );
      } else {
        // 规范 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 规范 2.3.4,x 为基本类型
    resolve(x);
  }
}
Promise.all实现原理
MyPromise.all = (arr) => {
  if (!Array.isArray(arr)) {
    throw new TypeError("参数应该是一个数组!");
  };
  return new MyPromise(function(resolve, reject) {
    let i = 0, result = [];
    next();
    function next() {
      //如果不是MyPromise对象,需要转换
      MyPromise.resolve(arr[i]).then(res => {
        result.push(res);
        i++;
        if (i === arr.length) {
            resolve(result);
        } else {
            next();
        };
      }, reject);
    };
  })
};

参考链接:
原生es6封装一个Promise对象

手写函数防抖和函数节流

你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。

这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。

PS:防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

我们先来看一个袖珍版的防抖理解一下防抖的实现:

// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
  // 缓存一个定时器id
  let timer = 0
  // 这里返回的函数是每次用户实际调用的防抖函数
  // 如果已经设定过定时器了就清空上一次的定时器
  // 开始一个新的定时器,延迟执行用户传入的方法
  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}
// 不难看出如果用户调用该函数的间隔小于wait的情况下,上一次的时间还未到就被清除了,并不会执行函数

这是一个简单版的防抖,但是有缺陷,这个防抖只能在最后调用。一般的防抖会有immediate选项,表示是否立即调用。这两者的区别,举个栗子来说:

例如在搜索引擎搜索问题的时候,我们当然是希望用户输入完最后一个字才调用查询接口,这个时候适用延迟执行的防抖函数,它总是在一连串(间隔小于wait的)函数触发之后调用。

例如用户给interviewMap点star的时候,我们希望用户点第一下的时候就去调用接口,并且成功之后改变star按钮的样子,用户就可以立马得到反馈是否star成功了,这个情况适用立即执行的防抖函数,它总是在第一次调用,并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。

// 这个是用来获取当前时间戳的
function now() {
  return +new Date()
}
/**
 * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,是否立即调用函数
 * @return {function}             返回客户调用函数
 */
function debounce (func, wait = 50, immediate = true) {
  let timer, context, args

  // 延迟执行函数
  const later = () => setTimeout(() => {
    // 延迟函数执行完毕,清空缓存的定时器序号
    timer = null
    // 延迟执行的情况下,函数会在延迟函数中执行
    // 使用到之前缓存的参数和上下文
    if (!immediate) {
      func.apply(context, args)
      context = args = null
    }
  }, wait)

  // 这里返回的函数是每次实际调用的函数
  return function(...params) {
    // 如果没有创建延迟执行函数(later),就创建一个
    if (!timer) {
      timer = later()
      // 如果是立即执行,调用函数
      // 否则缓存参数和调用上下文
      if (immediate) {
        func.apply(this, params)
      } else {
        context = this
        args = params
      }
    // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
    // 这样做延迟函数会重新计时
    } else {
      clearTimeout(timer)
      timer = later()
    }
  }
}

节流:

/**
 * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
 *
 * @param  {function}   func      回调函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。
 *                                如果想忽略结尾函数的调用,传入{trailing: false}
 *                                两者不能共存,否则函数不能执行
 * @return {function}             返回客户调用函数
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 之前的时间戳
    var previous = 0;
    // 如果 options 没传则设为空对象
    if (!options) options = {};
    // 定时器回调函数
    var later = function() {
      // 如果设置了 leading,就将 previous 设为 0
      // 用于下面函数的第一个 if 判断
      previous = options.leading === false ? 0 : _.now();
      // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // 获得当前时间戳
      var now = _.now();
      // 首次进入前者肯定为 true
      // 如果需要第一次不执行函数
      // 就将上次时间戳设为当前的
      // 这样在接下来计算 remaining 的值时会大于0
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 如果当前调用已经大于上次调用时间 + wait
      // 或者用户手动调了时间
       // 如果设置了 trailing,只会进入这个条件
      // 如果没有设置 leading,那么第一次会进入这个条件
      // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
      // 其实还是会进入的,因为定时器的延时
      // 并不是准确的时间,很可能你设置了2秒
      // 但是他需要2.2秒才触发,这时候就会进入这个条件
      if (remaining <= 0 || remaining > wait) {
        // 如果存在定时器就清理掉否则会调用二次回调
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // 判断是否设置了定时器和 trailing
        // 没有的话就开启一个定时器
        // 并且不能不能同时设置 leading 和 trailing
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };
图片懒加载与预加载
懒加载也就是延迟加载
原理:
页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过javascript设置了图片路径,浏览器才会发送请求。
懒加载的原理就是先在页面中把所有的图片统一使用一张占位图进行占位,把正真的路径存在元素的“data-url”(这个名字起个自己认识好记的就行)属性里,要用的时候就取出来,再设置
// 懒加载
function loadImg(src){
  let promise = new Promise(function (resolve, reject) {
    let img = document.createElement("img")
    img.onload = function () {
      resolve(img)
    }
    img.onerror = function () {
      reject("图片加载失败")
    }
    img.src = src
  })
  return promise
}
预加载 提前加载图片,当用户需要查看时可直接从本地缓存中渲染

实现预加载的三种方法:

用CSS和JavaScript实现预加载

仅使用JavaScript实现预加载

使用Ajax实现预加载

用CSS和JavaScript实现预加载

#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }  
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }  
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }

将这三个ID选择器应用到(X)HTML元素中,我们便可通过CSS的background属性将图片预加载到屏幕外的背景上。只要这些图片的路径保持不变,当它们在Web页面的其他地方被调用时,浏览器就会在渲染过程中使用预加载(缓存)的图片。简单、高效,不需要任何JavaScript。

该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。代码如下:

function preloader() {  
    if (document.getElementById) {  
        document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-02").style.background = "url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-03").style.background = "url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";  
    }  
}  
function addLoadEvent(func) {  
    var oldonload = window.onload;  
    if (typeof window.onload != "function") {  
        window.onload = func;  
    } else {  
        window.onload = function() {  
            if (oldonload) {  
                oldonload();  
            }  
            func();  
        }  
    }  
}  
addLoadEvent(preloader);

仅使用JavaScript实现预加载

var images = new Array()  
function preload() {  
  for (i = 0; i < preload.arguments.length; i++) {  
    images[i] = new Image()  
    images[i].src = preload.arguments[i]  
  }  
}  
preload(  
  "http://domain.tld/gallery/image-001.jpg",  
   "http://domain.tld/gallery/image-002.jpg",  
   "http://domain.tld/gallery/image-003.jpg"  
)  

使用Ajax实现预加载

window.onload = function() {  
    setTimeout(function() {  
        // XHR to request a JS and a CSS  
        var xhr = new XMLHttpRequest();  
        xhr.open("GET", "http://domain.tld/preload.js");  
        xhr.send("");  
        xhr = new XMLHttpRequest();  
        xhr.open("GET", "http://domain.tld/preload.css");  
        xhr.send("");  
        // preload image  
        new Image().src = "http://domain.tld/preload.png";  
    }, 1000);  
};

上面代码预加载了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超时是为了防止脚本挂起,而导致正常页面出现功能问题。

window.onload = function() {  
    setTimeout(function() {  
        // reference to   
        var head = document.getElementsByTagName("head")[0];  
        // a new CSS  
        var css = document.createElement("link");  
        css.type = "text/css";  
        css.rel  = "stylesheet";  
        css.href = "http://domain.tld/preload.css";  
        // a new JS  
        var js  = document.createElement("script");  
        js.type = "text/javascript";  
        js.src  = "http://domain.tld/preload.js";  
        // preload JS and CSS  
        head.appendChild(css);  
        head.appendChild(js);  
        // preload image  
        new Image().src = "http://domain.tld/preload.png";  
    }, 1000);  
};

这里,我们通过DOM创建三个元素来实现三个文件的预加载。正如上面提到的那样,使用Ajax,加载文件不会应用到加载页面上。从这点上看,Ajax方法优越于JavaScript。

参考链接:
Javascript图片预加载详解

使用es5实现es6的class

借用babel工具可以学习一下,es6的class 编译成es5时,长什么样

// ES6
class Person{
  constructor(name,age){
      this.name = name
   this.age = age
  }
  say() {
    console.log(this.name)
  }
  run() {
   console.log("run fast")
  }
  // 静态方法,类调用
  static getGirl(){
    console.log("girl friend")
  }
}
// ES5
var _createClass = function() {
  function defineProperties(target, props) { 
      for (var i = 0; i < props.length; i++) { 
          var descriptor = props[i];
            // 枚举
          descriptor.enumerable = descriptor.enumerable || false;
          // 可配置
          descriptor.configurable = true; 
      if ("value" in descriptor) 
        // 可写
          descriptor.writable = true;
          
      Object.defineProperty(target, descriptor.key, descriptor); 
    } 
  } 
  return function(Constructor, protoProps, staticProps) { 
      if (protoProps) 
          defineProperties(Constructor.prototype, protoProps); 
      if (staticProps) 
          defineProperties(Constructor, staticProps); 
      return Constructor; 
  }; 
}();

// 禁止 直接调用 Person()
function _classCallCheck(instance, Constructor) { 
    if (!(instance instanceof Constructor)) { 
        throw new TypeError("Cannot call a class as a function");
    }
}


var Person = function () {
  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }, {
    key: "run",
    value: function run() {
      console.log("run fast");
    }
  }], [{
    key: "getGirl",
    value: function getGirl() {
      console.log("girl friend");
    }
  }]);
  return Person;
}();

关于对象的enumerablewritableconfigurable,可以看看Javascript properties are enumerable, writable and configurable

JavaScript的sort方法内部使用的什么排序

默认排序顺序是根据字符串Unicode码点

函数式编程
函数式编程的本质,函数式编程中的函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,无论什么时候调用,调用几次,值都是不变的。
函数式的最主要的好处是不可变性带来的。没有可变的状态,函数就是引用透明的没有副作用。函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,这样写的代码容易进行推理,不容易出错。这使得单元测试和调试更容易。

参考链接:
js函数式编程指南

回调函数的坏处

回调地狱、代码的可阅读性和可维护性降低

如何实现一个可设置过期时间的localStorage

直接上链接:如何给localStorage设置一个过期时间?

用JavaScript的异步实现sleep函数
async function test() {
  console.log("Hello")
  let res = await sleep(1000)
  console.log(res)
}
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}
test()

参考链接:
JavaScript的sleep实现--Javascript异步编程学习

手写实现jsonp
window._pt_lt = new Date().getTime();
window._pt_sp_2 = [];
_pt_sp_2.push("setAccount,2953009d");
var _protocol = (("https:" == document.location.protocol) ? " https://" : " http://");
(function() {
  var atag = document.createElement("script");
  atag.type = "text/javascript";
  atag.async = true;
  atag.src = _protocol + "js.ptengine.cn/2953009d.js";
  var s = document.getElementsByTagName("script")[0];
  s.parentNode.insertBefore(atag, s);
})();
浅拷贝和深拷贝的区别
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

参考链接:
js浅拷贝和深拷贝

for..in 和 for..of 的区别

推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。

for…in…遍历对象会遍历出对象的所有可枚举的属性

for...in循环出的是key,for...of循环出的是value

注意,for...of是ES6新引入的特性。修复了ES5引入的for...in的不足

for...of不能循环普通的对象,需要通过和Object.keys()搭配使用

cookie和localStorage的区别
特性 cookie sessionStorage localStorage
数据生命期 生成时就会被指定一个maxAge值,这就是cookie的生存周期,在这个周期内cookie有效,默认关闭浏览器失效 页面会话期间可用 除非数据被清除,否则一直存在
存放数据大小 4K左右(因为每次http请求都会携带cookie) 一般5M或更大
与服务器通信 由对服务器的请求来传递,每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 数据不是由每个服务器请求传递的,而是只有在请求时使用数据,不参与和服务器的通信
易用性 cookie需要自己封装setCookie,getCookie 可以用源生接口,也可再次封装来对Object和Array有更好的支持
共同点 都是保存在浏览器端,和服务器端的session机制不同
JS执行过程中分为哪些阶段

数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少

时间一样。引用类型的变量都是堆内存。堆内存就像书架一样,只要你知道书名,就能直接找到对应的书。

内存空间

var a = {b: 1} 存放在哪里?
var a = {b: {c: 1}}存放在哪里?
var a = {name: "前端开发"}; var b = a; a = null, 那么b输出什么?

js变量可以用来保存两种类型的值:基本类型值和引用类型值。在ES6之前共有6种数据类型:Undefined、Null、Boolean、Number,String和Object,其中前5种是基本类型值。

基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中。

从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本。

引用类型的值是对象,保存在堆内存中。

包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针

理解队列数据结构的目的主要是为了清晰的明白事件循环(Event Loop)的机制到底是怎么回事。

jquery $(document).ready() 与window.onload的区别

1.执行时间

window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行。 
$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。

2.编写个数不同

window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个 
$(document).ready()可以同时编写多个,并且都可以得到执行

3.简化写法

window.onload没有简化写法 
$(document).ready(function(){})可以简写成$(function(){});
一个是数组中所有数都出现了两次,只有一个元素只出现了一次,找出这个数
let arr = [1,1,3,4,3,5,6,8,6,5,8]
function get() {
    let num = 0;
    arr.forEach(item => {
        num = num^item // 异或运算
    })
    console.log(num)
}
get()

js异或运算符^小技巧

Vue Vue 生命周期

1.beforcreate
2.created
3.beformount
4.mounted
5.beforeUpdate
6.updated
7.actived
8.deatived
9.beforeDestroy
10.destroyed

vue里面的虚拟dom是怎么回

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101051.html

相关文章

  • 前端开发-从入门到Offer - 收藏集 - 掘金

    摘要:一些知识点有哪些方法方法前端从入门菜鸟到实践老司机所需要的资料与指南合集前端掘金前端从入门菜鸟到实践老司机所需要的资料与指南合集归属于笔者的前端入门与最佳实践。 工欲善其事必先利其器-前端实习简历篇 - 掘金 有幸认识很多在大厂工作的学长,在春招正式开始前为我提供很多内部推荐的机会,非常感谢他们对我的帮助。现在就要去北京了,对第一份正式的实习工作也充满期待,也希望把自己遇到的一些问题和...

    sf_wangchong 评论0 收藏0
  • 前端工程师面试必备(持续更新中)

    摘要:最近的一次更新的变量有效,并且会作用于全部的引用的处理方式和相同,变量值输出时根据之前最近的一次定义计算,每次引用最近的定义有效嵌套三种预编译器的选择器嵌套在使用上来说没有任何区别,甚至连引用父级选择器的标记也相同。 面试汇总一:2018大厂高级前端面试题汇总 高级面试:【半月刊】前端高频面试题及答案汇总 css内容 响应式布局 当前主流的三种预编译器比较 CSS预处理器用一种专门的...

    jubincn 评论0 收藏0
  • Vue面试中,经常会被问到的面试/Vue知识点整理

    摘要:可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。我工作中只用到,对和不怎么熟与的区别相同点都支持指令内置指令和自定义指令都支持过滤器内置过滤器和自定义过滤器都支持双向数据绑定都不支持低端浏览器。 看看面试题,只是为了查漏补缺,看看自己那些方面还不懂。切记不要以为背了面试题,就万事大吉了,最好是理解背后的原理,这样面试的时候才能侃侃而谈。不然,稍微有水平的面试官一看就能看出,是...

    mengbo 评论0 收藏0
  • HTML面试整理总结

    摘要:前记为了准备春招面试,对自己的知识点进行一个总结积累,第一篇是关于方面的知识点,后续如果遇见新题会进行继续的补充什么是语义化,有什么好处语义化简单来说就是,段落使用,侧边栏用,主要内容使用。不存在或形式不正确会导致文档以混杂模式呈现。 前记 为了准备春招面试,对自己的知识点进行一个总结积累,第一篇是关于HTML方面的知识点,后续如果遇见新题会进行继续的补充 什么是 HTML 语义化,有...

    Chao 评论0 收藏0
  • 前端最实用书签(持续更新)

    摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<