You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
另外小姐姐最近在看机会 base 北京,邮箱已经附在 GitHub 上了。欢迎有坑位的同学进行推荐。
一、算法
1. 全排列
微信公众号:世界上有意思的事
function permutate(str) {
var array = str.split('');
function loop(array, pre = []) {
if (array.length == 1) {
return [pre.concat(array).join('')];
}
let res = [];
for (let index = 0; index < array.length; index++) {
var first = array.pop();
res = res.concat(loop(array, [...pre, first]));
array.unshift(first);
}
return res;
}
return Array.from(new Set(loop(array)))
}
复制代码
2. 二分搜索
微信公众号:世界上有意思的事
function BinarySearch1 (arr, target) {
return search(arr, target, 0, arr.length - 1)
function search (arr, target, from, to) {
if (from > to) {
return -1
}
const mid = Math.floor((from + to)/2)
if (arr[mid] > target) {
return search(arr, target, from, mid - 1)
} else if (arr[mid] < target) {
return search(arr, target, mid + 1, to)
} else {
return mid
}
}
}
function BinarySearch2 (arr, target) {
let from = 0
let to = arr.length - 1
let mid = Math.floor((from + to)/2)
while (from <= to) {
mid = Math.floor((from + to)/2)
if (arr[mid] > target) {
to = mid - 1
} else if (arr[mid] < target) {
from = mid + 1
} else {
return mid
}
}
return -1
}
复制代码
3. 排序
(1). 冒泡排序
微信公众号:世界上有意思的事
function BubbleSort (arr) {
const length = arr.length
for (let i = 0; i < length; i++) {
for (let j = 1; j < length-i; j++) {
if (arr[j] < arr[j - 1]) {
const temp = arr[j]
arr[j] = arr[j - 1]
arr[j - 1] = temp
}
}
}
return arr
}
复制代码
(2). 快速排序
微信公众号:世界上有意思的事
function QuickSort(arr, low, high) {
let left = low
let right = high
let basic = arr[low]
while (left < right) {
while (left < right && arr[right] > basic) {
right--
}
while (left < right && arr[left] <= basic) {
left++
}
if (left < right) {
const temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
} else {
const temp = arr[low]
arr[low] = arr[left]
arr[left] = temp
QuickSort(arr, low, left - 1)
QuickSort(arr, right + 1, high)
}
}
return arr
}
复制代码
(3). 选择排序
微信公众号:世界上有意思的事
function SelectionSort (arr) {
const length = arr.length
for (let i = 0; i < length; i++ ) {
let minIndex= i
for (let j = i + 1; j < length; j++) {
minIndex = arr[minIndex] <= arr[j] ? minIndex : j
}
if (minIndex !== i) {
const temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
}
return arr
}
复制代码
(4). 插入排序
微信公众号:世界上有意思的事
function InsertionSort (arr) {
const length = arr.length
for (let i = 1; i < length; i++) {
const temp = arr[i]
let j
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
arr[j+1] = arr[j]
}
arr[j+1] = temp
}
return arr
}
复制代码
(5). 希尔排序
插入排序的改进版。对间隔 gap 为一组的数进行插入排序
微信公众号:世界上有意思的事
function ShellSort (arr) {
const length = arr.length
let gap = Math.floor(length)
while (gap) {
for (let i = gap; i < length; i++) {
const temp = arr[i]
let j
for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap) {
arr[j + gap] = arr[j]
}
arr[j + gap] = temp
}
gap = Math.floor(gap / 2)
}
return arr
}
复制代码
(6). 归并排序
微信公众号:世界上有意思的事
function MergeSort (arr, low, high) {
const length = arr.length
if (low === high) {
return arr[low]
}
const mid = Math.floor((low + high)/2)
MergeSort(arr, low, mid)
MergeSort(arr, mid + 1, high)
merge(arr, low, high)
return arr
}
function merge (arr, low, high) {
const mid = Math.floor((low + high)/2)
let left = low
let right = mid + 1
const result = []
while (left <= mid && right <= high) {
if (arr[left] <= arr[right]) {
result.push(arr[left++])
} else {
result.push(arr[right++])
}
}
while (left <= mid) {
result.push(arr[left++])
}
while (right <= high) {
result.push(arr[right++])
}
arr.splice(low, high-low+1, ...result)
}
const test = [2, 34, 452,3,5, 785, 32, 345, 567, 322,5]
console.log(MergeSort(test, 0, test.length - 1))
复制代码
(7). 堆排序
微信公众号:世界上有意思的事
function HeapSort (arr) {
const length = arr.length
for (let i = Math.floor(length/2) - 1; i >= 0; i--) {
adjustHeap(arr, i, length)
}
for (let i = length - 1; i >=0; i--) {
const temp = arr[0]
arr[0] = arr[i]
arr[i] = temp
adjustHeap(arr, 0, i)
}
return arr
}
function adjustHeap (arr, position, size) {
const left = position * 2 + 1
const right = left + 1
let maxIndex = position
if (left < size && arr[left] > arr[maxIndex]) {
maxIndex = left
}
if (right < size && arr[right] > arr[maxIndex]) {
maxIndex = right
}
if (maxIndex !== position) {
const temp = arr[position]
arr[position] = arr[maxIndex]
arr[maxIndex] = temp
adjustHeap(arr, maxIndex, size)
}
return arr
}
复制代码
微信公众号:世界上有意思的事
function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
复制代码
发送数据的一方拿到接收数据一方的 window:targetWindow.postMessage("Welcome to unixera.com", "http://iframe1.unixera.com");
(9).chilid 与 child 通信
有跨域问题,只适合站内不同子域间的通信(设置 document.domain 为同一级域名)
(10).Cross Frame
这是一个通用的方法,简单来说是 A iframe 包含 B iframe,在 B iframe 中调用了相关的接口,完成调用之后获取到结果,location.href到和 A iframe 位于同一个域的 C iframe,在 C iframe 中调用 A iframe 中定义的方法,将 B iframe 中获取的结果作为参数传到要跳转的 url 后,在 C iframe 中通过location.search变量来获取变量。
var array = [1, 1, '1', '1'];
function unique(array) {
var res = [];
for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
for (var j = 0, resLen = res.length; j < resLen; j++ ) {
if (array[i] === res[j]) {
break;
}
}
if (j === resLen) {
res.push(array[i])
}
}
return res;
}
console.log(unique(array));
复制代码
2.indexOf
原理和双重循环是一样的
var array = [1, 1, '1'];
function unique(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res;
}
console.log(unique(array));
复制代码
3. 排序后去重
对于排好序的数组,可以将每个元素与前一个比较
var array = [1, 1, '1'];
function unique(array) {
var res = [];
var sortedArray = array.concat().sort();
var seen;
for (var i = 0, len = sortedArray.length; i < len; i++) {
if (!i || seen !== sortedArray[i]) {
res.push(sortedArray[i])
}
seen = sortedArray[i];
}
return res;
}
console.log(unique(array));
复制代码
function unique(array) {
return Array.from(new Set(array));
}
复制代码
function unique(array) {
return [...new Set(array)];
}
复制代码
6.ES6 Map
function unique (arr) {
const seen = new Map()
return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}
复制代码
三、高级技巧
1. 防抖节流
(1). 节流
在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
(2). 防抖
按最后一次算。比如说 “停止输入 5s 后才发送请求”
3. 数组展开
1. 递归
微信公众号:世界上有意思的事
function flat1 (arr) {
let result = []
arr.forEach(element => {
if (Array.isArray(element)) {
result = result.concat(flat1(element))
} else {
result.push(element)
}
});
return result
}
复制代码
function sleep (ms) {
return new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}
sleep(1000).then(()=>{
console.log('已经 sleep 1000ms')
})
复制代码
function sleep (ms) {
return new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}
async function test () {
var example = await sleep(1000)
console.log('已经 sleep 1000ms')
}
复制代码
function *sleep (ms) {
yield new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}
sleep(1000).next().value.then(()=>{
console.log('已经 sleep 1000ms')
})
复制代码
四、浏览器
1. 缓存
(1). 按缓存位置分
1.Service Worker
1. 有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制,被浏览器全部清空。
2. 如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。注意:经过 Service Worker 的 fetch() 方法获取的资源,即便它并没有命中 Service Worker 缓存,甚至实际走了网络请求,也会标注为 from ServiceWorker。
1. 因为是双方彼此都建立了连接,因此双方都要释放自己的连接,A 向 B 发出一个释放连接请求,他要释放链接表明不再向 B 发送数据了,此时 B 收到了 A 发送的释放链接请求之后,给 A 发送一个确认,A 不能再向 B 发送数据了,它处于 FIN-WAIT-2 的状态,但是此时 B 还可以向 A 进行数据的传送。此时 B 向 A 发送一个断开连接的请求,A 收到之后给 B 发送一个确认。此时 B 关闭连接。A 也关闭连接。
2. 为什么要有 TIME-WAIT 这个状态呢,这是因为有可能最后一次确认丢失,如果 B 此时继续向 A 发送一个我要断开连接的请求等待 A 发送确认,但此时 A 已经关闭连接了,那么 B 永远也关不掉了,所以我们要有 TIME-WAIT 这个状态。
前端小姐姐的 GitHub
另外小姐姐最近在看机会 base 北京,邮箱已经附在 GitHub 上了。欢迎有坑位的同学进行推荐。
一、算法
1. 全排列
2. 二分搜索
3. 排序
(1). 冒泡排序
(2). 快速排序
(3). 选择排序
(4). 插入排序
(5). 希尔排序
插入排序的改进版。对间隔 gap 为一组的数进行插入排序
(6). 归并排序
(7). 堆排序
二、JS 基础
1. 继承
2.this 指向
(1).this 指向有哪几种
1. 默认绑定:全局环境中,this 默认绑定到 window。
2. 隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this 隐式绑定到该直接对象。
3. 隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到 window。显式绑定:通过 call()、apply()、bind() 方法把对象绑定到 this 上,叫做显式绑定。
4.new 绑定:如果函数或者方法调用之前带有关键字 new,它就构成构造函数调用。对于 this 绑定来说,称为 new 绑定。
(2). 改变函数内部 this 指针的指向函数(bind,apply,call 的区别)
(3). 箭头函数
3. 数据类型
(1). 基本数据类型
Undefined、Null、Boolean、Number 、String、Symbol
(2).symbol
1. 语法:
2. 作用:定义一个独一无二的值
1. 用作对象的属性名
for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。Object.getOwnPropertySymbols()
方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。Reflect.ownKeys()
方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。2. 用于定义一组常量
3. 类型转换:
1. 转成字符串
2. 转成布尔值
3. 不能转成数字
4. 不能与其他类型的值进行运算
4. 属性:Symbol.prototype.description
5.Symbol.for(),Symbol.keyFor()
(3). 如何判断类型
typeof(),instanceof,Object.prototype.toString.call()
1.
typeof
操作符2.
instanceof
:用来判断对象是不是某个构造函数的实例。会沿着原型链找的3.
Object.prototype.toString.call()
(4). 判断是否是数组
Array.isArray(arr)
Object.prototype.toString.call(arr) === '[Object Array]'
arr instanceof Array
array.constructor === Array
(5). 字符串转数字
parseInt(string, radix)
4.CallBack Hell
大脑对于事情的计划方式是线性的、阻塞的、单线程的语义,但是回调表达异步流 程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大。难于理解的代码 是坏代码,会导致坏 bug。我们需要一种更同步、更顺序、更阻塞的的方式来表达异步,就像我们的大脑一样。
也是更重要的一点,回调会受到控制反转的影响,因为回调暗中把控制权交给第三 方 (通常是不受你控制的第三方工具!) 来调用你代码中的 continuation。可以发明一些特定逻辑来解决这些信任问题,但是其难度高于应有的水平,可能会产生更 笨重、更难维护的代码,并且缺少足够的保护,其中的损害要直到你受到 bug 的影响才会 被发现。
我们需要一个通用的方案来解决这些信任问题。不管我们创建多少回调,这一方案都应可 以复用,且没有重复代码的开销。
(1).Promise 为什么以及如何用于解决控制反转信任问题
Promise 的实现可以看这里
Promise 这种模式通过可信任的语义把回调作为参数传递,使得这种行为更可靠更合理。 通过把回调的控制反转反转回来,我们把控制权放在了一个可信任的系统 (Promise) 中, 这种系统的设计目的就是为了使异步编码更清晰。Promise 并没有摈弃回调,只是把回调的安排转交给了一个位于我们和其他工具之间的可信任 的中介机制。
调用回调过早;
这个问题主要就是担心代码是否会引入类似 Zalgo 这样的副作用 (参见第 2 章)。在这类问 题中,一个任务有时同步完成,有时异步完成,这可能会导致竞态条件。
根据定义,Promise 就不必担心这种问题,因为即使是立即完成的 Promise(类似于 new Promise(function(resolve){ resolve(42); })) 也无法被同步观察到。
也就是说,对一个 Promise 调用 then(..) 的时候,即使这个 Promise 已经决议,提供给 then(..) 的回调也总会被异步调用 (对此的更多讨论,请参见 1.5 节)。
调用回调过晚 (或不被调用);
回调未调用
调用回调次数过多;
未能传递所需的环境和参数;
Promise 至多只能有一个决议值 (完成或拒绝)。
如果你没有用任何值显式决议,那么这个值就是 undefined,这是 JavaScript 常见的处理方 式。但不管这个值是什么,无论当前或未来,它都会被传给所有注册的 (且适当的完成或 拒绝) 回调。
吞掉可能出现的错误和异常。
(2).promise、generator、async/await
promise
generator
async/await
5. 加载
(1). 异步加载 js 的方法
<script>
标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。(2). 图片的懒加载和预加载
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
6. 事件
(1). 事件流
HTML 中与 javascript 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面的滚动事件 onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下 “事件流” 的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序, DOM2 级事件流包括下面几个阶段。
IE 只支持事件冒泡。
(2). 什么是事件监听
addEventListener()
方法,用于向指定元素添加事件句柄,它可以更简单的控制事件,语法为element.addEventListener(event, function, useCapture)
;第一个参数是事件的类型 (如 "click" 或 "mousedown").
第二个参数是事件触发后调用的函数。
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
target.addEventListener(type, listener, options: EventListenerOptions);
target.addEventListener(type, listener, useCapture: boolean);
target.addEventListener(type, listener, useCapture: boolean, wantsUntrusted: boolean ); // Gecko/Mozilla only
复制代码
interface EventListenerOptions {
capture?: boolean
once?: boolean
passive?: boolean
}
复制代码
(3). mouseover 和 mouseenter 的区别
(4). 事件委托以及冒泡原理
简介:事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素 DOM 的类型,来做出不同的响应。
举例:最经典的就是 ul 和 li 标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在 li 标签上直接添加,而是在 ul 父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
(5). 事件代理在捕获阶段的实际应用
可以在父元素层面阻止事件向子元素传播,也可代替子元素执行某些操作。
7. 跨域
(1).CORS
CORS(Cross-Origin Resource Sharing,跨源资源共享) 背后的基本思想,就是使用自定义的 HTTP 头部 让浏览器与服务器进行沟通。
比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在 发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息 (协议、域名和端 口),以便服务器根据这个头部信息来决定是否给予响应。下面是 Origin 头部的一个示例:
Origin: http://www.nczonline.net
如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息 (如果是公共资源,可以回发 "*")。例如:
Access-Control-Allow-Origin: http://www.nczonline.net
如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器 会处理请求。注意,请求和响应都不包含 cookie 信息。
(2).IE
微软在 IE8 中引入了 XDR(XDomainRequest) 类型。以下是 XDR 与 XHR 的一些不同之 处。
(3). 其他浏览器
通过 XMLHttpRequest 对象实现了对 CORS 的原生支持
(4).JSONP
(5). 代理
起一个代理服务器,实现数据的转发
(6). 利用 iframe
lovelock.coding.me/javascript/…
(7).window.postMessage
只支持到 IE8 及以上的 IE 浏览器,其他现代浏览器当然没有问题。
(8). child 与 parent 通信
不受同源策略的限制
addEventListener('message', receiveMessage);
targetWindow.postMessage("Welcome to unixera.com", "http://iframe1.unixera.com");
(9).chilid 与 child 通信
有跨域问题,只适合站内不同子域间的通信(设置 document.domain 为同一级域名)
(10).Cross Frame
这是一个通用的方法,简单来说是 A iframe 包含 B iframe,在 B iframe 中调用了相关的接口,完成调用之后获取到结果,
location.href
到和 A iframe 位于同一个域的 C iframe,在 C iframe 中调用 A iframe 中定义的方法,将 B iframe 中获取的结果作为参数传到要跳转的 url 后,在 C iframe 中通过location.search
变量来获取变量。(11).window.name
window
对象的name
属性是一个很特殊的属性,在设定了window.name
之后,执行location.href
跳转,window.name
属性仍然不会发生变化,可以通过这种方式实现变量的传递。8.Ajax
(1). 实现一个 Ajax
(2).Ajax 状态
(3). 将原生的 ajax 封装成 promise
9. 垃圾回收
找出那些不再继续使用的变 量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔 (或代码执行中预定的收集时间), 周期性地执行这一操作。
(1). 标记清除
先所有都加上标记,再把环境中引用到的变量去除标记。剩下的就是没用的了
(2). 引用计数
跟踪记录每 个值被引用的次数。清除引用次数为 0 的变量⚠️ 会有循环引用问题 。循环引用如果大量存在就会导致内存泄露。
10.eval 是什么
eval 方法就像是一个完整的 ECMAScript 解析器,它只接受一个参数,即要执行的 ECMAScript (或 JavaScript) 字符串
11. 监听对象属性的改变
(一).ES5 中
(二). ES6 中
可以监听动态增加的属性。例如
user.id = 1
12. 实现一个私有变量
1. 配置属性
2. 代码
微信公众号:世界上有意思的事
function product(){
var name='xujiahui';
this.getName=function(){
return name;
}
}
var obj=new product();
复制代码
13. 操作符
(1).
==
和===
、以及Object.is
的区别1.
==
1. 会进行强制类型转换(!= 也是)
2. 在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
2.
===
:全等于,不转换3.
Object.is
===
有以下几点不同:+0===-0
,Object.is(+0, -0)
为 falseNaN !== NaN
,Object.is(NaN, NaN)
为 true(2).new 操作符做了哪些事情
用 new 操作符调用构造函数实际上会经历以下 4 个步骤:
14. 数组
(1). 数组常用方法
push(),pop(),shift(),unshift(),splice(),sort(),reverse(),map() 等
(2). 数组去重
要注意的是对象咋去重
1. 双重循环
每次插入一个元素的时候都和前面的每个元素比较一下
2.
indexOf
原理和双重循环是一样的
3. 排序后去重
对于排好序的数组,可以将每个元素与前一个比较
4.Object 键值对
把每一个元素存成 object 的 key。例如
['a']
,存成{'a': true}
我们可以发现,是有问题的,因为 1 和 '1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以使用
typeof item + item
拼成字符串作为 key 值来避免这个问题:然而,即便如此,我们依然无法正确区分出两个对象,比如 {value: 1} 和 {value: 2},因为
typeof item + item
的结果都会是object[object Object]
,不过我们可以使用 JSON.stringify 将对象序列化:5.ES6 Set 去重
6.ES6 Map
三、高级技巧
1. 防抖节流
(1). 节流
在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
(2). 防抖
按最后一次算。比如说 “停止输入 5s 后才发送请求”
3. 数组展开
1. 递归
微信公众号:世界上有意思的事
function flat1 (arr) {
let result = []
arr.forEach(element => {
if (Array.isArray(element)) {
result = result.concat(flat1(element))
} else {
result.push(element)
}
});
return result
}
复制代码
2.toString
function flat2 (arr) {
}
复制代码
3.reduce
微信公众号:世界上有意思的事
function flat3 (arr) {
}
复制代码
4.rest 运算符
微信公众号:世界上有意思的事
function flat4 (arr) {
while (arr.some(item => Array.isArray(item))) {
}
复制代码
5.ES6 flat
微信公众号:世界上有意思的事
function flat5 (arr: any[]) {
}
复制代码
4. 拖放
1.DragDrop 对象封装了拖放的所有基本功能。这是一个单例对象,并使用了模块模式来隐藏某些实 现细节。dragging 变量起初是 null,将会存放被拖动的元素,所以当该变量不为 null 时,就知道正 在拖动某个东西。handleEvent() 函数处理拖放功能中的所有的三个鼠标事件。它首先获取 event 对 象和事件目标的引用。之后,用一个 switch 语句确定要触发哪个事件样式。当 mousedown 事件发生 时,会检查 target 的 class 是否包含 "draggable" 类,如果是,那么将 target 存放到 dragging 中。这个技巧可以很方便地通过标记语言而非 JavaScript 脚本来确定可拖动的元素。
2.handleEvent() 的 mousemove 情况和前面的代码一样,不过要检查 dragging 是否为 null。当 它不是 null,就知道 dragging 就是要拖动的元素,这样就会把它放到恰当的位置上。mouseup 情况 就仅仅是将 dragging 重置为 null,让 mousemove 事件中的判断失效。
3.DragDrop 还有两个公共方法: enable() 和 disable(),它们只是相应添加和删除所有的事件处 理程序。这两个函数提供了额外的对拖放功能的控制手段。
4. 要使用 DragDrop 对象,只要在页面上包含这些代码并调用 enable()。拖放会自动针对所有包含 "draggable" 类的元素启用,如下例所示:
注意为了元素能被拖放,它必须是绝对定位的。
5.once
6.promise
Promise
是一个对象,保存着未来将要结束的事件,她有两个特征:Promise
对象代表一个异步操作,有三种状态,pending 进行中,fulfilled 已成功,rejected 已失败,只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也就是 promise 名字的由来Promise
对象状态改变只有两种可能,从 pending 改到 fulfilled 或者从 pending 改到 rejected,只要这两种情况发生,状态就凝固了,不会再改变,这个时候就称为定型 resolved7.sleep
用
Promise
四、浏览器
1. 缓存
(1). 按缓存位置分
cache.delete(resource)
或者容量超过限制,被浏览器全部清空。fetch()
方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。注意:经过 Service Worker 的fetch()
方法获取的资源,即便它并没有命中 Service Worker 缓存,甚至实际走了网络请求,也会标注为from ServiceWorker
。src
相同的image
,两个href
相同的link
) 都实际只会被请求最多一次,避免浪费。max-age=0
,no-cache
等头部配置。例如页面上存在几个相同src
的图片,即便它们可能被设置为不缓存,但依然会从 memory cache 中读取。这是因为 memory cache 只是短期使用,大部分情况生命周期只有一次浏览而已。而max-age=0
在语义上普遍被解读为 “不要在下次浏览时使用”,所以和 memory cache 并不冲突。no-store
。存在这个头部配置的话,即便是 memory cache 也不会存储,自然也不会从中读取了。Cache-control
,Pragma
等) 决定是否存入 disk cache(2). 按失效策略分
强制缓存 (也叫强缓存)
强制缓存直接减少请求数,是提升最大的缓存策略。 它的优化覆盖了请求、处理、响应三个阶段
可以造成强制缓存的字段是
Cache-control
和Expires
。Expires:
Cache-control
HTTP1.1
优先级高
max-age
:即最大有效时间must-revalidate
:如果超过了max-age
的时间,浏览器必须向服务器发送请求,验证资源是否还有效。no-cache
:虽然字面意思是 “不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。no-store
: 真正意义上的 “不要缓存”。所有内容都不走缓存,包括强制和对比。public
:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)private
:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。对比缓存 (协商缓存)
对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
Last-Modified
字段告知客户端,资源最后一次被修改的时间Last-Modified
的值写入到请求头的If-Modified-Since
字段If-Modified-Since
的值与Last-Modified
字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。Etag
存储的是文件的特殊标识 (一般都是 hash 生成的),服务器存储着文件的Etag
字段。Last-Modified
一致,只是Last-Modified
字段和它所表示的更新时间改变成了Etag
字段和它所表示的文件 hash,把If-Modified-Since
变成了If-None-Match
。(3).Ajax 解决浏览器缓存问题
2. 浏览器渲染原理
(1).Render Tree
display: none
)的元素不会被生成RenderTree
,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置(布局),最后把节点绘制到页面上(绘制)。Render Tree
的计算通常只需要遍历一次就可以完成,但table
及其内部元素除外,他们可能需要多次计算,通常要花 3 倍于同等元素的时间,这也是为什么要避免使用table
布局的原因之一。(2). 重绘
由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如
outline
,visibility
,color
、background-color
等,重绘的代价是高昂的,因为浏览器必须验证 DOM 树上其他节点元素的可见性。(3) 回流
回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及 DOM 中紧随其后的节点、祖先节点元素的随后的回流。
(4). 浏览器优化
现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即 16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
主要包括以下属性或方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
width
、height
getComputedStyle()
getBoundingClientRect()
所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。
(5). 减少重绘与回流
1.CSS
2.使用
transform
替代top
3.使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局4.避免使用
table
布局,可能很小的一个小改动会造成整个table
的重新布局。5.尽可能在
DOM
树的最末端改变class
,回流是不可避免的,但可以减少其影响。尽可能在 DOM 树的最末端改变 class,可以限制了回流的范围,使其影响尽可能少的节点。6.避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的
span
标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的span
标签,然后找到span
标签上的a
标签,最后再去找到div
标签,然后给符合这种条件的span
标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。7.将动画效果应用到
position
属性为absolute
或fixed
的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择requestAnimationFrame
,详见探讨 requestAnimationFrame。8.避免使用
CSS
表达式,可能会引发回流。9.将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如
will-change
、video
、iframe
等标签,浏览器会自动将该节点变为图层。10.CSS3 硬件加速(GPU 加速),使用 css3 硬件加速,可以让
transform
、opacity
、filters
这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color
这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。2.JavaScript
style
属性,或者将样式列表定义为class
并一次性更改class
属性。DOM
,创建一个documentFragment
,在它上面应用所有DOM 操作
,最后再把它添加到文档中。(6).JS 什么时候解析?
<script>
<script async>
<script defer>
五、计算机基础
1. 计算机网络
(1).TCP 三次握手
(2).CDN 原理
CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应
(4).DNS 解析
(5).HTTP 常用请求头
可以将 http 首部分为通用首部,请求首部,响应首部,实体首部
(5).OSI 七层模型
应用层:文件传输,常用协议 HTTP,snmp,FTP ,
表示层:数据格式化,代码转换,数据加密,
会话层:建立,解除会话
传输层:提供端对端的接口,tcp,udp
网络层:为数据包选择路由,IP,icmp
数据链路层:传输有地址的帧
物理层:二进制的数据形式在物理媒体上传输数据
(5).TCP 和 UDP 的区别
(6). 为什么三次握手四次挥手
(7).websocket 和 ajax 的区别是什么,websocket 的应用场景有哪些
WebSocket 的诞生本质上就是为了解决 HTTP 协议本身的单向性问题:请求必须由客户端向服务端发起,然后服务端进行响应。这个 Request-Response 的关系是无法改变的。对于一般的网页浏览和访问当然没问题,一旦我们需要服务端主动向客户端发送消息时就麻烦了,因为此前的 TCP 连接已经释放,根本找不到客户端在哪。 为了能及时从服务器获取数据,程序员们煞费苦心研究出来的各种解决方案其实都是在 HTTP 框架下做的妥协,没法子,浏览器这东西只支持 HTTP,我们有什么办法。所以大家要么定时去轮询,要么就靠长连接——客户端发起请求,服务端把这个连接攥在手里不回复,等有消息了再回,如果超时了客户端就再请求一次——其实大家也懂,这只是个减少了请求次数、实时性更好的轮询,本质没变。
WebSocket 就是从技术根本上解决这个问题的:看名字就知道,它借用了 Web 的端口和消息头来创建连接,后续的数据传输又和基于 TCP 的 Socket 几乎完全一样,但封装了好多原本在 Socket 开发时需要我们手动去做的功能。比如原生支持 wss 安全访问(跟 https 共用端口和证书)、创建连接时的校验、从数据帧中自动拆分消息包等等。
换句话说,原本我们在浏览器里只能使用 HTTP 协议,现在有了 Socket,还是个更好用的 Socket。
了解了 WebSocket 的背景和特性之后,就可以回答它能不能取代 AJAX 这个问题了:
对于服务器与客户端的双向通信,WebSocket 简直是不二之选。如果不是还有少数旧版浏览器尚在服役的话,所有的轮询、长连接等方式早就该废弃掉。那些整合多种双向推送消息方式的库(如http://Socket.IO、SignalR)当初最大的卖点就是兼容所有浏览器版本,自动识别旧版浏览器并采取不同的连接方式,现在也渐渐失去了优势——所有新版浏览器都兼容 WebSocket,直接用原生的就行了。说句题外话,这点很像 jQuery,在原生 js 难用时迅速崛起,当其他库和原生 js 都吸收了它的很多优势时,慢慢就不那么重要了。但是,很大一部分 AJAX 的使用场景仍然是传统的请求 - 响应形式,比如获取 json 数据、post 表单之类。这些功能虽然靠 WebSocket 也能实现,但就像在原本传输数据流的 TCP 之上定义了基于请求的 HTTP 协议一样,我们也要在 WebSocket 之上重新定义一种新的协议,最少也要加个 request id 用来区分每次响应数据对应的请求吧。
…… 但是,何苦一层叠一层地造个新轮子呢?直接使用 AJAX 不是更简单、更成熟吗?
另外还有一种情况,也就是传输大文件、图片、媒体流的时候,最好还是老老实实用 HTTP 来传。如果一定要用 WebSocket 的话,至少也专门为这些数据专门开辟个新通道,而别去占用那条用于推送消息、对实时性要求很强的连接。否则会把串行的 WebSocket 彻底堵死的。
所以说,WebSocket 在用于双向传输、推送消息方面能够做到灵活、简便、高效,但在普通的 Request-Response 过程中并没有太大用武之地,比起普通的 HTTP 请求来反倒麻烦了许多,甚至更为低效。
每项技术都有自身的优缺点,在适合它的地方能发挥出最大长处,而看到它的几个优点就不分场合地全方位推广的话,可能会适得其反。
我们自己在开发能与手机通信的互联网机器人时就使用了 WebSocket,效果很好。但并不是用它取代 HTTP,而是取代了原先用于通信的基于 TCP 的 Socket。
优点是:
原先在 Socket 连接后还要进行一些复杂的身份验证,同时要阻止未验证的连接发送控制指令。现在不需要了,在建立 WebSocket 连接的 url 里就能携带身份验证参数,验证不通过可以直接拒绝,不用设置状态;
原先自己实现了一套类似 SSL 的非对称加密机制,现在完全不需要了,直接通过 wss 加密,还能顺便保证证书的可信性;
原先要自己定义 Socket 数据格式,设置长度与标志,处理粘包、分包等问题,现在 WebSocket 收到的直接就是完整的数据包,完全不用自己处理;
前端的 nginx 可以直接进行转发与负载均衡,部署简单多了
(8).TCP/IP 的网络模型
2.HTTP 协议
(1). 常见的请求方法
HTTP 1.0
HTTP1.1
HTTP2.0
1.OPTIONS: 用于获取目的资源所支持的通信选项,比如说服务器支持的请求方式等等。
2.TRACE:实现沿通向目标资源的路径的消息环回(loop-back)测试 ,提供了一种实用的 debug 机制。
3.CONNECT
CONNECT
方法可以开启一个客户端与所请求资源之间的双向沟通的通道。它可以用来创建隧道(tunnel)。例如,CONNECT
可以用来访问采用了 SSL (HTTPS) 协议的站点。客户端要求代理服务器将 TCP 连接作为通往目的主机隧道。之后该服务器会代替客户端与目的主机建立连接。连接建立好之后,代理服务器会面向客户端发送或接收 TCP 消息流。所有通用服务器必须支持 GET 和 HEAD 方法。所有其他方法都是可选的。
(2).GET 和 POST 的区别
(3).HTTP 状态码
(4).301 和 302 有什么具体区别
3. 操作系统
(1). 进程和线程的区别
一个程序至少有一个进程,一个进程至少有一个线程,资源分配给进程,同一个进程下所有线程共享该进程的资源
(2). 线程的哪些资源共享,哪些资源不共享
1. 共享的资源有
2. 独享的资源有
(3). 进程间的通信方式有哪些
六、前端进阶
1.VUE
(1).vue 的生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 Dom、渲染→更新→渲染、销毁等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁。
(2).Vue 双向绑定原理
vue 数据双向绑定是通过数据劫持结合发布者 - 订阅者模式的方式来实现的。利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值 (get) 和设置属性值(set)。
2.Webpack
webpack 是一个现代 JavaScript 应用程序的静态模块打包器 (module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图 (dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
(1).webpack 和 gulp 区别
gulp 强调的是前端开发的工作流程,我们可以通过配置一系列的 task,定义 task 处理的事务(例如文件压缩合并、雪碧图、启动 server、版本控制等),然后定义执行顺序,来让 gulp 执行这些 task,从而构建项目的整个前端开发流程。
webpack 是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js 文件、css 文件等)都看成模块,通过 loader(加载器)和 plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。
3. 模块化
(1). 如何理解前端模块化
前端模块化就是复杂的文件编程一个一个独立的模块,比如 js 文件等等,分成独立的模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间相互依赖的问题,所以有了 commonJS 规范,AMD,CMD 规范等等,以及用于 js 打包(编译等处理)的工具 webpack
(2). 说一下 Commonjs、AMD 和 CMD
一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么功能就能加载什么模块。
1.Commonjs:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载 require() 引入模块。
2.AMD:中文名异步模块定义的意思。
1.require JS 实现了 AMD 规范
1. 主要用于解决下述两个问题。
2. 语法:requireJS 定义了一个函数 define,它是全局变量,用来定义模块。
2. 总结 AMD 规范:require() 函数在加载依赖函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块加载成功,才会去执行。因为网页在加载 js 的时候会停止渲染,因此我们可以通过异步的方式去加载 js, 而如果需要依赖某些,也是异步去依赖,依赖后再执行某些方法。
4. 简单实现 Node 的 Events 模块
简介:观察者模式或者说订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
node 中的 Events 模块就是通过观察者模式来实现的:
这样,eventEmitter 发出 say 事件,通过 On 接收,并且输出结果,这就是一个订阅模式的实现,下面我们来简单的实现一个 Events 模块的 EventEmitter。
1. 实现简单的 Event 模块的 emit 和 on 方法
2. 这样我们就定义了 Events,现在我们可以开始来调用:
3. 每个对象是独立的
因为是通过 new 的方式,每次生成的对象都是不相同的,因此:
5. 性能优化
七、尾巴
看到这里,是不是惊叹于小姐姐的实力了?赶紧点赞、评论、关注、分享吧!!
https://juejin.im/post/5e91b01651882573716a9b23?utm_source=gold_browser_extension
The text was updated successfully, but these errors were encountered: