loading

一些小知识

# 不使用file类型input触发文件上传

// 打开文件
window.showOpenFilePicker();
// 打开文件夹
window.showDirectoryPicker();
1
2
3
4

Safari浏览器并不支持,不能直接使用。

# JS重载

function fn() {
  switch (arguments.length) {
    case 1:
      var [name] = arguments
      console.log(`我是${name}`)
      break;
    case 2:
      var [name, age] = arguments
      console.log(`我是${name},今年${age}岁`)
      break;
    case 3:
      var [name, age, sport] = arguments
      console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`)
      break;
  }
}

// 高级做法
function addMethod(object, name, fn) {
  var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
  object[name] = function () { // 重写了object[name]的方法
    // 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
    if (fn.length === arguments.length) {
      return fn.apply(this, arguments);
      // 否则,判断old是否是函数,如果是,就调用old
    } else if (typeof old === "function") {
      return old.apply(this, arguments);
    }
  }
}

addMethod(window, 'fn', (name) => console.log(`我是${name}`))
addMethod(window, 'fn', (name, age) => console.log(`我是${name},今年${age}岁`))
addMethod(window, 'fn', (name, age, sport) => console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 关于 tcp

# ISN 是什么

ISN 全称是 Initial Sequence Number,是 TCP 发送方的字节数据编号的原点,告诉对方我要开始发送数据的初始化序列号

# ISN 是固定不变的吗

ISN 如果是固定的,攻击者很容易猜出后续的确认序号,为了安全起见,避免被第三方猜到从而发送伪造的 RST 报文,因此 ISN 是动态生成的

# 什么是半连接队列

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立连接。服务器会把这种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。 当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

# 二次握手可以吗

一)确认双方的收发能力 TCP 建立连接之前,需要确认客户端与服务器双方的收包和发包的能力。

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

所以,只有三次握手才能确认双方的接收与发送能力是否正常。 (二)序列号可靠同步 如果是两次握手,服务端无法确定客户端是否已经接收到了自己发送的初始序列号,如果第二次握手报文丢失,那么客户端就无法知道服务端的初始序列号,那 TCP 的可靠性就无从谈起。 (三)阻止重复历史连接的初始化 客户端由于某种原因发送了两个不同序号的 SYN 包,网络环境是复杂的,旧的数据包有可能先到达服务器。如果是两次握手,服务器收到旧的 SYN 就会立刻建立连接,那么会造成网络异常。 如果是三次握手,服务器需要回复 SYN+ACK 包,客户端会对比应答的序号,如果发现是旧的报文,就会给服务器发 RST 报文,直到正常的 SYN 到达服务器后才正常建立连接。 所以三次握手才有足够的上下文信息来判断当前连接是否是历史连接。 (四)安全问题 TCP 新建连接时,内核会为连接分配一系列的内存资源,如果采用两次握手,就建立连接,会放大 DDOS 攻击。 TCP 作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而三次握手恰好可以满足以上两方面的需求!

# 三次握手可以携带数据吗

  • 第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
  • 假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,疯狂着重复发 SYN 报文,这会让服务器花费大量的内存空间来缓存这些报文,这样服务器就更容易被攻击了。
  • 对于第三次握手,此时客户端已经处于连接状态,他已经知道服务器的接收、发送能力是正常的了,所以可以携带数据是情理之中。

# typeof null 为什么是object

null就出了一个 bug。根据 type tags 信息,低位是 000,因此 null被判断成了一个对象。这就是为什么 typeofnull的返回值是 "object"。

在 ES6 中曾有关于修复此 bug 的提议,提议中称应该让 typeofnull==='null' 但是该提议被无情的否决了,自此 typeofnull终于不再是一个 bug,而是一个 feature,并且永远不会被修复

# 0.1+0.2为什么不等于0.3

  • 计算机是通过二进制的方式存储数据的,所以计算机计算0.1+0.2的时候,实际上是计算的两个数的二进制的和。0.1的二进制是0.0001100110011001100...(1100循环),0.2的二进制是:0.00110011001100...(1100循环),这两个数的二进制都是无限循环的数。那JavaScript是如何处理无限循环的二进制小数呢?

  • 一般认为数字包括整数和小数,但是在 JavaScript 中只有一种数字类型:Number,它的实现遵循IEEE 754标准,使用64位固定长度来表示,也就是标准的double双精度浮点数。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留52位,再加上前面的1,其实就是保留53位有效数字,剩余的需要舍去,遵从“0舍1入”的原则。 根据这个原则,0.1和0.2的二进制数相加,再转化为十进制数就是:0.30000000000000004。

  • 解决方法:设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3

# RAF 和 RIC

  • requestAnimationFrame: 告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵 dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒 60 次。
  • requestIdleCallback:: 会在浏览器空闲时间执行回调,也就是允许开发人员在主事件循环中执行低优先级任务,而不影响一些延迟关键事件。如果有多个回调,会按照先进先出原则执行,但是当传入了 timeout,为了避免超时,有可能会打乱这个顺序

# escape、encodeURI、encodeURIComponent

  • encodeURI 是对整个 URI 进行转义,将 URI 中的非法字符转换为合法字符,所以对于一些在 URI 中有特殊意义的字符不会进行转义。
  • encodeURIComponent 是对 URI 的组成部分进行转义,所以一些特殊字符也会得到转义。
  • escape 和 encodeURI 的作用相同,不过它们对于 unicode 编码为 0xff 之外字符的时候会有区别,escape 是直接在字符的 unicode 编码前加上 %u,而 encodeURI 首先会将字符转换为 UTF-8 的格式,再在每个字节前加上 %。

# await 在等啥

await 表达式的运算结果取决于它等的是什么。

  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
function testAsy(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function testAwt(){    
  let result =  await testAsy('hello world');
  console.log(result);    // 3秒钟之后出现hello world
  console.log('cuger')   // 3秒钟之后出现cug
}
testAwt();
console.log('cug')  //立即输出cug

这是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。await暂停当前async的执行,所以'cug''最先输出,hello world'和‘cuger’是3秒钟后同时出现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# || 和 && 操作符的返回值

  • || 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。
  • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。
  • && 则相反,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。
  • || 和 && 返回它们其中一个操作数的值,而非条件判断的结果

# 2 == [[[2]]]

根据ES5规范,如果比较的两个值中有一个是数字类型,就会尝试将另外一个值强制转换成数字,再进行比较。而数组强制转换成数字的过程会先调用它的 toString方法转成字符串,然后再转成数字。所以 [2]会被转成 "2",然后递归调用,最终 [[[2]]] 会被转成数字 2。

# localStorage

  • localStorage 存储的键值采用什么字符编码:localStorage 存储的键和值始终采用 UTF-16 DOMString 格式,每个字符使用两个字节。与对象一样,整数键将自动转换为字符串。UTF-16,每个字符使用两个字节,是有前提条件的,就是码点小于0xFFFF(65535), 大于这个码点的是四个字节。

  • 5M 的单位:字符的长度 ,亦或 utf-16编码单元,10M 的字节数

  • 统计一个localStorage已使用空间

function sieOfLS() {
    return Object.entries(localStorage).map(v => v.join('')).join('').length;
}

1
2
3
4

# 同一个系统开两个网页,两个网页的sessionStorage共享吗?

不共享,localstorage 共享

# 数组的length是元素的个数,函数有length吗?length是什么?

函数的length就是第一个有默认值的参数之前的参数的个数,也可以说是必须要穿的形参的个数。

# 常见内置错误编码

1.ReferenceError:引用的变量不存在。 2.TypeErroe:数据类型不正确的错误。 3.RangeError:数据值不在其所允许的范围之内(超出边界)。 4.4.SyntaxErroe:语法错误

# 为什么设计成单线程?

js主要是实现用户与浏览器的交互,以及操作dom。那么假如js是多线程,那么多个线程修改同一个dom,那么最后浏览器遵循哪个结果呐?所以为了避免产生意外的复杂操作,js只能是单线程。虽然HTML5提出Web Worker标准允许多线程,但是子线程是不允许修改dom的而且完全受主线程全程控制。

# 异步编程和Eventloop的关系以及为什么有Eventloop

异步编程的概念:多个任务同时执行,但是执行的结果总要有一个统一的队列里面去反馈到系统。那么Eventloop的作用是什么呐?为了让这些事件有条不紊地进行,JS引擎需要对之执行的顺序做一定的安排。比如我们的宏任务和微任务的执行时机

# Generator函数的意义

Generator函数的出现就是使我们可以控制函数的执行和停止,我们都知道promise的状态改变之不能终止的,Generator函数的出现就为我们提供了一种中止的可能性。

# async/await会阻塞代码执行吗

async/await的作用就是让我们的异步代码看着更像同步代码,也可以说让我们的同步代码可以异步执行。那么async/await会不会阻塞代码的执行呐?毕竟我们要等待await后面的代码有结果之后才能继续往下走。所以说async/await实际上是会阻塞代码的执行,但是阻塞的也仅仅是那个函数作用域内部的代码,也就是await关键字后面的代码,对于外面的整个执行上下文是没有影响的,因为async关键字会让函数返回一个promise对象。

# promise真的解决了回调地狱吗

在复杂的逻辑操作的时候,promise的不断then链式调用也会让人觉得有点费解,但是从一定程度上来说promise的代码比起来Callback更加优雅一点,所以说promise只是缓解了回调地狱。

最近更新时间: 2022/09/28 16:26:36
最近更新
01
2023/07/03 00:00:00
02
2023/04/22 00:00:00
03
2023/02/16 00:00:00