JS面试题总结

1. 说说JavaScript中的数据类型?存储上的差别?

基本数据类型:

  • 字符串(String)
  • 数字(Number)
  • 布尔(Boolean)
  • 空值(Null)
  • 未定义(Undefined)
  • 符号(Symbol)

引用数据类型:

  • 对象(Object)
  • 数组(Array)
  • 函数(Function)

存储区别:

基本数据类型

  • 字符串、数字、布尔、空值和未定义是不可变的,他们在内存中的值不能被修改。每次对基本数据类型进行修改时,实际上时创建了一个新的值。
  • 基本数据类型的值通过简单的赋值来复制。当将一个原始类型的值赋给另一个变量时,会将值复制到新变量的内存位置上。所以两个变量存储的是完全相互独立的两个值。

引用数据类型:

  • 对象、数组和函数是可变的,它们的值可以被修改。
  • 引用类型的值实际上是存储在堆内存的对象。变量存储的是对象的引用,即对象所在内存地址的指针。
  • 当将一个引用类型的值赋给一个变量时,实际上是将引用复制给了新的变量。这意味着两个变量引用的是同一个对象。对其中一个变量所做的修改会影响到另一个变量,因为它们指向同一块内存空间。

需要注意的是,基本类型和引用类型的判等方式也不同。基本类型通过值的比较来判断是否相等,而引用类型判断的是变量是否引用同一对象。

2.typeof 与 instanceof 区别

typeofinstanceof是JavaScript中用于检测数据类型的两个操作符,它们之间存在一些区别:

1.typeof是一个一元操作符,用于检测一个值的基本类型。他返回一个表示数据类型的字符串。常见的返回值包括:“number”、“string”、“boolean”、“object”、“function”、“undefined” 和 “symbol”。注意,在使用typeof检测null值时会返回“object”。

2.instanceof是一个二元操作符,用于检测一个对象是否属于某个特定类的实例。它需要两个操作数:一个是要检测的对象,另一个是表示类的构造函数。它返回一个布尔值,表示对象是否属于该类的实例,或者是该类派生类的实例。
例如,obj instanceof Array用于检测obj是否是数组类型的实例。

注意以下区别:

  • typeof适用于所有的数据类型,包括基本类型的引用类型,而instanceof只适用于引用类型。
  • typeof的返回值是一个字符串,表示操作数的数据类型,而instanceof的返回值是布尔类型,表示对象是否为特定类的实例。
  • instanceof是基于原型链的概念进行判断的,它会向上遍历对象的原型链,检查对象是否属于指定类或者指定类的派生类的实例。而 typeof只判断基本类型,并不能检测出对象的基本类型。

因此,你可以根据具体的需求选择使用 typeofinstanceof来检测数据类型。如果你需要判断一个对象是否为某个类的实例,使用instanceof更为合适;而如果你只需要判断一个值的基本类型,使用typeof即可。

3.说说你对闭包的理解?闭包使用场景

闭包是指一个函数能够访问并且操作它定义时所在的词法作用域中的变量,即使在函数外部调用时,这些变量已经脱离了它们原先所在的作用域。简而言之,闭包就是函数+其词法环境的组合。

闭包的理解可以从两个方面来看:

1.函数和词法作用域:在JavaScript中,每个函数都具有自己的词法作用域,它定义了函数内部的变量和函数,以及外部环境中的变量和函数。函数在定义时就会创建一个词法作用域,并将其与函数绑定在一起。

2.闭包的形成:当一个函数引用了在其定义时所在的词法作用域中的变量(即使函数在定义时所在的作用域已经销毁),该函数就形成了闭包。闭包将这些引用的变量绑定在自己的作用域链中,使得这些变量在函数外仍然可以被访问和使用。

闭包的使用场景包括但不限于:

  1. 封装私有变量:通过使用闭包可以模拟类似私有变量的概念,保护数据的安全性,避免全局作用域的污染。

  2. 记忆上下文:闭包可以用于创建记忆函数,即函数返回另一个函数,而返回的函数仍然可以访问其定义时所在的词法作用域中的变量。这种特性可以用来缓存计算结果,提高性能。

  3. 实现模块化:通过使用闭包,可以创建对立的模块,将相关的变量和函数封装在一起,实现模块化的开发和组织代码。

需要注意的是,闭包肯能会带来内存泄漏的问题,因为闭包中引用的外部变量无法被及时释放。因此,在使用闭包时要注意管理内存,及时释放不再使用的引用。

4. bind、call、apply 区别?如何实现一个bind?

bindcallapply都适用于改变函数的执行上下文(即函数内部的this指向),他们之间的区别如下:

  1. callapply用法类似,他们都会立即调用函数,并且明确指定函数执行时的this值和参数:
  • call:接受一个参数列表,第一个参数为函数执行时的this值,后续参数为函数的参数。
  • apply:接受两个参数,第一个参数为函数执行时的this值,第二个参数为一个参数数组(可以是数组字面量或类数组对象)。
  1. bindcallapply不同,它返回一个新函数,并且并不立即调用原函数,而是在新函数执行时才调用原函数。同时,bind也允许你预先指定参数:
  • bind:接受一个参数作为函数执行时的this值,后续参数为预设的参数。

现在来给出一个简单的例子:

Function.prototype.myBind = function(context, ...presetArgs) {
  const fn = this; // 原函数
  return function(...args) {
    return fn.apply(context, [...presetArgs, ...args]);
  };
};

这里通过在Function的原型上定义myBind方法,在调用myBind时,将原函数保存在fn变量中,然后返回一个新函数。新函数在执行时,会将之前传入的this值、预设的参数和新传入的参数合并,调用原函数。这样就实现了一个简单的bind函数。

需要注意的是,上述实现只是一个简化版本,并未处理柯里化和构造器使用时的情况。在实际开发中,可以考虑使用Function.prototype.bind原生方法,它已经内置在JavaScript环境中,能够正常处理更多复杂的情况。

5.说说你对事件循环的理解

事件循环是JavaScript中用来处理异步操作的机制,它是实现单线程异步非阻塞执行的核心。事件循环机制确保了代码的执行顺序,同时允许异步操作在适当的时机执行。

事件循环的运行过程如下:

1.执行同步任务:首先,JavaScript会执行当前执行上下文中的同步任务,按照代码的顺序一行一行地执行。

2.处理异步任务队列:当遇到异步任务时,JavaScript将其交给相应的处理机制,并不会等待异步任务执行完毕。异步任务的结果将被放入任务队列中。

  • 定时器任务:通过setTimeoutsetInterval等函数创建的异步任务,会被添加到定时器任务队列中,等待指定的时间后执行。
  • 网络请求和事件监听器:比如通过XMLHttpRequest发起的异步请求、DOM事件监听等,当异步操作完成或事件触发时,对应的回调函数会被添加到任务队列中。
  • Promise:通过Promise对象的then方法注册的回调函数在异步操作完成后会被添加到任务队列中。

3.执行异步任务队列:当所有的同步任务执行完毕后,JavaScript会开始处理异步任务队列中的任务,按照顺序一个一个地执行。

  • 微任务(Microtask)队列:Promise的回调函数、queueMicrotask创建的任务会放入微任务队列,优先级高于宏任务。
  • 宏任务(Macrotask)队列:定时器任务、IO任务、渲染任务等属于宏任务,放入宏任务队列中。

在每次事件循环中,JavaScript会先执行微任务队列中的所有任务,直到微任务队列为空,然后再执行宏任务队列中的一个任务。循环执行上述步骤,形成事件循环。

通过事件循环,JavaScript能够处理异步操作,避免了阻塞其他任务的问题,保证了程序的执行顺序。理解事件循环对于编写高效的异步代码非常重要。

6.DOM常见的操作有哪些

DOM是HTML文档的编程接口,它提供了一组操作方法和属性,用于访问、操作和更新HTML文档的元素。常见的DOM操作包括:

1.元素选择和查询:

  • getElementById(id):通过元素id选择并返回一个元素对象。
  • getElementsByClassName(className):通过类名选择并返回一个元素对象集合。
  • getElementsByTagName(tagName):通过标签名选择并返回一个元素对象集合。
  • querySelector(selector):通过CSS选择器选择并返回匹配的第一个元素对象。
  • querySelectorAll(selector):通过CSS选择器选择并返回匹配的所有元素对象。

2.属性操作:

  • getAttribute(name):获取元素的属性值。
  • setAttribute(name, value):设置元素的属性值。
  • removeAttribute(name):移除元素的指定属性。

3.文本内容操作:

  • innerHTML:获取或设置元素内的HTML内容。
  • innerText或textContent:获取或设置元素内的纯文本内容。

4.样式操作:

  • style.property:获取或设置元素的CSS样式属性。
  • classList.add(className):添加一个类名。
  • classList.remove(className):移除一个类名。

5.节点操作:

  • createElement(tagName):创建一个元素节点。
  • createTextNode(text):创建一个文本节点。
  • appendChild(node):添加一个子节点。
  • removeChild(node):移除一个子节点。

6.事件操作:

  • addEventListener(event, listener):为元素添加事件监听器。
  • removeEventListener(event, listener):移除元素的事件监听器。

这只是一些常见的DOM操作,实际上DOM提供了非常丰富的方法和属性,以支持对HTML文档的全面控制和操作。

7.说说你对BOM的理解,常见的BOM对象你了解哪些?

BOM(浏览器对象模型)是指浏览器提供的一组对象和方法,用于与浏览器窗口进行交互,控制和操作。BOM允许开发者通过JavaScript代码来访问和操作浏览器窗口、导航历史、屏幕尺寸等信息。以下是BOM中常见的对象:

1.Window对象:表示浏览器窗口,也是BOM的顶层对象。

  • location:提供浏览器当前页面的URL信息,用于导航和操作URL。
  • navigator:提供浏览器和操作系统的相关信息。
  • document:表示当前窗口中加载的HTML文档。
  • history:提供浏览器导航历史记录的操作方法。
  • console:提供在浏览器控制台输出消息和调试信息的方法。

2.Screen对象:表示用户屏幕的信息。

  • width:屏幕宽度。
  • height:屏幕高度。
  • availWidth:可用的屏幕宽度(去除任务栏等额外空间)。
  • availHeight:可用的屏幕高度(去除任务栏等额外空间)。

3.Location对象:表示当前窗口的URL信息,可用于获取和修改URL。

  • href:当前页面的URL。
  • reload():重新加载当前页面。
  • assign(url):加载前的URL并替换当前页面。
  • replace(url):加载新的URL并不生成新的历史记录。

4.Navigator对象:提供了关于浏览器和操作系统的详细信息。

  • userAgent:浏览器的用户代理字符串。
  • platform:操作系统平台信息。
  • language:浏览器的首选语言。

5.History对象:表示浏览器的导航历史记录,可以进行前进、后退等操作。

  • length:历史记录中的页面数量。
  • back():后退到上一个页面。
  • forward():前进到下一个页面。
  • go(n):在历史记录中移动n步。

这些是BOM中较常见的对象,通过它们我们可以获取和操作浏览器窗口、URL、导航历史、屏幕尺寸等信息,实现与浏览器的交互和控制。

8.Javascript本地存储的方式有哪些?区别及应用场景?

JavaScript提供了几种本地存储的方式,用于在浏览器中存储数据,包括:

1.Cookie(HTTP Cookie):Cookie是一小段存储在用户计算上的数据,它在浏览器和服务器之间进行数据交换。Cookie可以设置过期时间,最大存储量较小(一般为4KB),并且随着每个HTTP请求发送到服务器。Cookie通常用于存储会话相关的信息、用户首选项等。

2.Web Storage(localStorage和sessionStorage):Web Storage是HTML5中的一种机制,提供了本地数据存储的方式,不会随每个HTTP请求发送到服务器。它包括两种类型:

  • localStorage:提供了持久性的本地存储,存储的数据会永久保存在浏览器中,即使关闭浏览器或重新打开页面也不会丢失。最大存储量较大(一般为5MB),可以存储较大的数据量。适用于长期存储、持久性的数据,如用户偏好设置、本地缓存等。
  • sessionStorage:提供了会话级别的本地存储,存储的数据只在当前会话(窗口或标签页)有效,关闭会话后数据会被清除。适用于临时性的数据存储,如临时会话数据、表单保存等。

3.IndexedDB:IndexedDB是一种高级的客户端存储API,用于在浏览器中保存结构化的数据。它提供了一个类似数据库的环境,可以进行数据的存储、检索和索引。IndexedDB存储量较大,可以存储复杂的数据结构,并支持高级的查询和事务操作。它适用于需要大量数据存储和离线应用程序。

区别和应用场景:

一、存储的时间有效期不同

  1. cookie的有效期是可以设置的,默认的情况下是关闭浏览器后失效
  2. sessionStorage的有效期是仅保持在当前页面,关闭当前会话页或者浏览器后就会失效
  3. localStorage的有效期是在不进行手动删除的情况下是一直有效的

二、存储的大小不同

  1. cookie的存储是4kb左右,存储量较小,一般页面最多存储20条左右信息
  2. localStorage和sessionStorage的存储容量是5Mb(官方介绍,可能和浏览器有部分差异性)

三、与服务端的通信

  1. cookie会参与到与服务端的通信中,一般会携带在http请求的头部中,例如一些关键密匙验证等。
  2. localStorage和sessionStorage是单纯的前端存储,不参与与服务端的通信

四、对于浏览器的支持

  1. cookie出现的时间较早,目前见到的浏览器都支持
  2. localStorage和sessionStorage出现的时间较晚,对于版本较低的浏览器不支持(比如IE8版本以下的都不支持)

选择使用哪种本地存储方式取决于具体的需求和场景。如果只是存储一些简单的会话信息或用户偏好设置,可以使用Cookie或Web Storage。如果需要存储大量数据或需要复杂的查询和事务操作,可以选择IndexedDB。

9.什么是防抖和节流?有什么区别?如何实现?

防抖(Debounce)和节流(Throttle)都是用于控制事件触发频率的技术,主要应用于优化性能和避免过度触发事件的问题。

  1. 防抖(Debounce):防抖是指在事件触发后等待一段时间(例如1000毫秒)后再执行回调函数,如果在这段时间内重新触发了相同事件,则重新计时。也就是说,只有在事件触发的间隔超过指定时间后,才会执行回调函数。

    应用场景:输入框搜索,只在用户停止输入一段时间后才进行搜索请求。

    实现方法:可以使用定时器来实现防抖。在事件触发时,先清除之前的定时器,然后重新设置一个新的定时器。

  2. 节流(Throttle):节流是指在一定时间间隔内(例如1000毫秒)最多触发一次回调函数。也就是说,在指定的时间间隔内,无论事件触发了多少次,只会执行一次回调函数。

    应用场景:滚动事件,限制一段时间内触发的次数,减小事件处理的频率。

    实现方法:常见的实现方式是使用时间戳或定时器。在事件触发时,记录当前时间戳,然后在下一次触发事件时与上一次记录的时间做比较,如果超过指定的时间间隔,则执行回调函数并更新时间戳。

