2024 web前端面试题(持续更新)

1.js有哪些基本数据类型

Undefined 、Null、Boolean、Number、String、Symbol(es6新增)、Bigint(谷歌67版本新增)

2.js有哪些引用数据类型

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

3.js有哪些内置对象?

Object、Array、Boolean、Number,String,date,math,error,promise,regExp,JSON,Global,map,set

4. Symbol有什么作用

Symbol类型的key是不能通过Object.keys()或者for…in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外。

5.JSON.stringify深拷贝对象有什么弊端

无法处理循环引用:当对象中存在循环引用时,即对象的某个属性指向该对象本身,JSON.stringify()会抛出异常。
无法拷贝非枚举属性和方法:JSON.stringify()只能拷贝对象自身的可枚举属性,并且会忽略函数和Symbol类型的属性。
无法拷贝特殊的对象属性:JSON.stringify()不会拷贝对象的原型链上的属性。
无法处理日期对象:将日期对象转换为JSON字符串后再使用JSON.parse()解析时,日期对象会变成字符串,而不是重新生成日期对象。
无法处理正则表达式对象:正则表达式对象在转换为JSON字符串后会变成空对象。
无法处理undefined和function:JSON.stringify()会将undefined和函数直接转换为null。
无法处理Infinity和NaN:JSON.stringify()会将Infinity和NaN转换为null。
特殊的深拷贝可以用三方库实现对象的深拷贝,比如:lodash、jQuery

6.讲下你对堆栈的理解,出入顺序又是怎样的

在计算机领域中,堆栈是两种数据结构。
堆:队列优先,先进先出;由操作系统自动分配释放 ,存放的是基础数据值。
栈:先进后出;存放的是引用数据类型地址指针,动态分配的空间 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

7.有哪些原生遍历方法

1.for循环语句,最基础的遍历,以数组的下标为索引,主要对数组元素进行遍历。
2. for in
循环主要用于遍历普通对象,i 代表对象的 key 值,obj[i] 代表对应的 value, 当用它来遍历 另外 for in 循环的时候,不仅遍历自身的属性,还会找到 prototype 上去, 所以最好在循环体内加一个判断,就用 obj[i].hasOwnProperty(i),这样就避免遍历出太多不需要的属性
3. whild 和 do while 循环
4. for of (ES6)。
for of 循环是 Es6 中新增的语句,用来替代 for in 和 forEach,不允许遍历 对象
它允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代(Iterable data)的数据结构,注意它的兼容性。
5. map()方法
map() 返回一个新数组。
map() 不会对空数组进行检测。
map() 不会改变原始数组。
6. filter() 方法。
filter 方法是 Array对象内置方法,它会返回通过过滤的元素,不改变原来的数组。
filter() 方法返回一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
7.some()
some() 方法用于检测数组是否存在元素满足指定条件(函数提供)。只要有一个元素满足则返回true,并不再继续往下判断,不改变原数组。
8.every()
every() 方法用于检测数组中是否所有元素都符合指定条件(通过函数提供),返回 boolen。
9.for eatch()
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
forEach 是否改变原数组取决于它如何被使用以及数组中元素的类型对于基本数据类型,forEach 不会改变原数组;对于引用数据类型,forEach 可以改变原数组的元素内容,但通过重新赋值来替换元素则不会改变原数组

8 whild和do while循环的区别

1、while循环是先判断条件再执行循环体,而do-while循环是先执行循环体再判断条件;
2、while循环是先判断循环条件,如果条件满足,则执行循环体中的代码,然后再次判断条件,如此循环,直到条件不满足时跳出循环,而do-while循环则是先执行循环体中的代码,然后再判断循环条件是否满足,如果条件满足,则继续执行循环体中的代码,如此循环,直到条件不满足时跳出循环。

9 前端有哪些设计模式并描述下作用

这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
2. 设计模式分类
创建型:创建对象的同时隐藏创建逻辑的方式。
工厂模式
在工厂模式中,我们在创建对象时不会对外部暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

目的:定义一个创建对象的接口,可以方便我们大量创建不同类型的对象,统一集中管理。

