js面试题
2024-03-16 14:22:07
浏览量: 1023
- 为什么会发预检请求
- 是一种安全策略,为了保障安全,保护用户数据和隐私,预先检测服务器是否允许跨域请求
- js的数据类型
- 栈:string, number, boolean, undefined, null, symbol, bigInt
- 堆:object (Object、Array、Function 都属于引用类型)
console.log( BigInt(123) )//123n 普通整型后边添加一个n表示BigInt
console.log( typeof BigInt(123) )// bigInt
console.log( typeof null )// object //typeof返回的类型都是小写字母
- 堆和栈的区别
- 堆和栈的概念存在于数据结构中和操作系统内存中。
- 在数据结构中,栈中数据的存取方式为先进后出。而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。完全 二叉树是堆的一种实现方式。
- 在操作系统中,内存被分为栈区和堆区。
- 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。
- 内部属性 [[Class]] 是什么?
- 所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性 [[Class]](我们可以把它看作一个内部的分类,而非传统的面向对象意义上的类)。这个属性无法直接访问,一般通过 Object.prototype.toString(..) 来查看
- js 有哪些内置对象?
- 值属性,这些全局属性返回一个简单值,这些值没有自己的属性和方法。
- 例如 Infinity、NaN、undefined、null 字面量
- 函数属性,全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。
- 例如 eval()、parseFloat()、parseInt() 等
- 基本对象,基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。
- 例如 Object、Function、Boolean、Symbol、Error 等
- 数字和日期对象,用来表示数字、日期和执行数学计算的对象。
- 例如 Number、Math、Date
- 字符串,用来表示和操作字符串的对象。
- 例如 String、RegExp
- 可索引的集合对象,这些对象表示按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。例如 Array
- 使用键的集合对象,这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。
- 例如 Map、Set、WeakMap、WeakSet
- 结构化数据,这些对象用来表示和操作结构化的缓冲区数据,或使用 JSON 编码的数据。
- 例如 JSON 等
- 控制抽象对象
- 例如 Promise、Generator 等
- 反射。例如 Reflect、Proxy
- undefined 与 undeclared 的区别?
- 已声明,未赋值的变量,其默认值为undefined,访问时不报错
- 还没有在作用域中声明过的变量,是 undeclared 的。访问undeclared变量,浏览器会报引用错误,如 ReferenceError: b is not defined 。但是我们可以使用 typeof 的安全防范机制来避免报错,因为对于 undeclared(或者 not defined )变量,typeof 会返回 "undefined"。
- null 和 undefined 的区别?
- 首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
- undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null 主要用于赋值给一些可能会返回对象的变量,作为初始化。
- 当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等 号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
-
- js 获取原型的方法?
- p.proto
- p.constructor.prototype
- Object.getPrototypeOf(p)
- typeof NaN 的结果是什么?
- NaN 意指“不是一个数字”(not a number)
- typeof NaN; // "number"
- NaN != NaN为 true。
- isNaN 和 Number.isNaN 函数的区别?
- Number.isNaN()只有传 NaN 进去才返回 true
- isNaN() 传 NaN 或者不能转化为 Number 的都会返回 true
- Array 构造函数只有一个参数值时的表现?
- Array 构造函数只带一个数字参数的时候,该参数会被作为数组的预设长度(length),而非只充当数组中的一个元素。这样创建出来的只是一个空数组,只不过它的 length 属性被设置成了指定的值。
- 其他值到数字值的转换规则?
- Undefined 类型的值转换为 NaN。
- Null 类型的值转换为 0。
- Boolean 类型的值,true 转换为 1,false 转换为 0。
- String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。
- Symbol 类型的值不能转换为数字,会报错。
- 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
- == 操作符的强制类型转换规则?
- 字符串和数字之间的相等比较,将字符串转换为数字之后再进行比较。
- 其他类型和布尔类型之间的相等比较,先将布尔值转换为数字后,再应用其他规则进行比较。
- null 和 undefined 之间的相等比较,结果为真。其他值和它们进行比较都返回假值。
- 对象和非对象之间的相等比较,对象先进行ToPrimitive抽象操作(如:调用toString方法)后,再进行比较。
- 如果一个操作值为 NaN ,则相等比较返回 false( NaN 本身也不等于 NaN )。
- 如果两个操作值都是对象,则比较它们是不是指向同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true,否则,返回 false。
- javascript 创建对象的几种方式?
- 我们一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码。但 js 和一般的面向对象的语言不同,在 ES6 之前它没有类的概念。但是我们可以使用函数来进行模拟,从而产生出可复用的对象 创建方式,我了解到的方式有这么几种:
- 第一种是工厂模式
- 第二种是构造函数模式
- 第三种模式是原型模式
- JavaScript 继承的几种实现方式?
- 第一种是以原型链的方式来实现继承,但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱。还有就是在创建子类型的时候不能向超类型传递参数。
- 第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的,这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。
- 第三种方式是组合继承,组合继承是将原型链和借用构造函数组合起来使用的一种方式。通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。这种方式解决了上面的两种模式单独使用时的问题,但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。
- 第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象,然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。
- 第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们的自定义类型时。缺点是没有办法实现函数的复用。
- 第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。
-
- 谈谈 This 对象的理解。
- this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。
- 什么是 DOM 和 BOM?
- DOM 指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
- BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM 的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局) 对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 locati on 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对 象的子对象。
- 事件是什么?IE 与火狐的事件机制有什么区别?如何阻止冒泡?
- 事件是用户操作网页时发生的交互动作,比如 click/move, 事件除了用户触发的动作外,还可以是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,包含了该事件发生时的所有相关信息( event 的属性)以及可以对事件进行的操作( event 的方法)。
- 事件处理机制:IE 支持事件冒泡、Firefox 同时支持两种事件模型,也就是:事件冒泡和事件捕获。
- event.stopPropagation() 或者 ie 下的方法 event.cancelBubble = true;
- 三种事件模型是什么?
- 事件是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型。
- 第一种事件模型是最早的 DOM0 级模型,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实 现,它可以在网页中直接定义监听函数,也可以通过 js 属性来指定监听函数。这种方式是所有浏览器都兼容的。
- 第二种事件模型是 IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
- 第三种是 DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。
- 事件委托是什么?
- 事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到 目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理。
- 使用事件代理我们可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理我们还可以实现事件的动态绑定,比如说新增了一个子节点,我们并不需要单独地为它添加一个监听事件,它所发生的事件会交给父元素中的监听函数来处理。
- 什么是闭包,为什么要用它?
- 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以 访问到当前函数的局部变量。
- 闭包有两个常用的用途。
- 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
- 函数的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
- javascript 代码中的 "use strict"; 是什么意思 ? 使用它区别是什么?
- use strict 指的是严格运行模式,设立严格模式的目的,主要是为了消除代码使用中的一些不安全的使用方式,也是为了消除 js 语法本身的一些不合理的地方,以此来减少一些运行时的怪异的行为。同时使用严格运行模式也能够提高编译的效率,从而提高代码的运行速度。
- 变量必须声明后再使用
- 禁止 this 指向全局对象
- 禁止使用 with 语句
- 如何判断一个对象是否属于某个类?
- 第一种方式是使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
- 第二种方式可以通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。
- 第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用 Object.prototype.toString() 方法来打印对象的 [[Class]] 属性来进行判断。
-
- 对于 JSON 的了解?
- JSON 是一种基于文本的轻量级的数据交换格式。它可以被任何的编程语言读取和作为数据格式来传递。
- 在项目开发中,我们使用 JSON 作为前后端数据交换的方式。在前端我们通过将一个符合 JSON 格式的数据结构序列化为 JSON 字符串,然后将它传递到后端,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。
- 因为 JSON 的语法是基于 js 的,因此很容易将 JSON 和 js 中的对象弄混,但是我们应该注意的是 JSON 和 js 中的对象不是一回事,JSON 中对象格式更加严格,比如说在 JSON 中属性值不能为函数,不能出现 NaN 这样的属性值等,因此大多数的 js 对象是不符合 JSON 对象的格式的。
- 在 js 中提供了两个函数来实现 js 数据结构和 JSON 格式的转换处理,一个是 JSON.stringify 函数,通过传入一个符合 JSON 格式的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不符合 JSON 格式,那么在序列化的时候会对这些值进行对应的特殊处理,使其符合规范。在前端向后端发送数据时,我们可以调用这个函数将数据对象转化为 JSON 格式的字符串。
- 另一个函数 JSON.parse() 函数,这个函数用来将 JSON 格式的字符串转换为一个 js 数据结构,如果传入的字符串不是标准的 JSON 格式的字符串的话,将会抛出错误。当我们从后端接收到 JSON 格式的字符串时,我们可以通过这个方法来将其解析为一个 js 数据结构,以此来进行数据的访问。
- js 延迟加载的方式有哪些?
- js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
- 第一种方式是我们一般采用的是将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
- 第二种方式是给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- 第三种方式是给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js 脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 第四种方式是动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
- Ajax 是什么? 如何创建一个 Ajax?
- 它是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
- 创建一个 ajax 有这样几个步骤
- 首先是创建一个 XMLHttpRequest 对象。
- 然后在这个对象上使用 open 方法创建一个 http 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。
- 在发起请求前,我们可以为这个对象添加一些信息和监听函数。比如说我们可以通过 setRequestHeader 方法来为请求添加头信息。我们还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,我们可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候我们可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候我们就可以通过 response 中的数据来对页面进行更新了。
- 当对象的属性和监听函数设置完成后,最后我们调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。
- 谈一谈浏览器的缓存机制?
- 浏览器的缓存机制指的是通过在一段时间内保留已接收到的 web 资源的一个副本,如果在资源的有效时间内,发起了对这个资源的再一次请求,那么浏览器会直接使用缓存的副本,而不是向服务器发起请求。使用 web 缓存可以有效地提高页面的打开速度,减少不必要的网络带宽的消耗。
- web 资源的缓存策略一般由服务器来指定,可以分为两种,分别是强缓存策略和协商缓存策略。
- 使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。
- 服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。
- Expires 是 http1.0 中的方式,因为它的一些缺点,在 http 1.1 中提出了一个新的头部属性就是 Cache-Control 属性, 它提供了对资源的缓存的更精确的控制。它有很多不同的值,常用的比如我们可以通过设置 max-age 来指定资源能够被缓存的时间 的大小,这是一个相对的时间,它会根据这个时间的大小和资源第一次请求时的时间来计算出资源过期的时间,因此相对于 Expires 来说,这种方式更加有效一些。常用的还有比如 private ,用来规定资源只能被客户端缓存,不能够代理服务器所缓存。还有如 n o-store ,用来指定资源不能够被缓存,no-cache 代表该资源能够被缓存,但是立即失效,每次都需要向服务器发起请求。
- 一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control 的优先级要高于 Expires 。
- 使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。 如果资源发生了修改,则返回修改后的资源。协商缓存也可以通过两种方式来设置,分别是 http 头信息中的 Etag 和 Last-Modified 属性。
- 服务器通过在响应头中添加 Last-Modified 属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。使用这种方法有一个缺点,就是 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变, 这样会造成缓存命中的不准确。
- 因为 Last-Modified 的这种可能发生的不准确性,http 中提供了另外一种方式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified 的方式更加精确。
- 当 Last-Modified 和 Etag 属性同时出现的时候,Etag 的优先级更高。使用协商缓存的时候,服务器需要考虑负载平衡的问题,因此多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因此在考虑负载平衡时,最好不要设置 Etag 属性。
- 强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
- Ajax 解决浏览器缓存问题?
- 在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。
- 在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。
- 在 URL 后面加上一个随机数:"fresh=" + Math.random();。
- 在 URL 后面加上时间戳:"nowtime=" + new Date().getTime();
- 同步和异步的区别?
- 同步指的是当一个进程在执行某个请求的时候,如果这个请求需要等待一段时间才能返回,那么这个进程会一直等待下去,直到消息返 回为止再继续向下执行。
- 异步指的是当一个进程在执行某个请求的时候,如果这个请求需要等待一段时间才能返回,这个时候进程会继续往下执行,不会阻塞等 待消息的返回,当消息返回时系统再通知进程进行处理。
- 什么是浏览器的同源政策?
- 一个域下的 js 脚本在未经允许的情况下,不能够访问另一个域的内容。这里的同源的指的是两个域的协议、域名、端口号必须相同,否则则不属于同一个域。
- 同源政策主要限制了三个方面
- 第一个是当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
- 第二个是当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
- 第三个是当前域下 ajax 无法发送跨域请求。
- 同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者 script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。
- 谈一下 cookie ?
- cookie 是服务器提供的一种用于维护会话状态信息的数据,通过服务器发送到浏览器,浏览器保存在本地,当下一次有同源的请求时,将保存的 cookie 值添加到请求头部,发送给服务端。这可以用来实现记录用户登录状态等功能。cookie 一般可以存储 4k 大小的数据,并且只能够被同源的网页所共享访问。
- 服务器端可以使用 Set-Cookie 的响应头部来配置 cookie 信息。一条cookie 包括了5个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 失效的时间,domain 是域名、path是路径,domain 和 path 一起限制了 cookie 能够被哪些 url 访问。secure 规定了 cookie 只能在确保安全的情况下传输,HttpOnly 规定了这个 cookie 只能被服务器访问,不能使用 js 脚本访问。
- ES6 模块与 CommonJS 模块、AMD、CMD 的差异。
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
- .call() 和 .apply() 和bind的区别?
- apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为参数列表
- call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数。
- bind参数和apply一样,但是不会立即执行,而是返回一个新函数
- JavaScript 类数组的定义?
- 一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用数组的方法。
- 常见的类数组对象有 arguments 和 DOM 方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有 length 属性值,代表可接收的参数个数。
- 常见的类数组转换为数组的方法有这样几种:
- Array.prototype.slice.call(arrayLike);
- Array.prototype.concat.apply([], arrayLike);
- Array.from(arrayLike);
- JavaScript 中的作用域与变量声明提升?
- 变量提升的表现是,无论我们在函数中何处位置声明的变量,好像都被提升到了函数的首部,我们可以在变量声明前访问到而不会报错。
- 造成变量声明提升的本质原因是 js 引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当我们访问一个变量时,我们会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。这就是会出现变量声明提升的根本原因。
- 函数提升只会提升函数声明,不会提升函数表达式
- 哪些操作会造成内存泄漏?
- 第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
- 第二种情况是我们设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留 在内存中,而无法被回收。
- 第三种情况是我们获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回 收。
- 第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。
- 什么是 Polyfill ?
- Polyfill 指的是用于实现浏览器并不支持的原生 API 的代码。
- 比如说 querySelectorAll 是很多现代浏览器都支持的原生 Web API,但是有些古老的浏览器并不支持,那么假设有人写了一段代码来实现这个功能使这些浏览器也支持了这个功能,那么这就可以成为一个 Polyfill。
- 介绍一下 js 的节流与防抖?
- 防抖(Debounce):当事件被连续触发多次时,只有最后一次触发会生效,前面的触发将被取消。常用于处理输入框的联想功能、按钮点击等场景
function debounce(func, delay) {
let timer; // 定义计时器变量
return function() {
clearTimeout(timer); // 清除之前的计时器
timer = setTimeout(() => {
func(); // 在指定的延迟时间内调用传入的函数
}, delay);
};
}
- 节流(Throttle):当事件被连续触发多次时,保持一段时间内只执行一次。常用于优化性能,比如图片加载、页面滚动等场景
function throttle(func, delay) {
let lastTime = null; // 记录上次执行的时间
return function() {
const currentTime = Date.now(); // 获取当前时间
if (lastTime && currentTime - lastTime < delay) {
return; // 若两次触发时间小于delay,则不执行函数
} else {
func(); // 否则执行函数并更新上次执行时间
lastTime = currentTime;
}
};
}
- Object.is() 与原来的比较操作符 “===”、“==” 的区别?
- 使用双等号进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
- 使用三等号进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。
- 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 认定为是相等的。
- escape,encodeURI,encodeURIComponent 有什么区别?
- encodeURI 是对整个 URI 进行转义,将 URI 中的非法字符转换为合法字符,所以对于一些在 URI 中有特殊意义的字符不会进行转义。
- encodeURIComponent 是对 URI 的组成部分进行转义,所以一些特殊字符也会得到转义。
- escape 和 encodeURI 的作用相同,不过它们对于 unicode 编码为 0xff 之外字符的时候会有区别,escape 是直接在字符的 unicode 编码前加上 %u,而 encodeURI 首先会将字符转换为 UTF-8 的格式,再在每个字节前加上 %。
- js 的事件循环是什么?
- 因为 js 是单线程运行的,在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行主线程代码。
- 当异步事件结束后,再将异步事件对应的回调加入到对应的任务队列中等待执行。
- 任务队列可以分为宏任务对列和微任务对列,当主线程中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。当微任务对列中没有可执行的任务时,再去再去检查宏任务对列中的任务。
- 微任务包括了 promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。
- 宏任务包括了 script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲 染等。
- 同类型的任务队列,按照先进先出的原则依次压栈执行
- 每次执行完一个宏任务后会立刻检查微任务队列中是否有可执行的微任务,不会等宏任务队列中可执行的任务全部执行完毕才检查微任务
setTimeout(() => {
console.log('s-1')
Promise.resolve().then(()=>{
console.log('p-1')
})
}, 0);
setTimeout(() => {
console.log('s-2')
Promise.resolve().then(()=>{
console.log('p-2')
})
}, 0);
// s-1
// p-1
// s-2
// p-2
- js Proxy的原理详解
- Proxy是js的内置对象,是es6语法
//语法
let perosn = {
name:'james',
age:26,
profession:'software'
}
var proxy = new Proxy(perosn,{
get:function(target,property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("propertype\""+property + "\" does no exit" );
}
}
});
- Object.defineProperty
- 该方法用于在对象上定义一个新属性,或者修改对象现有属性,并返回此对象
- value 属性的值
- writable 控制此属性是否可写
- get 给属性提供的getter方法
- set 给属性提供的setter方法
- configurable 当属性为true时,该属性的描述符才能够被改变,同时该属性也能从对象中被删除
- enumerable 当属性为true时,该属性才能够在对象的枚举中出现
var obj = {};
obj.a = "a";
obj['b'] = 'b';
//给对象创建新属性我们的方法:
Object.defineProperty(obj, 'c', {
value: 'c',
get: () => {
console.log('访问时触发')
return '自定义返回值';
},
set: (newValue) => {
console.log('修改时触发',newValue);
}
});
- 前端路由模式
- history
- 底层使用的是window.history.pushState事件
- 不带#,符合传统url地址格式,美观
- 上线后,需要在服务器中配置相关的规则,否则刷新页面会出现404错误
- ngix配置:try_files: $uri $uri/ /index.html;
- hash
- 不需要服务端处理
- 底层使用的是监听 window.onhashchange 事件
- Promise
- 三个状态 pendding等待 fulfilled成功 reject失败
- Promise 对象的核心原理是基于发布订阅模式,内部保存了两个队列,通过then收集成功的回调(onResolve,catch收集失败的回调(onReject)。当异步操作成功或失败时,Promise 对象会改变其状态,并执行相应的回调函数。
- mvc和mvvm的区别
- mvc即model-view-controller模型-视图-控制器,以控制器为桥梁将视图和模型进行代码分离,是单项数据通信
- mvvm即model-view-viewModel,是一种前端架构思想,以数据双向通信为核心,将ui界面和逻辑代码进行拆分,降低ui和业务逻辑的耦合性,提高重用性(如同一个业务逻辑函数可作用于不同的ui界面)。可以实现多个ui界面通过一个数据进行双向控制
- VIew-Model 利用数据绑定和DOM事件监听实现数据的双向绑定 思想:实现了VIew和Model的自动同步,也就是Model的属性改变时,不需要手动操作DOM元素才改变View的显示
- 逻辑像素和物理像素的区别
- 逻辑像素表示屏幕展示物体的视觉尺寸是多少,也就是css中使用的px,1px表示一个逻辑像素
- 物理像素表示设备屏幕的宽和高,代表屏幕上有多少个点像素点组成,是固定的,这是厂商在出厂时就设置好了的,即一个设备的分辨率是固定的
- 默认情况下1物理像素 = 1逻辑像素, 在高像素密度的设备上1物理像素 = 多个逻辑像素。比如: iPhone6的物理像素是7501334,逻辑像素是375667, 设备像素比是2
- h5移动端适配方案
- 宽度使用百分比自适应
- 加<meta name='viewport'/>
- 使用css的@media媒体查询语句
- 使用flex布局
- 使用rem单位;使用postcss和postcss-pxtorem插件转换px为rem,使用amfe-flexible动态设置html的font-size
html{
/* 设计稿尺寸750 */
font-size: calc(100vw/750 * 100);
}
- 说出输出结果
- 知识点:async函数和Promise本身是同步代码,它们的返回结果才是异步的微任务
async function fn1(){
console.log('fn1 s')
fn2()
console.log('fn1 e')
}
async function fn2(){
console.log('fn2')
}
console.log('script s')
fn1()
setTimeout(() => {
console.log('timeout')
}, 0);
new Promise(resove => {
console.log('promise')
resove()
}).then(()=>{
console.log('p then')
})
console.log('script e')
/**
script s
fn1 s
fn2
fn1 e
promise
script e
p then
timeout
*/
- Object.defineProperty 和 Proxy的区别
- Object.defineProperty只能劫持对象的属性,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历,而proxy是直接代理对象,不需要遍历操作
- Object.defineProperty对新增属性需要手动进行observe,因为defineproperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象,再对新增属性再使用defineproperty进行劫持。而Proxy可以直接监听对象属性的添加
- definedProperty不能监听数组变化,Proxy可以监听数组的变化
- definedProperty兼容性好,Proxy是es6语法性能好
- Proxy是构造函数,new Proxy()返回一个Proxy对象;definedProperty返回目标对象
- 判断数据类型
- typeof
- instanceof 可以判断对象的类型 ,是根据原型链来做判断依据的
- Object.prototype.toString
- Object.prototype.toString.call(window) // [object global] window是全局对象global的引用
- 垃圾回收机制
- JS的垃圾回收机制是为了防止内存泄漏,间歇的不定期的寻找到不再使用的变量,并释放它们所指向的内存
- 垃圾回收的方式
- 标记清除算法 - 标记阶段给活动的对象做上标记,清除阶段把没有标记的对象销毁(缺点:内存碎片化,分配速度慢)
- 引用计数算法 - 如果没有引用指向该对象,则销毁,现在很少使用这算法了
- 标记清除是JavaScript中主流的垃圾回收算法
- 设计模式
- 工厂模式、构造函数模式
- 单例模式
- 发布订阅模式
- 是基于事件机制来实现,收集的是事件回调函数
- 缓存队列,存放订阅信息
- 具有增加、删除订阅的api
- 调度中心,当状态改变时由调度中心通知所有订阅者执行监听函数
// 创建一个 EventEmitter 类作为事件管理器
class EventEmitter {
constructor() {
this.events = {}; // 存放所有注册的事件及其对应的处理函数列表
}
on(eventName, handler) {
if (!this.events[eventName]) {
this.events[eventName] = []; // 如果该事件还未被注册过,则初始化一个空数组
}
this.events[eventName].push(handler); // 将处理函数添加到事件名称对应的处理函数列表中
}
emit(eventName, ...args) {
const handlers = this.events[eventName];
if (handlers && Array.isArray(handlers)) {
for (let i = 0; i < handlers.length; i++) {
handlers[i](...args)// 依次调用每个处理函数并传入参数 args
}
}
}
}
- 观察者模式
- 是基于对象和发布订阅的基础上实现的,收集的是对象,具体数据状态更新后的逻辑由观察者对象内部根据数据状态的变化,做不同的处理
class Subject {
constructor() {
this.observers = []; // 存放观察者的数组
}
register(observer) {
if (!this.observers.includes(observer)) {
this.observers.push(observer);
}
}
notify(...args) {
for (const observer of this.observers) {
observer.update(...args);
}
}
}
class Observer {
update(data) {
console.log(`Observer received data: ${data}`);
}
}
// 创建主题对象
const subject = new Subject();
// 创建观察者对象并将其注册到主题上
const observerA = new Observer();
subject.register(observerA);
const observerB = new Observer();
subject.register(observerB);
// 向所有观察者发送通知
subject.notify('Hello World');
- 输入URL发生了什么
- DNS域名解析,域名解析为IP地址
- 该ip或域名下有http缓存且缓存有效时,取缓存数据
- 浏览器与目标服务器建立一个TCP链接,三次握手
- 浏览器向服务器发送请求报文
- 服务器向浏览器发送响应报文
- 响应资源解析:两种方案:
- spa 渲染
- 网页响应返回简单的html,逻辑都在js,通过js构建dom树
- ssr 同构渲染
- 将前端代码在服务器端运行生成静态HTML,然后将其传输到客户端展示
- 首评渲染速度快
- 对seo友好
- spa 渲染
- 关闭TCP链接,四次挥手
- 浏览器渲染
- 解析html(AST)dom tree
- 解析css tree
- 合并render tree
- 根据render tree绘制页面
- 跨域方式
- jsonp - 利用 script 标签没有跨域限制的特点,可以获取到json数据,简单兼容性好,仅支持get方式,容易被攻击
- CORS - 服务器端配置
- postMessage - HTML5 XML httpRequest的API
- websocket - HTML5的持久化协议,基于TCP协议,是一种双向通信协议,建立连接后,两端都能主动向对方发送或接受数据,一般用Socket.io,封装了websocket的接口
- Node中间件代理
- Nginx 反向代理
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event){
console.log(event.data, event.origin,event.source)
}
targetWindow.postMessage(data, targetOrigin, [transfer]);
- 浏览器渲染步骤
- 先将html里面代码从上往下进行加载解析,并且在过程中进行渲染,如果在过程中碰到一些需要请求的外部资源,如 图片,视屏,外部链接的css,js等资源,那么又会重新连接过去请求对应的外部资源,但是这一个过程是异步的渲染过程中主要由浏览器内核来完成,不同内核渲染方式稍有不同内核大致可以分为两个引擎:渲染引擎和js引擎
- 渲染引擎解析(解析可以理解为是浏览器不认识代码,就是将浏览器不认识的东西转换成浏览器能认识的东西)HTML文件,构建DOM tree(DOM树,我们可以理解为一种由HTML中的标签,文本节点和属性所组成的一种树结构数据)
- 解析CSS文件构建CSSOM tree(CSSOM树) 注意:渲染引擎它不能直接解析css的代码,所以需要转换成对应的CSSOM树
- 结合DOM tree和CSSOM tree生成Render tree(渲染树)
- 进行layout(布局)处理阶段,也就是将Render tree里面的东西进行一个布局,可以理解为将Render tree分配到屏幕上的某一位置,给每一个元素分配对应的屏幕坐标,只是分配的坐标并没有展示出来
- 绘制阶段,渲染引擎遍历循环Render tree将每一个节点绘制到屏幕上,需要知道这个绘制过程是渐进式的(就是说网页并不是全部绘制完成一下子出来,这样用户体验不是很好了,它是边解析边绘制的)
- 回流和重绘
- 回流 :在第一次的基础上布局会得到一些布局的基础信息(如元素的大小位置...),后面通过一些代码对这些信息进行了修 改,那么就会重新计算这些布局信息,这个过程叫回流
- 重绘:所谓重绘就是重新绘制意思,也就是浏览器在第一次渲染以后还会绘制,后期如果去修改了元素的大小,位置,颜色等,它就会重绘
- 注意:回流以后会重新绘制
- 页面渲染优化
- js脚本尽量放最后,或使用defer、async异步加载,或动态创建script标签
- 减少DOM操作,减少回流和重绘
- 减少js修改样式,通过修改class名称解决
- 使用document.createDocumentFragment文档碎片,新增dom结构
- 减少大图片使用,使用字体图标
- 工具类的sdk库,使用CDN方式加载
- 减少http请求,重复的接口请求使用前端弱缓存
- 资源按需加载和预加载
- post和get
- get参数通过URL传递,post放在body中
- get请求在URL中传递的参数有长度限制,post没有
- get在浏览器回退是无害的,而post会再次提交请求
- get请求会被浏览器主动cache,post不会
- get只接受ASCLL字符,post没有限制
- get产生一个tcp数据包,post产生两个tcp数据包
- 网站安全
- XSS脚本攻击
- 跨站脚本攻击,是常见和基本的攻击web网站的方法
- 攻击者通过注入非法的html标签或者javascript代码,从而当用户浏览该网页时,控制用户浏览器
- XSS防御方法
- httpOnly: 在cookie中设置httpOnly属性,使js脚本无法读取cookie信息
- 前端加上输入检查,后端做过滤检查
- 对用户输入的数据做标签转换
- CSRF
- 跨站点请求伪造,冒充用户发起请求,完成一些违背用户意愿的事,如修改用户信息等
- CSRF防御方法
- 验证码 - 强制用户必须与应用进行交互,才完成请求
- 表单提交尽量使用post,get相对容易被哪来做CSRF攻击
- 请求来源限制
- token验证 - 默认适合的方案
- 取消接口请求
- axios 使用CancelToken
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c
})
})
// 执行取消请求操作
cancel()
- 原生XMLHttpRequest使用abort
const xhr = new XMLHttpRequest();
xhr.open('GET', '<http://127.0.0.1:3000/api/get>', true);
xhr.send();
setTimeout(() => {
xhr.abort();
}, 1000);
- webpack简介
- webpack是一个模块打包工具,可以使用它管理项目中的模块依赖,并编译输出模块所需的静态文件
- webpack有对应的模块加载器,会分析模块间的依赖关系,最后合并生成优化后的静态资源
- 代码转换 - typescript编译成javascript,scss编译成css等
- 文件优化 - 压缩javascript/css/html代码和图片等
- 代码分割 - 提取多个页面的公共代码,提取首屏不需要执行部分的代码让其异步加载
- 模块合并 - 在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
- webpack构建过程
- 从entry里配置的module开发递归解析entry依赖的所有module,每找到一个module,就会根据配置的loader去找对应的转换规则对module进行转换后,再解析出当前module依赖的module,这些模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个组chunk
- 最后webpack会把所有chunk转换成文件输出,在整个流程中,webpack会在恰当的时机执行plugin里定义的逻辑
- diff算法
- diff算法是指生成更新补丁的方式,主要运用于虚拟DOM树变化后,更新真实DOM
- react的diff算法,触发更新的时间主要是在state变化和hooks调用之后。此时触发虚拟DOM树变更遍历,采用了深度优先的遍历算法,采用3种类型节点的比对,分别是树、组件和元素
- 树比对 - 由于网页视图中较少出现跨层级节点移动,所以两个虚拟DOM树只对同一层级的节点进行比较
- 组件比对 - 如果组件是同一类型,则进行树对比,如果不是则直接放入补丁中
- 元素比对 - 主要发生在同层级中,通过节点标记(key)操作生成补丁,节点操作对应真实DOM的操作
- React16起,引入了Fiber架构,是为了使整个更新过程可以随时暂停和恢复,节点和树分别采用了FiberNode和FiberTree进行重构,FiberNode使用了双链表的结构,可以直接找到兄弟节点和子节点
- vue整体的diff算法和react一致, 但没有Fiber,少了时间切片的能力
- 常见排序算法
- 快速排序的时间复杂度 O(n*log2n)
- 冒泡排序的时间复杂度是 O(n^2)
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) return arr; // 如果数组长度小于等于1,则直接返回原数组
const pivotIndex = Math.floor(arr.length / 2); // 选取基准元素(pivot)所在位置
const pivot = arr[pivotIndex]; // 获取基准元素值
let leftArr = []; // 存放比基准元素小的元素
let rightArr = []; // 存放比基准元素大的元素
for (let i = 0; i < arr.length; i++) {
if (i === pivotIndex) continue; // 跳过基准元素本身
if (arr[i] < pivot) {
leftArr.push(arr[i]); // 将比基准元素小的元素添加到左侧数组
} else {
rightArr.push(arr[i]); // 将比基准元素大的元素添加到右侧数组
}
}
return [...quickSort(leftArr), pivot, ...quickSort(rightArr)]; // 合并左、右两部分结果及基准元素
}
// 冒泡排序
function bubbleSort(arr) {
var len = arr.length - 1;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - i; j++) {
if (arr[j] > arr[j + 1]) { // 比较相邻元素大小并交换位置
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
- 算法复杂度
- 算法是指解决问题的方案准确而完整的描述,是一系列解决问题的清晰指令,算法代表着:用系统的方法解决问题的策略机制。 算法研究的是如何花最少的时间、最少的内存空间完成相同的需求。
- 时间维度
- 时间维度:执行当前算法所消耗的时间,时间越短,算法越好。
- 时间复杂度:一个代码执行的时间趋势,执行次数与执行参数息息相关,如果一个问题的规模是n,解决这个问题的某一算法需要时间T(n)。
// T(n) = O(f(n)) //大O表示法
// O:某个算法耗时与数据增长量之间的关系。
// T:代表算法需要执行的总时间
// n:输入的数据量
// 大O表示法:代码执行时间随数据规模增长的变化趋势,时间复杂度表示的是变化趋势,并不是真正的执行时间。
// 常见的时间复杂度:
// 常数阶:O(1)
// 线性阶:O(n),数据量增大n倍时,耗时也增大n的n倍
// 平方阶:O(n^2)
// 立方阶:O(n^3)
// 对数阶:O(logn)。如二分查找,n以对数的规模进行递减 n->n/2->n/4
// 线性对数阶:O(nlogn)
// 指数阶:O(2^n)
// 优劣对比:O(1)<O(logn)<O(n)<O(nlogn)<O(n^ 2)< O(n^ 3)<O(2^ n)
- 空间维度
- 空间维度:执行当前代码所消耗的内存空间。
- S(n) = T(f(n))
- 空间复杂度:是对一个算法在运行中临时占用空间大小的度量。表示的是:算法存储空间与输入值之间的关系。
- 通常来讲,只要我们的算法不涉及到动态的空间,如递归、栈,空间的复杂度通常为常数O(1)
- 算法的空间复杂度,并不是指实际占用的空间,而是计算整个算法的辅助空间单元(变量)的个数,与问题的规模没有关系,空间复杂度主要看新开辟的变量个数。
- 断点续传怎么做的
- 断点续传是一种网络传输技术,可以在文件传输中出现中断后恢复传输而无需重新开始传输整个文件。这在文件传输较大、网络不稳定、带宽有限等情况下尤为重要。 通常情况下,实现断点续传需要客户端和服务端的相互配合。以下是一般的实现步骤: 客户端发起文件上传请求时,向服务端发送一个 HTTP 请求,包含文件名、文件大小等信息。 服务端接收到请求后,根据文件名和文件大小创建一个空文件,并返回已经上传的文件大小,如果之前有部分数据已经上传过,那么服务端可以根据已上传的大小确定从哪个位置开始上传。 客户端接收到服务端返回的已经上传的文件大小后,可以根据已上传的大小计算出下一次应该从哪个位置开始上传。 客户端将文件切分为多个小块,每个小块的大小一般为几十 KB 或几百 KB,将每个小块的数据上传到服务端,并在每个小块的末尾添加一个分界符,标记上传结束。 服务端接收到每个小块后,将其存储到文件对应的位置,等待下一次上传。 如果上传过程中出现中断,客户端可以记录已经上传的文件大小和已经上传的小块数,下次上传时从上一次中断的位置开始继续上传。 总的来说,断点续传的实现需要客户端和服务端的相互配合,并需要考虑文件上传的稳定性和可靠性。
- 秒传怎么实现
- 秒传是指上传一个文件时,如果已经存在相同的文件,就直接使用已经存在的文件,而不需要重新上传。实现秒传需要用到文件的唯一标识。 一种常见的实现方式是在上传文件时,先计算文件的哈希值或者MD5值,并将其作为文件的唯一标识。然后将该标识与后台数据库中已经存在的文件的标识进行比对。如果存在相同的标识,说明该文件已经上传过,那么就直接使用已经存在的文件;如果不存在相同的标识,说明该文件是一个新文件,那么就进行正常的上传操作。 在实现过程中,可以通过在前端计算文件哈希值或MD5值的方式,避免上传重复的文件,减少服务器的压力,提高文件上传的效率。同时,后台服务器也需要维护一个文件信息的数据库,用于存储已经上传过的文件的唯一标识和存储路径等信息,以便实现秒传功能。 需要注意的是,文件的唯一标识需要具备唯一性和不可修改性,否则就无法实现秒传功能
- Web Component 概述
- Web Component 是一种用于构建可复用用户界面组件的技术,开发者可以创建自定义的 HTML 标签,并将其封装为包含逻辑和样式的独立组件,从而在任何 Web 应用中重复使用。
- 每个 Web Component 都具有自己的 DOM 和样式隔离,避免了全局 CSS 和 JavaScript 的冲突问题。它还支持自定义事件和属性,可以与其他组件进行通信和交互。
- 不同于 Vue/React 等社区或厂商的组件化开发方案,Web Component 被定义在标准的 HTML 和 DOM 标准中。它由一组相关的 Web 平台 API 组成,也可以与现有的前端框架和库配合使用
- h5 Hybrid开发模式注意事项
- web页面的导航层级不宜太多,原生页面返回和h5页面的路由返回可能会产生歧义
- 注意对各种机型的适配,一些属性在不同的手机型号上兼容性差距大,需要分别处理,有些低版本的安卓机使用默认浏览器内核不支持js的新语法
- H5的界面显示在手机上,对点击、触摸、滑动等事件的响应并不如原生控件那样流畅,甚至还会出现卡顿
- http1和http2的区别
- 多路复用技术:HTTP/2 使用多路复用技术,允许在一个连接上同时传输多个请求和响应,这避免了 HTTP/1 中常见的队头阻塞问题。12345
- 二进制分帧:HTTP/2 采用了二进制格式进行数据传输,使用帧来组织数据,每个帧包含类型、优先级、长度等头部信息,以及具体的数据内容。这种方式减少了数据传输的大小,并提高了数据传输的效率。135
- 头部压缩:HTTP/2 使用 HPACK 算法进行头部压缩,减少了传输的头部大小,从而提高了性能。1235
- 服务器推送:HTTP/2 中引入了服务器推送机制,允许服务器在客户端请求数据之前主动推送相关的数据给客户端,这进一步提高了性能和速度。
- 安全性:HTTP/2 对安全性的要求更高,要求使用 TLS 加密协议进行传输,保证了数据传输的安全性。1
- 无状态特性:HTTP/1 是无状态的,每个连接都是一个新的连接,而 HTTP/2 通过使用流和帧,可以在一个连接上保持多个活跃的“对话”,从而在一定程度上改善了无状态特性。
- https为什么是安全的
- 加密传输:HTTPS通过SSL/TLS加密技术对通信数据进行加密,使得非法用户无法解密获取有用信息,从而保证了数据传输过程中的安全性。123
- 证书验证:HTTPS要求网站拥有合法的SSL证书,浏览器会对证书进行验证。如果证书有问题,浏览器会显示“不受信任的证书”警告,确保用户访问的网站是可信的。13
- 防劫持:HTTPS通过加密维护数据的完整性,防止第三方篡改数据包。这样,用户在浏览网站时可以确信所看到的内容未被修改或被恶意代码篡改,有效防止了网站欺诈、木马病毒、黑客攻击等问题。
- 验证身份:由于HTTPS使用SSL证书,可以验证网站的身份真实性,用户可以相信网站的真实身份,避免信用卡欺诈、虚假网站欺骗等问题。
- js怎么获取客户端计算机硬件信息
- navigator对象可以获取一些基本的硬件信息,如操作系统、浏览器信息
- WebRTC API提供了获取摄像头和麦克风信息的能力,可以间接获取一些硬件信息
// 请求可用媒体输入和输出设备的列表
navigator.mediaDevices.enumerateDevices().then(devices => {});
// 调用打印API,连接使用打印机
window.addEventListener('beforeprint', function() {});
window.print({
documentTitle: 'My Print Job', // 打印作业标题
data: [new Blob([cont"Hello World!"ent], { type: 'text/plain' });] // 需要打印的数据
}).then(function () {});
// navigator.printers.getPrinters().then(function (printerList) {});//获取打印机列表,兼容问题很大
- 微前端实现方案
- iframe
- 刷新页面,url路由丢失,会返回首页
- 全局上下午隔离,数据不共享
- UI不兼容,如:弹框组件的遮罩不能覆盖整个窗口
- 慢,每次子应用进入都是一次浏览器上下文重建,资源重新加载的过程
- single-spa
- https://single-spa.js.org/
- single-spa是一个很好的微前端基础框架,qiankun框架就是基于single-spa来实现的
- js和css没有隔离
- 不能开箱即用,需修改大量配置
- qiankun
- 阿里开源的前端框架
- 提供了开箱即用的api
- 任意技术均可接入
- 实现了js和css的隔离
- 支持资源与加载
- https://qiankun.umijs.org/
// 在主应用中注册微应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react-app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/app-react',
}
]);
start();
// react子应用配置-------
// 在 src 目录新增 public-path.js:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 路由修改
// <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
// 入口文件 index.js 修改
function render(props) {
const { container } = props;
ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function mount(props) {
console.log('[react16] props from main framework', props);
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}
// 修改 webpack 配置---------
// 安装插件 @rescripts/cli,修改脚手架webpack默认配置,当然也可以选择其他的插件,例如 react-app-rewired
// 根目录新增 .rescriptsrc.js:
const { name } = require('./package');
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = 'umd';
// webpack 5 需要把 jsonpFunction 替换成 chunkLoadingGlobal
config.output.jsonpFunction = `webpackJsonp_${name}`;
config.output.globalObject = 'window';
return config;
}
// ...
};
// 修改 package.json:
// - "start": "react-scripts start",
// + "start": "rescripts start",
// - "build": "react-scripts build",
// + "build": "rescripts build",
// - "test": "react-scripts test",
// + "test": "rescripts test",
// - "eject": "react-scripts eject"
- 前端性能优化
- 性能检测工具
- Lighthouse
- node库,用于分析和提供改善Web应用质量的建议,生成检测报告文件
- Audits
- chrome 插件
- Lighthouse
- 网络层面优化
- 静态资源上cdn
- 网站内的资源有多个不同域名时,使用prefetch预加载
- <link rel="prefetch" href="xxx" />
- 当浏览器解析到link标签时,读取到rel的值为prefetch,便会将这一个资源添加的队列中,当浏览器空闲时提取资源
- 图片优化
- 不同的图片使用合适的格式:png、jpg、gif、webp、apng
- 使用占位图、懒加载
- 预加载
- js优化
- 防抖、节流
推荐文章
-
2024-02-18 04:56:39 浏览量: 1014
-
2024-03-06 15:35:55 浏览量: 1103
-
2024-03-13 05:01:19 浏览量: 1009
-
2024-03-12 20:42:57 浏览量: 1008
-
2024-02-16 14:02:15 浏览量: 1007
-
2024-02-23 17:39:12 浏览量: 1011
-
2024-03-02 09:11:40 浏览量: 1009
-
2023-11-26 13:04:03 浏览量: 1014
-
2024-01-07 23:57:59 浏览量: 1015
-
2024-02-15 20:56:24 浏览量: 1004
-
2024-03-15 08:46:32 浏览量: 1016
-
2024-03-10 22:08:25 浏览量: 1018
-
2024-03-16 14:22:07 浏览量: 1023
-
2024-03-15 04:54:17 浏览量: 1012
-
2024-03-16 14:21:18 浏览量: 1007
-
2024-02-29 09:32:30 浏览量: 1009
-
2024-03-16 14:21:43 浏览量: 1005
-
2024-03-12 08:58:21 浏览量: 1008
-
2024-02-15 23:08:03 浏览量: 1002
-
2024-03-13 08:03:42 浏览量: 1005