区别:

  • 防抖是在等待一段时间后执行回调函数,如果在这段时间内重新触发了相同事件,则重新计时。
  • 节流是在一定时间间隔内最多触发一次回调函数。

实现方法可以根据具体场景选择使用定时器或时间戳,并在事件触发时进行相应的处理。这些技术有助于提高性能、减少不必要的事件处理和网络请求。

10.如何通过JS判断一个数组

1.通过JS可以使用Array.isArray()方法来判断一个对象是否为数组。该方法会返回一个布尔值,表示被判断的对象是否为数组。下面是使用Array.isArray()方法判断数组的示例代码:

let arr = [1, 2, 3];

if (Array.isArray(arr)) {
  console.log("arr 是一个数组");
} else {
  console.log("arr 不是一个数组");
}

在上述示例中,Array.isArray(arr)返回true,因为arr是一个数组,所以会在控制台输出 “arr 是一个数组”。

注意:在使用Array.isArray()方法时,需要注意兼容性。如果在某些较旧的浏览器或环境中,可能不支持Array.isArray()方法,此时可以考虑使用其他方法来判断对象是否为数组,例如使用Object.prototype.toString.call()方法:

let arr = [1, 2, 3];

if (Object.prototype.toString.call(arr) === "[object Array]") {
  console.log("arr 是一个数组");
} else {
  console.log("arr 不是一个数组");
}

这种方法会返回对象的类型字符串,如果是数组,则返回"[object Array]"。使用这种方式进行数组判断通常更为兼容。

2.通过instanceof判断

instanceof运算符用于检验构造函数的prototype属性是否出现在对象的原型链中的任何位置,返回一个布尔值。

let a = [];
a instanceof Array; //true
let b = {};
b instanceof Array; //false

instanceof运算符检测Array.prototype属性是否存在于变量a的原型链上,显然a是一个数组,拥有Array.prototype属性,所以为true。

3.通过constructor判断

实例的构造函数属性constructor指向构造函数,那么通过constructor属性也可以判断是否为一个数组。

4.通过Object.prototype.toString.call()判断

Object.prototype.toString().call()可以获取到对象的不同类型,例如

let a = [1,2,3]
Object.prototype.toString.call(a) === '[object Array]';//true

它强大的地方在于不仅仅可以检验是否为数组,比如是否是一个函数,是否是数字等等
甚至对于多全局环境时, Object.prototype.toString().call()也能符合预期处理判断。

11.new操作符具体干了什么

new操作符用于创建了一个新的对象实例,他具体做了以下几个步骤:

  1. 创建了一个空对象: new操作符会创建一个空对象,这个空对象将会成为新创建的对象实例。
  2. 设置原型链关联:新创建的空对象会被关联到一个构造函数的原型对象上。通过[Prototype]来实现原型链的关联,使新对象能够访问构造函数原型对象上的属性和方法。
  3. 执行构造函数:将新创建出来的空对象作为this的值传递给构造函数,并执行构造函数中的代码。构造函数中的代码可以对新对象进行初始化操作,给它添加属性、设置属性值,或执行其他操作。
  4. 返回新对象:如果构造函数内部没有显示返回其他对象,那么new操作符默认返回新创建的对象。如果构造函数中有显示的返回语句返回一个对象,而不是新创建的对象。

总结来说,new操作符会创建一个新的对象实例,并将这个新对象与构造函数的原型对象进行关联。然后,通过调用构造函数来对新对象进行初始化。最后,如果构造函数没有显式返回其他对象,则new操作符会将新对象作为结果返回。

在 JavaScript 中,使用 new操作符可以实例化自定义的构造函数,创建具有相同属性和方法的新对象。这个新对象可以使用构造函数中定义的方法,并且可以访问构造函数原型链上的其他属性和方法。这使得 new操作符在面向对象编程中起到了重要的作用。

12. null,undefined的区别

1.undefined是一个表示未定义值的类型,表示一个变量声明但未被赋值,或者访问对象属性或数组元素时不存在的值,当一个函数没有显示地返回值时,其默认返回值为undefined
2.null是一个表示空值的类型,它是一个有意赋值为空的对象,使用null可以将一个变量显示地设置为空

总结来说,undefined表示未定义,通常用于变量声明但未赋值的情况;而null表示空值,通常用于显示地将一个变量设置为空。它们在类型上的区别、使用时的语义和场景略有不同,但都代表为空值。

13. javascript 代码中的"use strict";是什么意思

这个指令告诉JavaScript引擎在解析和执行代码时采用严格模式(strict mode)。