应用场景:在不同场景需要创建不同实例时。

应用实例:使用工厂类创建不同类型的产品。
单例模式
目的:确保全局只有一个实例对象
应用场景:为了避免重复新建,避免多个对象存在相互干扰。(当需要一个对象去贯穿整个系统执行任务时才会用到单例模式,除此之外的场景应避免单例模式的使用。
结构型:关注类和对象的组合,简化系统的设计
外观模式
外观模式隐藏系统的复杂性,并向外部提供了一个可以访问系统的接口。它向现有的系统添加一个接口,来隐藏系统的复杂性。

目的:通过为多个复杂的子系统提供一个一致的接口,隐藏系统的复杂性
应用场景:
(1)为复杂的模块或子系统提供外界访问的模块。
(2)子系统相对独立。
代理模式
在代理模式中,一个类代表另一个类的功能。

目的:用一个代理对象来控制对另一个对象的访问

应用场景:
(1)想在访问一个类时做一些控制
(2)由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

应用实例:
代理加载图片类,若缓存中有,则直接返回缓存数据;若没有,则调用加载图片类。

行为型:关注对象之间的通信,增加灵活性。
策略模式
在策略模式中,一个类的行为或其算法可以在运行时更改。我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

目的:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。优化 if-else 分支。

应用场景:一个系统有许多许多类,而区分它们的只是他们直接的行为。

应用实例:
用策略模式将多种运算整合并判断

迭代器模式
迭代器模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

迭代器模式解决了此些问题:

提供一致的遍历各种数据结构的方式,而不用了解数据的内部结构
提供遍历容器(集合)的能力而无需改变容器的接口
一个迭代器通常需要实现以下接口:

hasNext():判断迭代是否结束,返回Boolean
next():查找并返回下一个元素
目的:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示

应用场景:需要对某个对象进行操作,但是又不能暴露内部
应用实例
(1) 为 js 数组实现一个迭代器
观察者模式
类似订阅发布,比如普通的点击事件

中介者模式
类似事件总线,他们通过一个第三方来进行数据交互
访问者模式
使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。

目的:解耦数据结构与数据操作。

应用场景:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

应用实例:对于公司财务数据,财务人员关心收入与支出数据,而老板关心盈利数据
详细说明

10 js怎么判断数据类型

1.Object.prototype.toString方法:这种方法可以返回一个对象的内部属性[[Class]]的值,从而获取其类型
2. constructor :在类的原型上都会带有一个constructor属性,用来存储当前类本身。任何对象在其__proto__上有一个constructor属性用来存储当前类本身。利用这一点,可以获取到某个实例的constructor属性值是否为所属的类。
3. instanceof
4. typeof

11. sort()方法

js排序方法,sort 方法会改变原数组或列表。

12.图片懒加载

IntersectionObserver API 提供了一种创建IntersectionObserver 对象的方法,对象用于监测目标元素与视窗(viewport)的交叉状态,并在交叉状态变化时执行回调函数,回调函数可以接收到元素与视窗交叉的具体数据。

if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
        entries.forEach(function(entry) {
            if (entry.isIntersecting) {
                let lazyImage = entry.target;
                lazyImage.src = lazyImage.dataset.src;
                lazyImage.classList.remove("lazy");
                lazyImageObserver.unobserve(lazyImage);
            }
        });
    });

13.ts与js有什么区别

TypeScript 是 JavaScript 的超集,它在 JavaScript 的基础上添加了强类型、接口、类、泛型等特性。
有自动上文类型推断。

14.TypeScript 比 JavaScript 更好吗?

JavaScript 非常适合更简单的应用程序,因为它可以在所有平台(跨平台)上运行并且非常轻量级。另外,与JS的最小开销相比,编译TS代码需要的时间和CPU资源对项目而言会更麻烦。
TS 使代码重构变得更加容易,并且更强调显式类型,使开发人员能够掌握各种组件的交互方式。由于它支持编译时调试,对于处理大型复杂应用程序的团队来说,有一定的好处。

