寄生组合继承
题目形容: 实现一个你认为不错的 js 继承形式
实现代码如下:
function Parent(name) {
this.name = name;
this.say = () => {console.log(111);
};
}
Parent.prototype.play = () => {console.log(222);
};
function Children(name) {Parent.call(this);
this.name = name;
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();
—- 问题知识点分割线 —-
数组去重
ES5 实现:
function unique(arr) {var res = arr.filter(function(item, index, array) {return array.indexOf(item) === index
})
return res
}
ES6 实现:
var unique = arr => [...new Set(arr)]
—- 问题知识点分割线 —-
树形构造转成列表
题目形容:
[
{
id: 1,
text: '节点 1',
parentId: 0,
children: [
{
id:2,
text: '节点 1_1',
parentId:1
}
]
}
]
转成
[
{
id: 1,
text: '节点 1',
parentId: 0 // 这里用 0 示意为顶级节点
},
{
id: 2,
text: '节点 1_1',
parentId: 1 // 通过这个字段来确定子父级
}
...
]
实现代码如下:
function treeToList(data) {let res = [];
const dfs = (tree) => {tree.forEach((item) => {if (item.children) {dfs(item.children);
delete item.children;
}
res.push(item);
});
};
dfs(data);
return res;
}
—- 问题知识点分割线 —-
对 WebSocket 的了解
WebSocket 是 HTML5 提供的一种浏览器与服务器进行 全双工通信 的网络技术,属于应用层协定。它基于 TCP 传输协定,并复用 HTTP 的握手通道。浏览器和服务器只须要实现一次握手,两者之间就间接能够创立持久性的连贯,并进行双向数据传输。
WebSocket 的呈现就解决了半双工通信的弊病。它最大的特点是:服务器能够向客户端被动推动音讯,客户端也能够被动向服务器推送音讯。
WebSocket 原理:客户端向 WebSocket 服务器告诉(notify)一个带有所有接收者 ID(recipients IDs)的事件(event),服务器接管后立刻告诉所有沉闷的(active)客户端,只有 ID 在接收者 ID 序列中的客户端才会解决这个事件。
WebSocket 特点的如下:
- 反对双向通信,实时性更强
- 能够发送文本,也能够发送二进制数据‘’
- 建设在 TCP 协定之上,服务端的实现比拟容易
- 数据格式比拟轻量,性能开销小,通信高效
- 没有同源限度,客户端能够与任意服务器通信
- 协定标识符是 ws(如果加密,则为 wss),服务器网址就是 URL
- 与 HTTP 协定有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采纳 HTTP 协定,因而握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
Websocket 的应用办法如下:
在客户端中:
// 在 index.html 中间接写 WebSocket,设置服务端的端口号为 9999
let ws = new WebSocket('ws://localhost:9999');
// 在客户端与服务端建设连贯后触发
ws.onopen = function() {console.log("Connection open.");
ws.send('hello');
};
// 在服务端给客户端发来音讯的时候触发
ws.onmessage = function(res) {console.log(res); // 打印的是 MessageEvent 对象
console.log(res.data); // 打印的是收到的音讯
};
// 在客户端与服务端建设敞开后触发
ws.onclose = function(evt) {console.log("Connection closed.");
};
—- 问题知识点分割线 —-
HTTP 申请报文的是什么样的?
申请报⽂有 4 局部组成:
- 申请⾏
- 申请头部
- 空⾏
-
申请体
其中:(1)申请⾏包含:申请⽅法字段、URL 字段、HTTP 协定版本字段。它们⽤空格分隔。例如,GET /index.html HTTP/1.1。
(2)申请头部: 申请头部由关键字 / 值对组成,每⾏⼀对,关键字和值⽤英⽂冒号“:”分隔 - User-Agent:产⽣申请的浏览器类型。
- Accept:客户端可辨认的内容类型列表。
- Host:申请的主机名,容许多个域名同处⼀个 IP 地址,即虚拟主机。
(3)申请体: post put 等申请携带的数据
—- 问题知识点分割线 —-
什么是 HTTPS 协定?
超文本传输平安协定(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行平安通信的传输协定。HTTPS 经由 HTTP 进行通信,利用 SSL/TLS 来加密数据包。HTTPS 的次要目标是提供对网站服务器的身份认证,爱护替换数据的隐衷与完整性。HTTP 协定采纳 明文传输 信息,存在 信息窃听 、 信息篡改 和信息劫持 的危险,而协定 TLS/SSL 具备 身份验证 、 信息加密 和完整性校验 的性能,能够防止此类问题产生。
平安层的主要职责就是 对发动的 HTTP 申请的数据进行加密操作 和 对接管到的 HTTP 的内容进行解密操作。
—- 问题知识点分割线 —-
过程和线程的区别
- 过程能够看做独立利用,线程不能
- 资源:过程是 cpu 资源分配的最小单位(是能领有资源和独立运行的最小单位);线程是 cpu 调度的最小单位(线程是建设在过程的根底上的一次程序运行单位,一个过程中能够有多个线程)。
- 通信方面:线程间能够通过间接共享同一过程中的资源,而过程通信须要借助 过程间通信。
- 调度:过程切换比线程切换的开销要大。线程是 CPU 调度的根本单位,线程的切换不会引起过程切换,但某个过程中的线程切换到另一个过程中的线程时,会引起过程切换。
- 零碎开销:因为创立或撤销过程时,零碎都要为之调配或回收资源,如内存、I/O 等,其开销远大于创立或撤销线程时的开销。同理,在进行过程切换时,波及以后执行过程 CPU 环境还有各种各样状态的保留及新调度过程状态的设置,而线程切换时只需保留和设置大量寄存器内容,开销较小。
—- 问题知识点分割线 —-
强类型语言和弱类型语言的区别
- 强类型语言:强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量的应用要严格合乎定义,所有变量都必须先定义后应用。Java 和 C ++ 等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不通过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
- 弱类型语言:弱类型语言也称为弱类型定义语言,与强类型定义相同。JavaScript 语言就属于弱类型语言。简略了解就是一种变量类型能够被疏忽的语言。比方 JavaScript 是弱类型定义的,在 JavaScript 中就能够将字符串 ’12’ 和整数 3 进行连贯失去字符串 ’123’,在相加的时候会进行强制类型转换。
两者比照:强类型语言在速度上可能略逊色于弱类型语言,然而强类型语言带来的严谨性能够无效地帮忙防止许多谬误。
—- 问题知识点分割线 —-
深拷贝(思考到复制 Symbol 类型)
题目形容: 手写 new 操作符实现
实现代码如下:
function isObject(val) {return typeof val === "object" && val !== null;}
function deepClone(obj, hash = new WeakMap()) {if (!isObject(obj)) return obj;
if (hash.has(obj)) {return hash.get(obj);
}
let target = Array.isArray(obj) ? [] : {};
hash.set(obj, target);
Reflect.ownKeys(obj).forEach((item) => {if (isObject(obj[item])) {target[item] = deepClone(obj[item], hash);
} else {target[item] = obj[item];
}
});
return target;
}
// var obj1 = {
// a:1,
// b:{a:2}
// };
// var obj2 = deepClone(obj1);
// console.log(obj1);
—- 问题知识点分割线 —-
对象创立的形式有哪些?
个别应用字面量的模式间接创建对象,然而这种创立形式对于创立大量类似对象的时候,会产生大量的反复代码。但 js 和个别的面向对象的语言不同,在 ES6 之前它没有类的概念。然而能够应用函数来进行模仿,从而产生出可复用的对象创立形式,常见的有以下几种:
(1)第一种是工厂模式,工厂模式的次要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目标。然而它有一个很大的问题就是创立进去的对象无奈和某个类型分割起来,它只是简略的封装了复用代码,而没有建设起对象和类型间的关系。
(2)第二种是构造函数模式。js 中每一个函数都能够作为构造函数,只有一个函数是通过 new 来调用的,那么就能够把它称为构造函数。执行构造函数首先会创立一个对象,而后将对象的原型指向构造函数的 prototype 属性,而后将执行上下文中的 this 指向这个对象,最初再执行整个函数,如果返回值不是对象,则返回新建的对象。因为 this 的值指向了新建的对象,因而能够应用 this 给对象赋值。构造函数模式绝对于工厂模式的长处是,所创立的对象和构造函数建设起了分割,因而能够通过原型来辨认对象的类型。然而构造函数存在一个毛病就是,造成了不必要的函数对象的创立,因为在 js 中函数也是一个对象,因而如果对象属性中如果蕴含函数的话,那么每次都会新建一个函数对象,节约了不必要的内存空间,因为函数是所有的实例都能够通用的。
(3)第三种模式是原型模式,因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它蕴含了通过构造函数创立的所有实例都能共享的属性和办法。因而能够应用原型对象来增加专用属性和办法,从而实现代码的复用。这种形式绝对于构造函数模式来说,解决了函数对象的复用问题。然而这种模式也存在一些问题,一个是没有方法通过传入参数来初始化值,另一个是如果存在一个援用类型如 Array 这样的值,那么所有的实例将共享一个对象,一个实例对援用类型值的扭转会影响所有的实例。
(4)第四种模式是组合应用构造函数模式和原型模式,这是创立自定义类型的最常见形式。因为构造函数模式和原型模式离开应用都存在一些问题,因而能够组合应用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数办法的复用。这种办法很好的解决了两种模式独自应用时的毛病,然而有一点有余的就是,因为应用了两种不同的模式,所以对于代码的封装性不够好。
(5)第五种模式是动静原型模式,这一种模式将原型办法赋值的创立过程挪动到了构造函数的外部,通过对属性是否存在的判断,能够实现仅在第一次调用函数时对原型对象赋值一次的成果。这一种形式很好地对下面的混合模式进行了封装。
(6)第六种模式是寄生构造函数模式,这一种模式和工厂模式的实现基本相同,我对这个模式的了解是,它次要是基于一个已有的类型,在实例化时对实例化的对象进行扩大。这样既不必批改原来的构造函数,也达到了扩大对象的目标。它的一个毛病和工厂模式一样,无奈实现对象的辨认。
—- 问题知识点分割线 —-
TCP 的流量管制机制
一般来说,流量管制就是为了让发送方发送数据的速度不要太快,要让接管方来得及接管。TCP 采纳大小可变的 滑动窗口 进行流量管制,窗口大小的单位是字节。这里说的窗口大小其实就是每次传输的数据大小。
- 当一个连贯建设时,连贯的每一端调配一个缓冲区来保留输出的数据,并将缓冲区的大小发送给另一端。
- 当数据达到时,接管方发送确认,其中蕴含了本人残余的缓冲区大小。(残余的缓冲区空间的大小被称为窗口,指出窗口大小的告诉称为窗口通告。接管方在发送的每一确认中都含有一个窗口通告。)
- 如果接管方应用程序读数据的速度可能与数据达到的速度一样快,接管方将在每一确认中发送一个正的窗口通告。
- 如果发送方操作的速度快于接管方,接管到的数据最终将充斥接管方的缓冲区,导致接管方通告一个零窗口。发送方收到一个零窗口通告时,必须进行发送,直到接管方从新通告一个正的窗口。
—- 问题知识点分割线 —-
Unicode、UTF-8、UTF-16、UTF-32 的区别?
(1)Unicode
在说 Unicode
之前须要先理解一下 ASCII
码:ASCII 码(American Standard Code for Information Interchange
)称为美国规范信息替换码。
- 它是基于拉丁字母的一套电脑编码零碎。
- 它定义了一个用于代表常见字符的字典。
- 它蕴含了 ”A-Z”(蕴含大小写),数据 ”0-9″ 以及一些常见的符号。
- 它是专门为英语而设计的,有 128 个编码,对其余语言无能为力
ASCII
码能够示意的编码无限,要想示意其余语言的编码,还是要应用 Unicode
来示意,能够说 Unicode
是ASCII
的超集。
Unicode
全称 Unicode Translation Format
,又叫做对立码、万国码、繁多码。Unicode
是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了对立并且惟一的二进制编码,以满足跨语言、跨平台进行文本转换、解决的要求。
Unicode
的实现形式(也就是编码方式)有很多种,常见的是 UTF-8、UTF-16、UTF-32 和USC-2。
(2)UTF-8
UTF-8
是应用最宽泛的 Unicode
编码方式,它是一种可变长的编码方式,能够是 1—4 个字节不等,它能够齐全兼容 ASCII
码的 128 个字符。
留神: UTF-8
是一种编码方式,Unicode
是一个字符汇合。
UTF-8
的编码规定:
- 对于 单字节 的符号,字节的第一位为 0,前面的 7 位为这个字符的
Unicode
编码,因而对于英文字母,它的Unicode
编码和ACSII
编码一样。 - 对于 n 字节 的符号,第一个字节的前 n 位都是 1,第 n + 1 位设为 0,前面字节的前两位一律设为 10,剩下的没有提及的二进制位,全副为这个符号的
Unicode
码。
来看一下具体的 Unicode
编号范畴与对应的 UTF-8
二进制格局:
编码范畴(编号对应的十进制数) | 二进制格局 |
---|---|
0x00—0x7F(0-127) | 0xxxxxxx |
0x80—0x7FF(128-2047) | 110xxxxx 10xxxxxx |
0x800—0xFFFF(2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000—0x10FFFF(65536 以上) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那该如何通过具体的 Unicode
编码,进行具体的 UTF-8
编码呢?步骤如下:
- 找到该
Unicode
编码的所在的编号范畴,进而找到与之对应的二进制格局 - 将
Unicode
编码转换为二进制数(去掉最高位的 0) - 将二进制数从右往左一次填入二进制格局的
X
中,如果有X
未填,就设为 0
来看一个理论的例子:
“马 ”字的Unicode
编码是:0x9A6C
,整数编号是39532
(1)首选确定了该字符在第三个范畴内,它的格局是 1110xxxx 10xxxxxx 10xxxxxx
(2)39532 对应的二进制数为1001 1010 0110 1100
(3)将二进制数填入 X 中,后果是:11101001 10101001 10101100
(3)UTF-16
1. 立体的概念
在理解 UTF-16
之前,先看一下 立体 的概念:Unicode
编码中有很多很多的字符,它并不是一次性定义的,而是分区进行定义的,每个区寄存 65536(216)个字符,这称为一个 立体,目前总共有 17 个立体。
最后面的一个立体称为 根本立体 ,它的码点从0 — 216-1,写成 16 进制就是U+0000 — U+FFFF
,那剩下的 16 个立体就是 辅助立体,码点范畴是 U+10000—U+10FFFF
。
2. UTF-16 概念:
UTF-16
也是 Unicode
编码集的一种编码模式,把 Unicode
字符集的形象码位映射为 16 位长的整数(即码元)的序列,用于数据存储或传递。Unicode
字符的码位须要 1 个或者 2 个 16 位长的码元来示意,因而 UTF-16
也是用变长字节示意的。
3. UTF-16 编码规定:
- 编号在
U+0000—U+FFFF
的字符(罕用字符集),间接用两个字节示意。 - 编号在
U+10000—U+10FFFF
之间的字符,须要用四个字节示意。
4. 编码辨认
那么问题来了,当遇到两个字节时,怎么晓得是把它当做一个字符还是和前面的两个字节一起当做一个字符呢?
UTF-16
编码必定也思考到了这个问题,在根本立体内,从 U+D800 — U+DFFF
是一个空段,也就是说这个区间的码点不对应任何的字符,因而这些空段就能够用来映射辅助立体的字符。
辅助立体共有 220 个字符位,因而示意这些字符至多须要 20 个二进制位。UTF-16
将这 20 个二进制位分成两半,前 10 位映射在 U+D800 — U+DBFF
,称为 高位 (H),后 10 位映射在 U+DC00 — U+DFFF
,称为 低位(L)。这就相当于,将一个辅助立体的字符拆成了两个根本立体的字符来示意。
因而,当遇到两个字节时,发现它的码点在 U+D800 —U+DBFF
之间,就能够晓得,它前面的两个字节的码点应该在 U+DC00 — U+DFFF
之间,这四个字节必须放在一起进行解读。
5. 举例说明
以 “𡠀” 字为例,它的 Unicode
码点为 0x21800
,该码点超出了根本立体的范畴,因而须要用四个字节来示意,步骤如下:
- 首先计算超出局部的后果:
0x21800 - 0x10000
- 将下面的计算结果转为 20 位的二进制数,有余 20 位就在后面补 0,后果为:
0001000110 0000000000
- 将失去的两个 10 位二进制数别离对应到两个区间中
U+D800
对应的二进制数为1101100000000000
,将0001000110
填充在它的后 10 个二进制位,失去1101100001000110
,转成 16 进制数为0xD846
。同理,低位为0xDC00
,所以这个字的UTF-16
编码为0xD846 0xDC00
(4)UTF-32
UTF-32
就是字符所对应编号的整数二进制模式,每个字符占四个字节,这个是间接进行转换的。该编码方式占用的贮存空间较多,所以应用较少。
比方“马”字的 Unicode 编号是:U+9A6C
,整数编号是39532
,间接转化为二进制:1001 1010 0110 1100
,这就是它的 UTF-32 编码。
(5)总结
Unicode、UTF-8、UTF-16、UTF-32 有什么区别?
Unicode
是编码字符集(字符集),而UTF-8
、UTF-16
、UTF-32
是字符集编码(编码规定);UTF-16
应用变长码元序列的编码方式,相较于定长码元序列的UTF-32
算法更简单,甚至比同样是变长码元序列的UTF-8
也更为简单,因为其引入了独特的 代理对 这样的代理机制;UTF-8
须要判断每个字节中的结尾标记信息,所以如果某个字节在传送过程中出错了,就会导致前面的字节也会解析出错;而UTF-16
不会判断结尾标记,即便错也只会错一个字符,所以容错能力教强;- 如果字符内容全副英文或英文与其余文字混合,但英文占绝大部分,那么用
UTF-8
就比UTF-16
节俭了很多空间;而如果字符内容全副是中文这样相似的字符或者混合字符中中文占绝大多数,那么UTF-16
就占优势了,能够节俭很多空间;
—- 问题知识点分割线 —-
协商缓存和强缓存的区别
(1)强缓存
应用强缓存策略时,如果缓存资源无效,则间接应用缓存资源,不用再向服务器发动申请。
强缓存策略能够通过两种形式来设置,别离是 http 头信息中的 Expires 属性和 Cache-Control 属性。
(1)服务器通过在响应头中增加 Expires 属性,来指定资源的过期工夫。在过期工夫以内,该资源能够被缓存应用,不用再向服务器发送申请。这个工夫是一个相对工夫,它是服务器的工夫,因而可能存在这样的问题,就是客户端的工夫和服务器端的工夫不统一,或者用户能够对客户端工夫进行批改的状况,这样就可能会影响缓存命中的后果。
(2)Expires 是 http1.0 中的形式,因为它的一些毛病,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更准确的管制。它有很多不同的值,
Cache-Control
可设置的字段:
public
:设置了该字段值的资源示意能够被任何对象(包含:发送申请的客户端、代理服务器等等)缓存。这个字段值不罕用,个别还是应用 max-age= 来准确管制;private
:设置了该字段值的资源只能被用户浏览器缓存,不容许任何代理服务器缓存。在理论开发当中,对于一些含有用户信息的 HTML,通常都要设置这个字段值,防止代理服务器 (CDN) 缓存;no-cache
:设置了该字段须要先和服务端确认返回的资源是否产生了变动,如果资源未发生变化,则间接应用缓存好的资源;no-store
:设置了该字段示意禁止任何缓存,每次都会向服务端发动新的申请,拉取最新的资源;max-age=
:设置缓存的最大有效期,单位为秒;s-maxage=
:优先级高于 max-age=,仅实用于共享缓存(CDN),优先级高于 max-age 或者 Expires 头;max-stale[=]
:设置了该字段表明客户端违心接管曾经过期的资源,然而不能超过给定的工夫限度。
一般来说只须要设置其中一种形式就能够实现强缓存策略,当两种形式一起应用时,Cache-Control 的优先级要高于 Expires。
no-cache 和 no-store 很容易混同:
- no-cache 是指先要和服务器确认是否有资源更新,在进行判断。也就是说没有强缓存,然而会有协商缓存;
- no-store 是指不应用任何缓存,每次申请都间接从服务器获取资源。
(2)协商缓存
如果命中强制缓存,咱们无需发动新的申请,间接应用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
下面曾经说到了,命中协商缓存的条件有两个:
max-age=xxx
过期了- 值为
no-store
应用协商缓存策略时,会先向服务器发送一个申请,如果资源没有产生批改,则返回一个 304 状态,让浏览器应用本地的缓存正本。如果资源产生了批改,则返回批改后的资源。
协商缓存也能够通过两种形式来设置,别离是 http 头信息中的 Etag 和Last-Modified 属性。
(1)服务器通过在响应头中增加 Last-Modified 属性来指出资源最初一次批改的工夫,当浏览器下一次发动申请时,会在申请头中增加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当申请发送到服务器后服务器会通过这个属性来和资源的最初一次的批改工夫来进行比拟,以此来判断资源是否做了批改。如果资源没有批改,那么返回 304 状态,让客户端应用本地的缓存。如果资源曾经被批改了,则返回批改后的资源。应用这种办法有一个毛病,就是 Last-Modified 标注的最初批改工夫只能准确到秒级,如果某些文件在 1 秒钟以内,被批改屡次的话,那么文件已将扭转了然而 Last-Modified 却没有扭转,这样会造成缓存命中的不精确。
(2)因为 Last-Modified 的这种可能产生的不准确性,http 中提供了另外一种形式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中增加了 Etag 属性,这个属性是资源生成的惟一标识符,当资源产生扭转的时候,这个值也会产生扭转。在下一次资源申请时,浏览器会在申请头中增加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接管到申请后会依据这个值来和资源以后的 Etag 的值来进行比拟,以此来判断资源是否产生扭转,是否须要返回资源。通过这种形式,比 Last-Modified 的形式更加准确。
当 Last-Modified 和 Etag 属性同时呈现的时候,Etag 的优先级更高。应用协商缓存的时候,服务器须要思考负载平衡的问题,因而多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因而在思考负载平衡时,最好不要设置 Etag 属性。
总结:
强缓存策略和协商缓存策略在缓存命中时都会间接应用本地的缓存正本,区别只在于协商缓存会向服务器发送一次申请。它们缓存不命中时,都会向服务器发送申请来获取资源。在理论的缓存机制中,强缓存策略和协商缓存策略是一起单干应用的。浏览器首先会依据申请的信息判断,强缓存是否命中,如果命中则间接应用资源。如果不命中则依据头信息向服务器发动申请,应用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器间接应用本地资源的正本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
—- 问题知识点分割线 —-
懒加载与预加载的区别
这两种形式都是进步网页性能的形式,两者次要区别是一个是提前加载,一个是缓慢甚至不加载。懒加载对服务器前端有肯定的缓解压力作用,预加载则会减少服务器前端压力。
- 懒加载也叫提早加载,指的是在长网页中提早加载图片的机会,当用户须要拜访时,再去加载,这样能够进步网站的首屏加载速度,晋升用户的体验,并且能够缩小服务器的压力。它实用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的 src 属性设置为空字符串,将图片的实在门路保留在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出实在门路赋值给图片的 src 属性,以此来实现图片的提早加载。
- 预加载指的是将所需的资源提前申请加载到本地,这样前面在须要用到时就间接从缓存取资源。 通过预加载可能缩小用户的等待时间,进步用户的体验。我理解的预加载的最罕用的形式是应用 js 中的 image 对象,通过为 image 对象来设置 scr 属性,来实现图片的预加载。
—- 问题知识点分割线 —-
实现一个三角形
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;
}
还有很多,就不一一实现了,总体的准则就是通过上下左右边框来管制三角形的方向,用边框的宽度比来管制三角形的角度。
—- 问题知识点分割线 —-
异步任务调度器
形容:实现一个带并发限度的异步调度器 Scheduler,保障同时运行的工作最多有 limit
个。
实现:
class Scheduler {queue = []; // 用队列保留正在执行的工作
runCount = 0; // 计数正在执行的工作个数
constructor(limit) {this.maxCount = limit; // 容许并发的最大个数}
add(time, data){const promiseCreator = () => {return new Promise((resolve, reject) => {setTimeout(() => {console.log(data);
resolve();}, time);
});
}
this.queue.push(promiseCreator);
// 每次增加的时候都会尝试去执行工作
this.request();}
request() {
// 队列中还有工作才会被执行
if(this.queue.length && this.runCount < this.maxCount) {
this.runCount++;
// 执行先退出队列的函数
this.queue.shift()().then(() => {
this.runCount--;
// 尝试进行下一次工作
this.request();});
}
}
}
// 测试
const scheduler = new Scheduler(2);
const addTask = (time, data) => {scheduler.add(time, data);
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输入后果 2 3 1 4
—- 问题知识点分割线 —-
代码输入问题
window.number = 2;
var obj = {
number: 3,
db1: (function(){console.log(this);
this.number *= 4;
return function(){console.log(this);
this.number *= 5;
}
})()}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number); // 15
console.log(window.number); // 40
这道题目看清起来有点乱,然而实际上是考查 this 指向的:
- 执行 db1()时,this 指向全局作用域,所以 window.number 4 = 8,而后执行匿名函数,所以 window.number 5 = 40;
- 执行 obj.db1(); 时,this 指向 obj 对象,执行匿名函数,所以 obj.numer * 5 = 15。
—- 问题知识点分割线 —-
instance 如何应用
右边能够是任意值,左边只能是函数
'hello tuture' instanceof String // false