在严格模式下,一些容易导致错误或不安全的行为将被禁止,同时引入了一些新的限制和功能。严格模式有助于避免一些常见的错误,并提高代码的可靠性和效率。

在严格模式下,一些主要的变化包括:

  • 变量必须先声明在使用
  • 禁止使用未声明的变量
  • 禁止删除变量、函数和函数参数
  • 函数内部的this值为undefined
  • 禁止使用八进制字面量
  • 禁止使用eval和arguments作为变量名
  • 限制了eval和arguments的行为
  • 禁止使用with语句
  • this指向的对象在全局环境下不再是全局对象

通过使用严格模式,可以编写更加规范、安全和可维护的JavaScript代码。

14. 同步和异步的区别?

同步:在同步操作中,程序会按照顺序执行代码。每一行代码的执行必须在上一行代码执行完成之后才能开始。如果遇到一个耗时的操作,程序会停在那里等待操作完成后再继续执行下一行代码。这意味着代码的执行是阻塞的,只有当前代码执行完毕后才能进行下一步操作。

异步:在异步操作中,程序不会等待一个操作完成后再进行下一个操作。相反,它会继续执行后续的代码,而不管前面的操作是否完成。当异步操作完成后,程序会收到通知,然后执行相应的回调函数或继续执行特定的代码,这种方式可以提高程序的性能和响应性,因为不需要等待耗时操作完成。

异步操作通常用于需要等待时间的操作,例如从服务器获取数据、读取文件或执行网络请求等。通过使用回调函数、promise、async/await或事件监听等机制,可以管理异步操作的流程和处理异步操作的结果。

总结来说,同步是按照代码的顺序逐行执行,代码的执行时阻塞的,而异步是不会阻塞代码的执行,可以将耗时的操作交给其他线程或进程处理,等待操作完成后再进行响应的处理。异步有助于提高程序的性能和响应性。

15.谈一谈箭头函数与普通函数的区别

箭头函数是ES6引入的新的函数语法,相比普通函数,它们有以下几个区别:

1.语法简洁:箭头函数的语法更为简洁,通常可以用更少的代码来表达相同的功能。它可以使用=>符号来定义函数,省略了function关键字和函数体的花括号。

2.没有自己的this:箭头函数没有自己的this值,它会捕获并继承外层函数的this值。这意味着在箭头函数内部,无论在何处调用,this都指向的是定义时的外层作用域的this值。而普通函数的this值则由调用方式决定。

3.没有arguments对象:箭头函数没有自己的arguments对象,它会继承外层函数的arguments对象。如果需要使用类似的功能,可以使用剩余参数(…args)来替代。

总的来说,箭头函数相对于普通函数更加简洁和具有更明确的作用域规则。它们适用于需要较短的函数体并希望继承外层this值的情况。然而,由于它们没有自己的thisarguments,所以并不适用于所有的函数场景。

16.JS 数组和对象的遍历方式,以及几种方式的比较

在JavaScript中,遍历数组和对象有多种方式。下面是一些常见的遍历方式及其比较:

1.for循环

  • 数组遍历:使用for循环和数组的length属性可以遍历数组的每个元素。
  • 对象遍历: 使用for...in循环可以遍历对象的每个可枚举属性。

比较:for循环是一种常用的遍历方式,对于数组和对象都可以使用。但需要注意的是,再使用for…in遍历对象时,会遍历对象的原型链上的可枚举属性,因此建议使用hasOwnProperty进行属性的过渡。

2.forEach()方法:

  • 数组遍历:使用数组的forEach方法可以遍历数组的每个元素。它接受一个回调函数作为参数,回调函数会在每个元素上被调用。
  • 对象遍历:对象没有直接提供forEach方法,但可以使用Object.keys(obj)先获取对象的属性数组,然后再使用forEach方法进行遍历。

比较:forEach方法提供了更简洁的语法来遍历数组和对象。它适用于需对每个元素执行相同操作的场景。

3.for…of循环:

  • 数组遍历:使用for...of循环可以遍历数组的每个元素。它会依次访问数组中的每个值,而非索引。
  • 对象遍历:对象不能直接使用for...of循环进行遍历,因为它不是可迭代对象。

比较:for...of循环提供了简洁的语法,并且可以直接遍历数组中的值,而不是索引。然而,它并不使用于对象遍历。

综上所述,对于数组遍历来说,forEach方法和for...of循环是最常用的、简洁有效的方式。对于对象遍历,for...inObject.keys(obj).forEach是最常用的方式。选择遍历方式需要根据具体需求和代码的简洁性来考虑。

17.如何解决跨域问题