15.ts有哪些基本类型

TypeScript 的基本数据类型有boolean、number 、string 、 array 、 enum 、any 、void。

16 ts中的interface是什么

在TypeScript里,接口的作用就是为你的代码或第三方代码做类型声明,是命名对象类型的一种方式。
ts的接口需要以定义(限制)的角度来理解,只要传入的对象满足接口提到的必要条件,那么它就是被允许的。
还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。
可选属性
语法: 定义属性时,冒号前面加?,例如:interface A {a?:string}
只读属性
语法: 定义属性时, 属性前加readonly,代表这个是只读属性。例如:interface A { readonly a:number }
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性.
可合并
interface声明的对象类型允许合并。不过,如果是含有同名属性,它的类型要保持一致。
可继承
interface声明的对象类型可以继承其他接口对象类型,使用extends扩展。

17 vue-router 的原理

vueRouter有两种模式:一种是Hash模式,一种是History模式。
1.hash模式,主要是hashHistory:hash(“#”)符号的本来作用是加在URL中指示网页中的位置,hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面,每一次改变hash,都会在浏览器的访问历史中增加一个记录,通过监听hashchange事件来执行界面的替换。
2.history模式,我们主要利用这个HTML5History来操作浏览器历史记录栈,主要方法有back(), forward(), go()来读取浏览器路由历史并控制跳转,HTML5新增pushState(), replaceState()2个方法来修改历史信息,调用这两个方法修改历史信息后,虽然当前URL改变了,但浏览器不会立即发送请求该URL,这就满足单页面应用”更新视图但不重新请求页面“的需求,修改浏览器历史记录后会触发popstate事件,可以通过监听popstate事件来执行页面的替换。

18 vue中hash模式和history模式区别

  1. hash模式有#,history模式没有#,更美观
  2. pushState设置的新url是和当前url同源的任意url,hash模式只可以修改#后面的内容,也就是只可以设置与当前同文档的url
  3. pushState设置的新url和当前url相同时也会把记录添加进记录栈中,而hash只有新的和当前的不同的时候才会添加到栈中
  4. history模式刷新会重新请求服务器,需要服务器配置/后面的地址,找不到会404.
  5. hash路由支持低版本的浏览器,而history路由是HTML5新增的API。

19 如何解决vue history模式刷新404异常

安装URL重写模块,配置重写规则,统一指向index.html.或者直接把url重写生成的web.config文件直接放置到网站根目录下即可。

20 闭包是什么

一个函数返回了不属于他作用域内的变量,这种情况叫做闭包。
函数执行会形成一个全新的私有作用域保护里面的变量不受外界的干扰,这种保护机制就叫做闭包。
函数执行会形成一个私有作用域,而且这个栈内存不销毁,这样的话,里面的私有变量和外面不冲突并且值还能保存下来这样叫闭包。

21 闭包作用

  1. 保护–保护私有变量和外界互不干扰,没有必然关系
    jQuery(JQ)前端非常经典的类库:提供了大量的方法供开发人员使用 => 为了防止全局变量污染,所以JQ中的方法和变量是用闭包保护起来
  2. 形成一个不销毁的私有作用域,不销毁的私有栈内存,这样里面的东西就可以保存下来了

22 原型链是什么

  1. 每一个函数数据类型都有一个天生自带的属性叫prototype(原型属性),这个属性的属性值是一个对象用来存储当前类的公用属性和方法的
  2. 每一个类原型对象上有一个天生自带的属性叫constructor,存储的是当前函数或当前类本身
  3. 每一个对象都天生自带一个属性叫__proto__,这个属性指向所属类的prototype这个原型 当我们通过实例调取某一个属性的时候,首先看看是不是私有属性,是私有的就拿私有的,如果不是私有的,默认通过__proto__往所属类的原型上找,如果没有的话,再通过原型上的__proto__再往上找…直到找到Object这个类的基类的原型(Object.prototype)为止,这个机制叫原型链查找机制

23 说说你对作用域,作用域链的理解

  1. Scope(作用域)当前的执行上下文,值和表达式在其中可见或可被访问。如果一个变量或表达式不在当前的作用域中,那么它是不可用的。作用域也可以堆叠成层次结构,子作用域可以访问父作用域,反过来则不行
    作用域链是 JavaScript 中用于查找变量的一种机制。它由当前作用域和所有父级作用域的变量对象组成。当访问一个变量时,JavaScript 引擎会首先在当前作用域的变量对象中查找,如果找不到,则会向上一级作用域中查找,直到找到为止,这种向上查找的链路就是作用域链。

  2. 作用域链的主要作用是确定变量和函数的可访问性。它保证了在函数内部可以访问外部的变量和函数,但外部无法访问函数内部的变量和函数,从而实现了封装和隔离。

作用域链的形成是在函数定义时确定的,而不是在函数执行时确定的。每当创建一个函数时,都会创建一个新的作用域,并将其添加到作用域链的顶部。当函数执行完毕后,该作用域会被销毁,作用域链也会相应地调整。

24.讲述下从输入http地址到页面生成发生了什么

✔1. DNS解析
没有找到缓存时url解析成ip服务器地址
.
✔2. 建立TCP连接
三次握手
第1次握手:客户端发送一个带有SYN(synchronize)标志的数据包给服务端;

第2次握手:服务端接收成功后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了;

第3次握手:客户端再回传一个带有ACK标志的数据包,表示知道了,握手结束。

.
✔3. 发送HTTP请求
.
✔4. 服务器处理请求服务器返回响应结果
服务器处理后将响应报⽂通过TCP连接发送回浏览器
.
✔5. 关闭TCP链接
四次挥手
第1次挥手:客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态;

第2次挥手:服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务端进入CLOSE_WAIT状态;

第3次挥手:服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态;

第4次挥手:客户端收到FIN后,客户端t进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,服务端进入CLOSED状态,完成四次挥手。

.
✔6. 浏览器解析HTML等资源
获取到资源HTML、CSS、js ->构建DOM树 > 构建styleSheets树
.
✔7. 浏览器将页面布局渲染
布局(构建render树) > 分层 > 绘制 >分块 > 光栅化 > 合成

25 简述下new一个对象的过程

在调用new的过程中会发生以下几件事:
首先创建一个新对象,这个新对象的__proto__属性(原型)指向构造函数的prototype属性(原型)
此时构造函数执行环境的this会指向这个新对象
执行构造函数中的代码,一般是通过this给新对象添加新的成员属性或方法
最后返回这个新对象

26 讲一下ES6有哪些新增功能

  1. const 和 let
  2. 解构赋值.
  3. 模板字符串
  4. 箭头函数
  5. Array.from()将类数组转为数组
  6. Symbol
  7. Set、Map
  8. Promise,async await
  9. 类Class
  10. Module 模块相关(export、import 、export default)

27.set()和map()的理解

set和map都是es6新增的数据结构

其中set是一个类数组结构,值是唯一的,没有重复的

map类似于对象,也是键值对的集合,但是键的类型范围不局限于字符串,可以是各类的

相比于Object类型的“键值对” map结构提供了一个“值值对”

set和map的区别

1 map是键值对,set是值的集合,当然键和值可以是任何的值

2 map可以通过get方法获取值,而set不行,因为set只有值

3 都可以通过迭代器进行 for...of遍历

4 set的值是唯一的,可以用来做数组去重,map由于没有格式限制,可以做数据存储

28 有了解过前端的单元测试,和集成测试吗

  1. 单元测试

所谓单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。通俗的讲,在前端,单元可以理解为一个独立的模块文件,单元测试就是对这样一个模块文件的测试。

对于一个独立的模块(ES6模块),因为功能相对独立,所以我们可以首先编写测试代码,然后根据测试代码指导编写逻辑代码。

所以提到TDD,这里的测试一般是指单元测试

BDD与集成测试

什么是BDD

所谓BDD(Behavior Driven Development),即行为驱动开发,简单的来说就是先编写业务逻辑代码,然后以使得所有业务逻辑按照预期结果执行为目的,编写测试代码,是一种以用户行为来驱动开发过程的开发模式。

2.集成测试

所谓集成测试(Integration Testing),是指对软件中的所有模块按照设计要求进行组装为完整系统后,进行检查和验证。通俗的讲,在前端,集成测试可以理解为对多个模块实现的一个交互完整的交互流程进行测试。

对于多个模块(ES6模块)组成的系统,需要首先将交互行为完善,才能按照预期行为编写测试代码。

所以提到BDD,这里的测试一般是指集成测试。
前端的测试框架有:jest.js karma.js

28 css如何进行性能优化

1.如果规则拥有ID选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹

配它们了)。

2.避免使用通配规则,如*{}计算次数惊人!只对需要用到的元素进行选择。

3.尽量少的去对标签进行选择,而是用class。

4.尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过

三层,更多的使用类来关联每一个标签元素。

5.了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。

渲染性能:

(1)慎重使用高性能属性:浮动、定位。

(2)尽量减少页面重排、重绘。

(3)去除空规则:{}。空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少css文档体积。

(4)属性值为0时,不加单位。

(5)属性值为浮动小数0.**,可以省略小数点之前的0。

(6)标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。

(7)不使用@import前缀,它会影响css的加载速度。

(8)选择器优化嵌套,尽量避免层级过深。

29 什么是面向对象编程(oop)

面向对象编程是一种程序设计范式,它将程序中的数据和操作封装在一起,形成对象。对象具有属性和方法,可以通过调用方法来实现对属性的操作。面向对象编程强调代码的重用性、可维护性和可扩展性

面向对象编程有三大特性,封装、继承和多态。

  1. 封装

封装是把客观事物封装成抽象的类,并隐藏实现细节,使得代码模块化。比如,我们可以把“汽车”这个客观事物封装成一个类,这个类有颜色、型号等属性,有启动、加速、刹车等方法,而这些属性和方法的具体实现则被隐藏起来,使用者只需要知道这个类有哪些属性和方法,不需要知道这些方法是如何实现的。

  1. 继承