解决跨域问题的方法取决于具体的情况和控制的权限,下面列出了一些常见的跨域问题解决方法:

  1. 使用CORS(Cross-Origin Resource Sharing):CORS是一种浏览器机制,用于控制跨域请求的许可权限。在服务器端设置响应的响应头,允许特定的域名、端口和方法来访问资源。大多数现代浏览器都默认支持CORS,通过配置服务器返回的响应头信息即可实现跨域资源的交互。
  2. JSONP(JSON with Padding): JSONP是一种在前端实现跨域请求的解决方案。它利用<script>标签可以跨越引用资源的特性,通过在请求中指定一个回调函数名,并将数据作为函数参数包裹返回,使得跨域请求得以实现。但需要服务器端的支持才能返回相应格式的数据。
  3. 代理服务器:可以设置一个代理服务器,将所有跨域请求通过该服务器转发,然后再返回结果给前端,从而绕过浏览器的同源策略限制。这种方式需要服务器进行相应的配置,将请求转发到目标服务器并返回结果。
  4. WebSocket: WebScoket是一种在浏览器和服务器之间建立持久化连接的协议,它允许双向通信。WebSocket不受同源策略的限制,可以直接进行跨域通信。
  5. 后端代理和反向代理: 在服务器端设置代理或反向代理,将跨域请求转发到目标服务器,然后将响应返回给前端。这种方式是一种常见的解决跨域问题的方法,可以在代理服务器上进行相应的配置和转发。

需要注意的是,在跨域请求中,浏览器会进行预测检查,发送OPTIONS请求来验证服务器是否支持跨域请求。在服务器端进行相应的配置,允许OPTIONS请求通过,可以确保跨域请求的正常进行。

18. XML和JSON的区别

  1. JSON是JavaScript object Notation;XML是可扩展标记语言
  2. JSON是基于JavaScript语言;XML源自SGML
  3. JSON是一种表示对象的方式;XML是一种标记语言,使用标记结构来表示数据项
  4. JSON不提供对命名空间的任何支持;XML支持名称空间
  5. JSON支持数组;XML不支持数组
  6. JSON不使用结束标记;XML有开始和结束标签
  7. JSON的安全性较低;XML比JSON更安全
  8. JSON不支持注释;XML支持注释
  9. JSON仅支持UTF-8编码;XML支持各种编码

19.谈谈你对webpack的看法

webpack是一个功能强大的模块打包工具,它在现代前端开发中扮演着重要的角色。我对webpack有一些观点,如下:

  1. 模块化打包:webpack允许开发者将项目拆分成多个模块,并通过依赖关系将它们打包到单个文件中。这使得代码更加可维护和可复用,并且能够提高应用程序的性能。
  2. 强大的插件系统:webpack的插件系统非常丰富,可以通过插件来扩展和定制webpack的功能。例如,使用插件可以优化代码、压缩文件、提取共享的代码等。这些插件大大提高了开发效率和项目质量。
  3. 模块热替换:webpack支持热模块替换,这是一个非常有用的功能。在开发过程中,它可以实时更新修改的模块,而无需刷新整个页面。这加快了开发效率,并提供了更好的开发体验。
  4. 支持多种类型:webpack不仅支持JavaScript模块,还可以处理其他资源类型,如CSS、图片、字体等。这让开发者可以将所有的前端资源都整合在一个打包工具中,简化了开发流程。
  5. 优化和性能:webpack具有许多优化功能,例如代码分割、懒加载、缓存等,可以帮助减少文件的大小并提升应用程序的加载速度。同时,webpack还支持异步加载,可以将应用程序划分为多个小块,按需加载,提高用户体验。

总的来说,webpack是一个功能强大且灵活的工具,它能够帮助开发者更高效的构建现代化的前端应用程序。它的模块化打包和丰富的插件系统是它的主要特点,而且通过优化和性能方面的功能,他能够提升项目的质量和用户体验

20.说说webpack中常见的Loader?解决了什么问题?

  1. Babel Loader:用于将ES6的JavaScript代码转换为浏览器可执行的ES5代码,解决了浏览器对新语法的兼容性问题。
  2. CSS Loader:用于加载和解析CSS文件,并将其添加到页面中,解决了在浏览器中引入CSS的问题。
  3. Style Loader:用于将CSS代码注入到页面的style标签中,解决了将CSS应用到应用程序的问题。
  4. SASS Loader:用于将SASS 或SCSS文件转换为CSS代码,解决了在Webpack中使用SASS或SCSS的问题。
  5. File Loader:用于加载文件,并将其输出到指定的目录中,解决了在Webpack中处理图片、字体等文件的问题。
  6. URL Loader:类似于File Loader,但对于较小的文件,可以将其转换为Base64编码的URL,减少网络请求,提高性能。
  7. Image Loader:用于加载和优化图片文件,解决了在Webpack中处理图片的问题。

这些Loader通过配置在Webpack中,可以处理不同类型的文件,并将它们转化为模块,使用开发者可以在项目中引入和使用它们。Loader解决了在前端开发中的一些常见问题,如新语法的转换、样式的模块化。资源的加载和优化等。它们能够让开发者更方便的处理和管理前端资源,提高开发效率和应用程序性能。