继承是面向对象编程的另一个重要特性,它提供了一种无需重新编写,使用现有类的所有功能并进行扩展的能力。比如,我们可以定义一个“电动车”类,它继承了“汽车”类,就自动拥有了“汽车”类的所有属性和方法,比如颜色、型号等属性,启动、加速、刹车等方法,然后我们还可以在“电动车”类上增加一些新的属性和方法,比如电池容量、充电方法等。

  1. 多态

多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。比如,我们定义了一个“汽车”类,它有一个“启动”方法,然后我们又定义了一个“电动车”类,它继承了“汽车”类,也有一个“启动”方法,但是“电动车”类的“启动”方法的实现可能与“汽车”类的不同,这就是多态。

30 什么是seo,怎么优化

SEO,即Search Engine Optimization, 搜索引擎优化,是一种通过分析搜索引擎的排名规律,了解各种搜索引擎怎样进行搜索、怎样抓取互联网页面、怎样确定特定关键词的搜索结果排名的技术。

搜索引擎采用易于被搜索引用的手段,对网站进行有针对性的优化,提高网站在搜索引擎中的自然排名,吸引更多的用户访问网站,提高网站的销售能力和宣传能力,从而提升网站的品牌效应。

1.title
title,就是浏览器上显示的那些内容,不仅用户能看到,也能被搜索引擎检索到(搜索引擎在抓取网页时,最先读取的就是网页标题,所以title是否正确设置极其重要),title一般不超过80个字符,而且词语间要用英文"-"隔开,因为计算机只对英文的敏感性较高,对汉语的敏感性不高。

用法:
IT培训机构|专注编程培训
(1) 首页title写法,一般是"网站名称-主关键字词或一句含有主关键字的描述"。一般网站名称放在后面,因为搜索引擎给予标题最前面的词比后面高。比如:"冰箱_变频冰箱-海尔官网”

(2)文章页title写法,一般有3种:“文章标题-网站名称”、“内容标题-栏目名称-网站名称”、“内容标题-网站名称”。其中,“内容标题-栏目名称-网站名称”的写法最规范,它能给用户很好的提示,让用户知道他在访问哪篇文章,并且是在哪个网站的哪个栏目下。
2.description (内容摘要)
description是对于一个网页的简要内容概况。description一般不超过150个字符,描述内容要和页面内容相关。

用法:

(1)首页description写法,一般是讲首页的标题、关键字和一些特殊目的的内容融合到里面,写成简单的介绍。

(2)文章页keywords写法,建议提取文章中的关键字。


3.keywords(关键字)
keywords,主要作用是告诉搜索引擎本页内容是围绕哪些词展开的。因此keywords的每个词都要能在内容中找到相应匹配,才有利于排名。keywords一般不超过3个,每个关键字不宜过长,而且词语间要用英文","隔开。尽量将重要的关键字靠前放,因为靠后的关键字排名较差,除非你占有很高的权重。

用法:


(1)首页keywords写法,一般是"网站名称,主要栏目名,主要关键字"

31 vue router和route的区别

Router(路由器):router是Vue Router库中的核心概念,它是用于管理和控制应用程序中的路由的对象。router实例负责定义应用程序的路由规则,并将URL与对应的组件进行映射。它可以处理导航、跳转、参数传递等路由相关的操作。

在Vue中,可以通过创建一个router实例来配置路由规则,并将其挂载到Vue应用中。这样,就可以使用router来管理应用程序的路由。

Route(路由):route是指当前激活的路由,它是一个包含当前路由信息的对象。每当进行路由导航时,route对象会被更新为当前激活的路由信息。route对象包含了当前路由的路径、参数、查询参数等信息,可以通过访问this.$route来获取。

在Vue中,可以使用route对象来获取当前路由的信息,例如获取当前路由的路径、参数等。可以在组件中通过this.$route来访问route对象。

总结来说,router是Vue Router库中的核心概念,用于管理和控制应用程序的路由。而route是指当前激活的路由,它是一个包含当前路由信息的对象,用于获取当前路由的相关信息。

32 跨域相关问题,怎么解决跨域

什么是跨域
指一个域下文档或者脚本去请求另一个域下的资源。
什么是同源策略
同源策略(Same origin policy),简称SOP,是一种约定,它由Netscape公司1995年引入浏览器,它是浏览器核心也是最基本的安全功能,如果缺少同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指“协议+ 域名+ 端口”三种相同;

同源策略的目的
为了保证用户信息的安全,防止恶意网站窃取数据

同源策略限制的行为操作
cookie、localStorage、IndexDB无法读取 (不能共享cookie)
DOM和Js对象无法获得(不能相互操作dom)
Ajax请求不能发送(不能发送ajax请求)
跨域解决方案
1.通过jsonp跨域

我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
jsonp缺点:只能实现get一种请求

2.document.domain + iframe跨域

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
此方案仅限主域相同,子域不同的跨域应用场景。

3.location.hash + iframe

a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

4.window.name + iframe跨域

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

5.postMessage跨域

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题: a.) 页面和其打开的新窗口的数据传递 b.) 多窗口之间消息传递 c.) 页面与嵌套的iframe消息传递 d.) 上面三个场景的跨域数据传递

用法:postMessage(data,origin)方法接受两个参数 data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。 origin: 协议+主机+端口号,也可以设置为"*“,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/"。

6.跨域资源共享(CORS)

只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

7.nginx代理跨域

通过nginx配置一个代理服务器(域名与demo1相同,端口不同)做跳板机,反向代理访问demo2接口,并且可以顺便修改cookie中demo信息,方便当前域cookie写入,实现跨域登录。

8.nodejs中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置
cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

9.WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯。基本上用来作实时数据的展示。

33.vue的原理

首先要对数据(data)进行劫持监听。所以需要设置一个监听器Observer,用来监听所有的属性。
在 JavaScript 中有两种劫持属性访问的方式:Object.defineProperty 和 Proxy 。
Vue 2 使用 Object.defineProperty 完全由于需支持更旧版本浏览器的限制。
在 Vue 3 中使用了 Proxy 来创建响应式对象,将 getter/setter 用于 ref。