21.说说webpack中常见的Plugin?解决了什么问题?

  1. HtmlWebpackPlugin:根据模板自动生成HTML文件,并动态将打包生成得文件引入其中,解决了手动创建HTML文件得问题。
  2. MiniCssExtractPlugin:将CSS代码从打包生成得js文件中提取出来,生成独立得CSS文件,解决了将样式与逻辑分离的问题。
  3. CleanWebpackPlugin:在每次构建前清理输出目录,解决了构建时遗留旧文件的问题。
  4. DefinePlugin:允许在编译时创建全局变量,可以在代码中使用这些变量,解决了在代码中直接硬编码常量的问题。
  5. HotModuleReplacementPlugin:启用模块热替换,在应用程序运行时更新修改的模块,解决了开发过程中频繁刷新页面的问题。
  6. UglifyJsPlugin:用于压缩和混淆JS代码,减小文件体积,解决了文件大小过大的问题。
  7. CopyWebpackPlugin:将指定的文件或文件夹复制到构建目录中,解决了静态资源的复制问题。

这些Plugin通过在Webpack配置中引用和配置,可以在打包过程中执行特定的功能和操作。他们解决了在前端开发过程中的一些常见问题,如自动生成HTML文件,提取CSS,清理输出目录等。同时,它们还提供了许多优化和扩展功能,如压缩代码,创建全局变量等,帮助开发者更好地优化和管理项目。Plugin的作用是丰富了Webpack的功能,提高了开发效率和项目质量

22.webpack的打包原理

  1. 入口文件分析:webpack会根据配置文件中指定的入口文件来开始分析整个应用的依赖关系。它会从入口文件开始递归的解析模块间的依赖关系,找出所有被引用的模块
  2. 加载器处理:在解析关系的过程中,webpack会根据文件的类型选择相应的加载器进行处理。加载器可以对模块进行各种转换和处理,例如将ES6代码转换为ES5、对样式表进行处理、压缩图像等。
  3. 依赖图构建:webpack通过解析模块间的依赖关系构建一个依赖图,其中包含了每个模块及其依赖的所有模块。这个依赖图反映了模块间的依赖关系,webpack根据这个图来确定模块的加载顺序。
  4. 输出文件生成:webpack确定了模块的加载顺序后,会将所有模块打包成一个或多个输出文件。这些输出文件可以是JavaScript文件、样式表文件、图像文件等等,具体个数和类型取决于配置文件中的设置。
  5. 代码分割和优化:webpack可以根据配置文件中的设置对代码进行分割和优化。它可以将公共的模块提取出来,形成一个单独的文件,以减少重复加载和提高性能。同时,webpack也可以对文件进行压缩和混淆等优化操作。
  6. 结果输出:webpack将打包生成的文件输出到指定的目录中,供浏览器或服务器加载和使用。输出的文件可以是开发环境下的未压缩版本,也可以是生产环境下的压缩和优化后的版本。

通过以上的步骤,webpack实现了将各个模块打包成静态资源的过程,使得前端应用可以高效地加载和执行。

23.如何优化webpack打包速度

  1. 配置合理的模式:根据开发和生成环境的不同,合理选择webpack的模式,在生产环境下,使用production模式可以启用各种优化选项,例如代码压缩,变量混淆等。
  2. 精简打包的范围:通过配置entryinclude选项,只打包必要的模块,减少不必要的依赖的分析和处理。
  3. 使用代码分割:通过webpack的代码分割功能,将公共的模块提取出来,形成一个单独的文件,以减少冗余和重复加载。
  4. 合理配置加载器和插件:评估每个加载器和插件的性能开销,选择高效的工具,并配置合理的选项。避免无谓的,不必要的功能和转换操作。
  5. 使用缓存:在webpack配置中启用缓存,以便在增量编译时只重新构建修改过的模块,减少整体的重新编译时间。
  6. 多进程/多实例打包:使用工具如HappyPack或thread-loader将webpack的构建过程分解为多个子进程或实例,充分利用多核CPU的能力加速构建。
  7. 减小输出文件体积:开启代码压缩和文件压缩选项,减小输出文件的体积,提高加载速度。
  8. 使用懒加载:对于大型应用,使用懒加载策略,只在需要时加载模块,而不是一次性加载所有模块,以提高初始化加载速度

通过以上优化策略,可以显著提升webpack的打包速度,提高前端应用的开发效率和用户体验

24.说说你对promise的了解

Promise是一种用于处理异步操作的对象,它可以将异步操作的结果以同步的方式进行处理和返回,Promise有三种状态:pending(进行中)、fulfulled(已成功)和rejected(以失败)。

通过Promise,我们可以将异步操作封装成一个Promise对象,然后通过调用Promise的then方法来处理异步操作的结果。当异步操作成功完成时,Promise对象的状态会变为fulfilled,并且会调用then方法中的回调函数来处理结果。当异步操作失败时,Promise对象的状态会变为rejected,并且会调用catch方法中的回调函数来处理错误。