每一个组件都有一个Watcher实例。如果属性发生变化,需要通知订阅者Watcher,看是否需要更新。
因为订阅者有多个,所以需要一个消息订阅器(发布者)Dep(订阅者集合的管理数组)来专门收集这些订阅者,在Observer和Watcher之间进行统一管理。
还需要一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令初始化为一个订阅者Watcher,并替换模板数据或绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

34. 虚拟dom是什么?为什么vue中会用到虚拟dom?

在现代Web应用中,DOM(Document Object Model)是不可或缺的一部分,它允许JavaScript改变页面的内容、结构和样式。然而,DOM操作往往是性能瓶颈的主要来源之一。这是因为每次DOM结构发生变化时,浏览器都需要重新计算页面的几何属性(称为“重排”或“回流”)并更新屏幕上的绘制(称为“重绘”)。

重排(Reflow):当DOM的变化影响了元素的几何属性(如宽、高、位置等)时,浏览器需要重新计算页面的布局,并调整其他元素的位置。这个过程称为重排,它通常比较耗时,尤其是在处理复杂布局时。

重绘(Repaint):当DOM的变化只影响元素的外观,而不影响其布局时(如改变背景色、文字颜色等),浏览器只需重新绘制受影响的区域,而不需要进行整个页面的布局计算。重绘通常比重排要快,但频繁的重绘仍然会影响性能。
虚拟DOM的定义
虚拟DOM(Virtual DOM)是一个编程概念,它是对真实DOM(Document Object Model)内存中的抽象表示。虚拟DOM本身并不是一个真实的DOM节点,而是一个轻量级的JavaScript对象,它模拟了真实DOM树的结构和属性。开发者通过操作虚拟DOM,可以间接地更新真实DOM,而不需要直接进行频繁的DOM操作,从而提高Web应用的性能。

虚拟DOM与真实DOM的关系
虚拟DOM与真实DOM之间是一种映射关系。虚拟DOM是对真实DOM的一种抽象和模拟,它可以在内存中高效地进行创建、更新和对比操作,而不需要直接操作真实的DOM树。当虚拟DOM发生变化时,它会与之前的虚拟DOM树进行对比(这个过程称为“diffing”),然后计算出最小的变化集,并将这些变化应用到真实DOM上,从而实现对页面的高效更新

35 diff算法的理解

Vue 基于虚拟 DOM 做更新。diff 算法的核心就是比较两个虚拟DOM 树的差异,返回一个 patch 对象,这个对象的作用就是存储两个节点不同的地方,最后用 patch 里记录的信息进行更新真实DOM。
diff 算法的特点:
1.比较只会在同层级进行, 不会跨层级比较;
2. 在 diff 比较的过程中,循环从两边向中间比较;
Diff 比较总结
如果两个节点不相同的话,那么直接删除老节点,然后创建新节点。

如果是相同节点,那么会比较两个节点的差异,包括节点的 key 属性、tag 标签、事件等等;

如果两个父节点相同的话,那么就比较儿子节点。
比较子节点有以下4种情况:
1.如果都是文本节点且不相等,直接将el文本节点更新为新节点的文本内容即可;
2.如果老的有儿子,新的没儿子,直接删除老的儿子节点;
3.如果老的没儿子,新的有儿子,那么就创造新的儿子节点,直接插入父节点中;
4.如果两者都有子节点,则执行 updateChildren 函数比较子节点。
vue3中优化方案
Vue3 中采用最长递增子序列来实现 diff 优化

35.vue2与vue3面试题之区别

1.数据双向绑定( proxy() 替代 object.defineProperty() )

2.生命周期函数的更换
vue3多了个setup,且所有生命周期前面都多了个on。

3.新增一下功能
Composition API 组合api

Teleport 传送组件,把dom节点挂载到body等其他标签上

Fragments 碎片化节点 ( 不需要根节点 )

Emits Component Option自定义事件

createRenderer 渲染器