Promise还提供了一些方法,比如all、race和resolve等。all方法可以接收一个Promise数组作为参数,并在所有Promise对象都成功完成时返回一个新的Promise对象,该对象的结果是一个包含所有Promise结果的数组。race方法可以接收一个Promise数组作为参数,并在任何一个Promise对象成功完成时返回一个新的Promise对象,该对象的结果是第一个成功完成的Promise结果。

总的来说,Promise是一种非常有用的异步编程解决方案,它可以避免回调地狱,并提供了一种更加优雅和可读性更高的方式来处理异步操作。

25.async函数是什么,有什么作用?

async函数是一种用于简化异步操作的语法糖,它可以让我们以更加同步的方式编写异步代码。通过使用async函数,我们可以将异步操作以类似于同步操作的方式进行编写和处理,而不需要显示的使用回调函数或者Promise。

async函数的定义方式是在函数前面加上async关键字。在async函数内部,我们可以使用await关键字来等待一个异步操作的结果。当遇到await关键字时,async函数会暂停执行,直到等待的异步操作完成并返回结果,然后再继续执行后面的代码。

async函数会返回一个Promise对象,该对象的结果是async函数执行的结果。如果在async函数内部使用了return语句返回一个值,那么Promise对象的结果就是这个值。如果在async函数内部抛出一个异常,那么Promise对象的状态就会变为rejected,并且会抛出响应的错误。

async函数的作用是简化异步操作的编写和处理过程,使得异步代码更加易读和易于维护。它可以避免回调地狱,减少嵌套层级,提高代码的可读性和可维护性。同时,async函数还可以配合其他异步操作的解决方案,比如Promise和Generator,来实现更加复杂的异步编程任务。

26.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢

SPA(Single Page Application)即单页面应用,是一种Web应用的架构模式。传统的Web应用每次用户操作都会重新加载整个页面,而SPA则只在首次加载时加载整个页面,后续的操作都是通过AJAX技术动态更新页面的某个部分,实现页面的无刷新切换。

SPA的优点:

  1. 用户体验好:由于只需要加载一次页面,后续的页面切换都是通过异步加载数据来更新,可以提供更快的响应速度和更流畅的用户体验。
  2. 前后端分离:SPA通过前后端分离的方式,前端负责展示和交互逻辑,后端只负责提供API接口,实现了前后端的解耦。
  3. 提高开发效率:SPA使用了现代化的前端框架,如Vue、React等,可以提供丰富的功能和组件,简化了开发过程,提高了开发效率。

SPA的缺点:

  1. 首次加载时间长:由于SPA需要加载整个页面和相关资源,首次加载时间可能会较长,特别是在网络条件差的情况下。
  2. SEO不友好:由于SPA只有一个HTML页面,搜索引擎难以获取到完整的页面内容,对于SEO优化来说有一定的挑战。
  3. 内存占用较高:由于SPA需要一次性加载所有的页面和相关资源,占用的内存较大,对于低配置设备来说可能会有性能问题。

实现SPA应用的关键是前端框架(Vue和React等)来管理页面的路由和状态,并通过AJAX技术来动态加载数据并更新页面。前端框架提供了路由机制,可以根据不同的URL路径来加载不同的组件,并通过组件间的通信来实现页面的切换和数据的更新。同时,前端框架还提供了状态管理机制,可以统一管理应用的状态,使得不同组件之间可以共享数据。最后,通过AJAX技术,前端可以向后端请求数据,并将数据渲染到页面的相应部分,实现页面的更新。

27.SPA首屏加载速度慢的怎么解决

一、什么是首屏加载
首屏时间,指的是浏览器从响应用户输入网站地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
首屏加载可以说是用户体验中最重要的环节

二、加载慢的原因
在页面渲染的过程,导致加载速度慢的因素可能如下:

  • 网络延时问题
  • 资源文件体积是否过大
  • 资源是否重复发送请求去加载了
  • 加载脚本的时候,渲染内容堵塞了

三、常见的几种SPA首屏优化方式:

  • 减小入口文件体积
  • 静态资源本地缓存
  • UI框架按需加载
  • 图片资源的压缩
  • 组件重复打包
  • 开启GZip压缩
  • 使用SSR

28. 说说你对作用域链的理解

  • 作用域链是一种查找变量和函数的机制,它是由当前执行环境和其所有父级执行环境的变量对象组成的链式结构。当在一个执行环境中访问变量或函数时,会首先在当前执行环境的变量对象中查找,如果找不到,则会沿着作用域链向上查找,直到找到对应的变量和函数,或者到达最外层的全局对象。
  • 作用域链的创建是在函数定义时确定的,它与函数的定义位置有关,当函数被调用时,会创建一个新的执行环境,其中包含一个新的变量对象,并将其添加到作用域链的前端。这样,函数内部就可以访问其所在作用域以及其外部作用域中的变量和函数,形成了一个作用域链。
  • 通过作用域链的机制,函数可以访问外部作用域中的变量,但外部作用域不能访问函数内部的变量,这就实现了变量的封装和保护。

总结

  • 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
  • 简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值