SFC State-driven CSS Variables (v-bind in style)

SFC style scoped can now include global rules or rules that target only slotted content

Suspense组件 异步组件

4.定义数据的不一样
vue2 在data之中定义
vue3 则是在setup之中,使用ref、reactive等定义数据
5.

36.vue3双向绑定与vue2双向绑定的区别

由 vue2 的响应式原理可以看出,vue 底层需要对 vue 实例的返回的每一个 key 进行 get 和 set 操作,无论这个值有没有被用到。所以在 vue 中定义的 data 属性越多,那么初始化开销就会越大。而 proxy 是一个惰性的操作,它只会在用到这个 key 的时候才会执行 get,改值的时候才会执行 set。所以在 vue3 中实现响应式的性能实际上要比 vue2 实现响应式性能要好

object.defineProperty对于后期添加的属性值是不参与劫持设置为响应式属性的
proxy是代理的整个对象所以能劫持到。

37.前端模块规范化标准有哪些?

1.CommonJS

同步加载方式,适用于服务端,因为模块都放在服务器端,对于服务端来说模块加载较快,不适合在浏览器环境中使用,因为同步意味着阻塞加载。

所有代码都运行在模块作用域,不会污染全局作用域。

模块可以多次加载,但只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。

模块加载的顺序,按照其在代码中出现的顺序。

2.AMD(Asynchronous Module Definition)
AMD允许输出的模块兼容CommonJS

异步并行加载,不阻塞 DOM 渲染。

推崇依赖前置,也就是提前执行(预执行),在模块使用之前就已经执行完毕。

3.CMD(Common Module Definition)
CMD 是通用模块加载,要解决的问题与 AMD 一样,只不过是对依赖模块的执行时机不同 ,推崇就近依赖。

sea.js 是 CMD 规范的一个实现代表库

定义模块使用全局函数define,接收一个 factory 参数,可以是一个函数,也可以是一个对象或字符串
4.UMD(Universal Module Definition)

UMD是AMD和CommonJS的糅合
UMD的实现很简单:
先判断是否支持Node.js模块(exports是否存在),存在则使用Node.js模块模式。
再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。
前两个都不存在,则将模块公开到全局(window或global)

5.ES6模块化
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
ES6 中,import引用模块,使用export导出模块。默认情况下,Node.js默认是不支持import语法的,通过babel项目将 ES6 模块 编译为 ES5 的 CommonJS。
因此Babel实际上是将import/export翻译成Node.js支持的require/exports。
2024 web前端面试题(持续更新)_第1张图片

详情

38 cmd与amd的区别

AMD 是提前执行,CMD 是延迟执行。
AMD 是依赖前置,CMD 是依赖就近。

39 "require"与"import"的区别

  1. 写法上的区别
    require/exports 的用法只有以下三种简单的写法:
    const fs = require(‘fs’)
    exports.fs = fs
    module.exports = fs
    import/export 的写法就多种多样:

import fs from ‘fs’
import {default as fs} from ‘fs’
import * as fs from ‘fs’
import {readFile} from ‘fs’
import {readFile as read} from ‘fs’
import fs, {readFile} from ‘fs’

export default fs
export const fs
export function readFile
export {readFile, read}
export * from ‘fs’

  1. 输入值的区别
    require输入的变量,基本类型数据是赋值,引用类型为浅拷贝,可修改
    import输入的变量都是只读的,如果输入 a 是一个对象,允许改写对象属性。

3.执行顺序
require:不具有提升效果,到底加载哪一个模块,只有运行时才知道。
import:具有提升效果,会提升到整个模块的头部,首先执行。import的执行早于foo的调用。本质就是import命令是编译阶段执行的,在代码运行之前。

4.使用表达式和变量
require:很显然是可以使用表达式和变量的

let a = require(‘./a.js’)
a.add()

let b = require(‘./b.js’)
b.getSum()
import静态执行,不能使用表达式和变量,因为这些都是只有在运行时才能得到结果的语法结构。

你可能感兴趣的:(前端,javascript,开发语言,面试)