问题及答案

一、css部分

1、CSS选择器及其优先级

对于选择器的优先级:

1.标签选择器、伪元素选择器:1

2.类选择器、伪类选择器、属性选择器:10

3.id 选择器:100

4.内联样式:1000

注意事项:

1.!important 声明的样式的优先级最高;

2.如果优先级相同,则最后出现的样式生效;

3.继承得到的样式的优先级最低;

4.通用选择器(*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以它们的权值都为 0 ;

5.样式表的来源不同时,优先级顺序为:内联样式 > 内部样式 > 外部样式 > 浏览器用户自定义样式 > 浏览器默认样式。

2、CSS实现三列布局(左右固定宽度,中间自适应)

(1)CSS浮动
   第一个float:left,第二个float:right,第三个设置margin-left和margin-right
(2)绝对定位法
   第一个定位到left,第二个定位到right,第三个设置margin-left和margin-right
(3)flex布局

.left{
  width:200px;
  或者
  flex:0 0 200px;
}
.right{
  width:200px;
  或者
  flex:0 0 200px;
}
.center{
  flex:1;
}

3、如果要做优化,CSS提高性能的方法有哪些?

   内联首屏关键CSS
   异步加载CSS
   资源压缩
   合理使用选择器
   减少使用昂贵的属性
   不要使用@import

4.Flex容器属性

1.flex-direction主轴方向(row(默认值):主轴为水平方向,起点在左端。row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。)
2.flex-wrap(nowrap(默认值):不换行。
wrap:换行。
wrap-reverse:换行,第一行在下方。 )
3.flex-flow(12简写形式)
4.justify-content项目在主轴上的对齐方式。(flex-start(默认值):左对齐(即上面页面展示效果)
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。)
5.align-items项目在交叉轴上如何对齐。(lex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。)
6.align-content

5.标准盒模型和怪异盒模型的区别: 

怪异盒模型的宽度变小
标准盒大小计算公式:width(content) + padding + border + margin
怪异盒大小的计算公式:width(content + padding + border) + margin

 6. rem的特点:

1、rem的大小是根据html根目录下的字体大小进行计算的。
2、当我们改变根目录下的字体大小的时候,下面字体都改变。
3、rem不仅可以设置字体的大小,也可以设置元素宽、高等属性。

7.em的特点:

 字体大小是根据父元素字体大小设置的。

8.http请求过程

浏览器发起请求-> 解析域名得到ip进行TCP连接 ->浏览器发送HTTP请求和头信息发送->服务器对浏览器进行应答,响应头信息和浏览器所需的内容-> 关闭TCP连接或保持-> 浏览器得到数据数据进行操作。

9.前端如何实现跨域?

当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域
出于浏览器的同源策略限制。
同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
1.JSONP原理
利用script元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 数据。
但是JSONP请求一定需要对方的服务器做支持才可以。
JSONP优点是兼容性好,可用于解决主流浏览器的跨域数据访问的问题。
缺点是仅支持get方法具有局限性。

2.CORS原理
实现CORS通信的关键是服务器,需要在服务器端做一些小小的改造。
只要服务器实现了CORS接口,就可以跨源通信。
在响应头上添加Access-Control-Allow-Origin属性,指定同源策略的地址。同源策略默认地址是网页的本身。只要浏览器检测到响应头带上了CORS,并且允许的源包括了本网站,那么就不会拦截请求响应。

3.Nginx
浏览器在访问受限时,可通过不受限的代理服务器访问目标站点。
proxy代理是前端用的最多的解决跨域的方法。
即配置一台和浏览器相同端口的服务器,浏览器访问代理服务器,代理服务器向目标服务器发送请求,由于服务器之间不存在跨域问题,代理服务器就可以拿到请求数据,而后因为浏览器和代理服务器端口号一致,不存在跨域问题,因此浏览器不会拦截从代理服务器收到的数据,顺利拿到请求数据。

10.修改图片颜色--通过css

filter: drop-shadow(40px 0px yellow); 

二、js

1、防抖和节流,应用场景

防抖和节流都是防止某一时间频繁触发,但是原理却不一样。
防抖是将多次执行变为只执行一次,节流是将多次执行变为每隔一段时间执行。
防抖(debounce):
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
节流(throttle):
鼠标不断点击触发,mousedown(单位时间内只触发一次)
监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

2、什么是闭包

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。

 闭包的优缺点:

 优点:

  • 保护变量:闭包可以保护函数内部的变量不被外部访问和修改,从而提高了程序的安全性。
  • 延长变量的生命周期:闭包可以使函数内部的变量在函数执行完毕后仍然存在,便于后续的使用,增加了变量的灵活性。
  • 实现私有成员:通过将变量封装在闭包内部,外部无法直接访问,只能通过闭包提供的接口来访问,这类似于面向对象编程中的私有成员。
  • 实现函数柯里化:可以将一个多参数的函数转化为一个单参数的函数序列,使得函数的调用更加灵活和简洁。

  缺点:

  • 内存占用:由于闭包会保留对外部变量的引用,可能导致内存的占用较多,尤其是在长时间运行的闭包中,容易引发内存泄漏问题。
  • 性能消耗:闭包的创建和执行需要额外的内存和时间开销,因为它们需要保存函数及其相关联的环境信息,这可能会影响程序的整体性能。
  • 难以理解和调试:在使用闭包时,可能增加代码的复杂性,尤其在处理多层嵌套的情况时,理解其运行机制和进行调试可能会变得更加困难。

3、继承有哪些方法

     1)原型链继承:原型链继承是通过将一个对象的实例作为另一个对象的原型来实现继承。它是JavaScript 中最原始的继承方式

  

function Person(name) {
  this.name = name;
}
 
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
};
 
function Student(name, grade) {
  this.grade = grade;
}
 
Student.prototype = new Person();
 
const student = new Student('Allen', 10);
student.sayHello(); // 输出:Hello, my name is Allen.
 


     2)借用构造函数继承(伪造对象、经典继承):构造函数继承是通过在子类构造函数中调用父类构造函数来实现继承。这种继承方式可以避免父类原型上的属性和方法被子类实例共享的问题

     

function Person(name) {
  this.name = name;
}
 
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
};
 
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}
 
const student = new Student('Allen', 10);
student.sayHello(); // 抛出 TypeError 错误


     3)实例继承(原型式继承):

       ES5 里面的 Object.create 方法,这个方法接收两个参数:一是用作新对象原型的对象、二是         为新对象定义额外属性的对象(可选参数)

      JavaScript 原型式继承是一种简单的继承方式,它可以通过一个已有的对象来创建新对象,新        对象具有与已有对象相同的属性和方法。

function createObject(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
 
const person = {
  name: 'Allen',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};
 
const student = createObject(person);
student.grade = 10;
 
console.log(student.name); // 输出:Allen
student.sayHello(); // 输出:Hello, my name is Allen.


     4)组合式继承:组合继承是将原型链继承和构造函数继承结合起来使用的一种继承方式。它继承了父类构造函数的属性和方法,同时也继承了父类原型链上的属性和方法。

function Person(name) {
  this.name = name;
}
 
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
};
 
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}
 
Student.prototype = new Person();
Student.prototype.constructor = Student;
 
const student = new Student('Allen', 10);
student.sayHello(); // 输出:Hello, my name is Allen.


     5)寄生式继承:

使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。

JavaScript 寄生式继承(Parasitic Inheritance)是一种基于原型式继承的模式,在原型式继承的基础上添加了一些额外的方法,以增强对象的功能

function createObject(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
 
// 寄生式继承
function createStudent(person) {
  const student = createObject(person);
  student.grade = 10;
  student.sayGrade = function() {
    console.log(`My grade is ${this.grade}.`);
  };
  return student;
}
 
const person = {
  name: 'Allen',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};
 
const student = createStudent(person);
student.sayHello(); // 输出:Hello, my name is Allen.
console.log(student.grade); // 输出:10
student.sayGrade(); // 输出:My grade is 10.

6)寄生组合继承:

结合第五种中提及的继承方式,解决普通对象的继承问题的 Object.create 方法,我们在前面这几种继承方式的优缺点基础上进行改造,得出了寄生组合式的继承方式,这也是所有继承方式里面相对最优的继承方式。

JavaScript 寄生组合式继承(Parasitic Combination Inheritance)是一种常用的继承方式,它结合了原型式继承和构造函数继承的优点,以达到最优的继承效果

// 原型式继承
function createObject(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
 
// 构造函数
function Person(name) {
  this.name = name;
  this.friends = ['Alice', 'Bob'];
}
 
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
};
 
// 寄生组合式继承
function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}
 
Student.prototype = createObject(Person.prototype);
Student.prototype.constructor = Student;
 
Student.prototype.sayGrade = function() {
  console.log(`My grade is ${this.grade}.`);
};
 
const student = new Student('Allen', 10);
student.sayHello(); // 输出:Hello, my name is Allen.
console.log(student.friends); // 输出:["Alice", "Bob"]
student.sayGrade(); // 输出:My grade is 10.


     7)es6继承 extends:

ES6 提供了继承的关键字 extends

ES6 中引入了 class 语法糖,让原本基于构造函数和原型的继承方式更加友好和易于理解

class Person {
  constructor(name) {
    this.name = name;
    this.friends = ['Alice', 'Bob'];
  }
 
  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}
 
class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }
 
  sayGrade() {
    console.log(`My grade is ${this.grade}.`);
  }
}
 
const student = new Student('Allen', 10);
student.sayHello(); // 输出:Hello, my name is Allen.
console.log(student.friends); // 输出:["Alice", "Bob"]
student.sayGrade(); // 输出:My grade is 10.

4、什么是深/浅拷贝,有哪些实现方式

    基本数据类型:string、number、boolean、undefined、null、Symbol(ES6,符号类型)
    引用数据类型:object、array、function
JS数据类型分为基本数据类型和引用数据类型,基本数据类型保存的是值,引用类型保存的是引用地址(this指针)。浅拷贝共用一个引用地址,深拷贝会创建新的内存地址

Object.assign:对象的合并 (第一级属性深拷贝,第一级以下的级别属性浅拷贝。)
ES6中的 Object.assign(),第一个参数必须是个空对象。
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象

5、数组有哪些常用方法:

(1)push()

向数组的末尾添加新内容

参数:要添加的项。传递多个用逗号隔开,任何数据类型都可以

返回值:新增后数组的长度

是否改变原数组:改变

(2)pop()

删除数组的最后一项

参数:无

返回值:被删除的项

是否改变原数组:改变

(3)shift()

删除数组的第一项

参数:无

返回值:被删除的项

是否改变原数组:改变

(4)unshift()

向数组首位添加新内容

参数:要添加的项,多项用','隔开

返回值:新数组的长度

是否改变原数组:改变

(5)slice()

按照条件查找出其中的部分内容

参数:

array.slice(n, m),从索引n开始查找到m处(不包含m)

array.slice(n) 第二个参数省略,则一直查找到末尾

array.slice(0)原样输出内容,可以实现数组克隆

array.slice(-n,-m) slice支持负参数,从最后一项开始算起,-1为最后一项,-2为倒数第二项

返回值:返回一个新数组

是否改变原数组:不改变

(6)splice()

对数组进行增删改

增加:ary.splice(n,0,m)从索引n开始删除0项,把m或者更多的内容插入到索引n的前面

返回空数组

修改:ary.splice(n,x,m)从索引n开始删除x个,m替换删除的部分

把原有内容删除掉,然后用新内容替换掉

删除:ary.splice(n,m) 从索引n开始删除m个内容

(如果第二个参数省略,则从n删除到末尾)

返回删除的新数组,原有数组改变

(7)join()

用指定的分隔符将数组每一项拼接为字符串

参数:指定的分隔符(如果省略该参数,则使用逗号作为分隔符)

返回值:拼接好的字符串

是否改变原数组:不改变

(8)concat()

用于连接两个或多个数组

参数:参数可以是具体的值,也可以是数组对象。可以是任意多个

返回值:返回连接后的新数组

是否改变原数组:不改变

(9)indexOf()

检测当前值在数组中第一次出现的位置索引

参数:array.indexOf(item,start) item:查找的元素 start:字符串中开始检索的位置

返回值:第一次查到的索引,未找到返回-1

是否改变原数组:不改变

(10)includes()

判断一个数组是否包含一个指定的值

参数:指定的内容

返回值:布尔值

是否改变原数组:不改变

(11)sort()

对数组的元素进行排序(默认是从小到大来排序 并且是根据字符串来排序的)

参数:可选(函数) 规定排序规则 默认排序顺序为按字母升序

返回值:排序后新数组

是否改变原数组:改变

(12)reverse()

把数组倒过来排列

参数:无

返回值:倒序后新数组

是否改变原数组:改变

(13)forEach()

循环遍历数组每一项

参数:函数 ary.forEach(function(item,index,ary){}) item:每一项 index:索引 ary:当前数组

返回值:无

是否改变原数组:不改变

注:引出下一个问题,slice和splice区别:

   1.splice改变原数组,slice不改变原数组。

   2.splice除了可以删除之外,还可以插入。

   3.splice可传入3个参数,slice接受2个参数。

6、Promise.all和Promise.race的区别,应用场景

Promise.all()可以将多个实例组装个成一个新实例,成功的时候返回一个成功的数组;失败的时候则返回最先被reject失败状态的值。  var p=Promise.all([p1,p2,p3]);
适用场景:比如当一个页面需要在很多个模块的数据都返回回来时才正常显示,否则loading。
promise.all中的子任务是并发执行的,适用于前后没有依赖关系的。Promise.race()意为赛跑的意思,也就是数组中的任务哪个获取的块,就返回哪个,不管结果本身是成功还是失败。一般用于和定时器绑定,比如将一个请求和三秒的定时器包装成Promise实例,加入到Promise队列中,请求三秒中还没有回应时,给用户一些提示或相应的操作。

7、微任务和宏任务的区别

1.宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。
2.微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。
3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
微任务:process.nextTick、MutationObserver、Promise.then catch finally
宏任务:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame

8.改变this指向call、apply与bind区别:

前两个可以自动执行,bind不会自动执行,需要手动调用
call、bind与apply区别:前两个都有无数个参数,apply只有两个参数,而且第二个参数为数组

9.数组和字符串的相互转换

使用字符串的 split() 方法可以根据指定的分隔符把字符串切分为数组。
如果使用数组的 join() 方法,可以把数组元素连接为字符串。

10.js 判断数据类型的几种方法

typeof str     // "string" 字符串
typeof num     // "number" 数值
typeof array   // "object" 对象(可以和函数区别开)
// 👆注意,数组也是一个对象
typeof date    // "object" 对象 
typeof func    // "function" 函数
typeof symbol  // "symbol"

11.es6新特性:

1、let 和 const
let 表示申明变量。const 表示申明常量
常量定义了就不能改了。对象除外,因为对象指向的地址没变。
const在申明是必须被赋值。
两者都为块级作用域。
2、模板字符串
3、解构
4、函数的默认值
5、Spread / Rest 操作符,三个点…
6、箭头函数
7、for of
for of遍历的是键值对中的值
for in遍历的是键值对中的键
8、class类,原型链的语法糖表现形式
9、导入导出
导入improt
导出export default
10、promise
Promise 用于更优雅地处理异步请求。
11、async/await
比promise更好的解决了回调地狱
12、Symbol,新的基本类型
13、Set集合
存储任何类型的唯一值,即集合中所保存的元素是不重复的。类数组结构。
let arrNew = new Set(待去重的数组)

12.原型链

当js试图得到一个对象的属性时,会先去这个对象的本身去寻找,如果这个对象本身没有找到这个属性,那么js就会去它构造函数的’prototype’属性中去寻找,也就是去’proto‘中寻找,如果’prototype’属性本身中依旧没有找到,’prototype’中依旧有一个‘proto’。

原型可以解决什么问题:
对象共享属性和方法
谁有原型:
函数拥有:prototype
对象拥有:proto
对象查找属性或者方法的顺序:
先在对象本身查找–>构造函数中查找–>对象的原型中查找–>构造函数的原型中查找–>当前原型中查找
原型链的最顶端是null

 13、Promise.all 和 Promise.allSettled的区别与应用

1、Promise.all:

当所有的Promise都成功(resolve)时,Promise.all返回一个包含所有Promise结果的数组。
如果任何一个Promise被拒绝(reject),Promise.all会立即返回一个被拒绝的Promise,并带有第一个被拒绝Promise的错误信息。
结果数组的顺序与传入的Promise数组的顺序相同。

2、Promise.allSettled:

Promise.allSettled返回一个Promise,该Promise在所有的Promise都已完成(不管是成功还是失败)后解析。
返回的Promise最终会以一个包含每个Promise结果的对象数组的形式进行解析。
每个对象具有status和value属性:
status可为字符串"fulfilled"表示成功、或字符串"rejected"表示拒绝。
value属性存储了已解决或已拒绝的值。
Promise.allSettled不会中断执行,而是等待所有Promise都解决或拒绝,然后返回结果

总结:

Promise.all在全部成功时才返回,一旦有Promise被拒绝,立即返回拒绝的Promise。
Promise.allSettled会等待所有Promise都完成(无论成功与否),并以一个包含每个Promise结果的对象数组的形式返回。
使用Promise.all时,如果你希望在任何一个Promise被拒绝时立即中断执行并处理错误,你可以结合使用.catch方法来捕获错误。而对于Promise.allSettled,你可以在最后统一处理每个Promise的结果,无论成功与否。

 14、什么是递归

递归(Recursion):是一个函数在其定义中直接或间接调用自身的一种方法。直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数

递归需要有递归出口与递归关系式:

  • 递归出口:边界条件,停止递归,问题规模足够小,直可以接给出答案
  • 递归关系式:分解原问题为更小规模但解决方法与原问题一样的子问题

递归优点:

1.简洁性:递归可以用较少的代码实现复杂的功能。相对于使用循环来处理嵌套结构,递归代码通常更简洁、易于理解和维护。

2.可读性:递归可以使代码更加可读和自解释。它可以更直观地表示问题的解决方案,特别是对于涉及嵌套结构的问题。

3.灵活性:递归可以应对未知深度的数据结构,因为它不需要提前知道要处理的嵌套层级。

4.问题分解:递归通过将问题划分为更小的子问题,使得复杂问题的解决变得更加可行。

递归缺点:

1.性能开销:递归可能会导致性能问题,尤其是当递归层级很深时。每次递归调用都需要在内存中创建一个新的函数上下文,这可能会占用大量的内存和处理时间。

2.栈溢出:如果递归层级过深,函数调用的堆栈可能会超出系统的限制,导致栈溢出错误。这通常可以通过限制递归层级或使用尾递归优化来避免。

3.难以调试:由于递归涉及到函数的自我调用,调试递归函数可能会变得复杂和困难。错误的递归调用可能导致死循环或无限递归,从而使得调试变得更加困难。

15、什么是cookie

   cookie是会话跟踪技术,也是页面之间传递数据的变量。cookie分为会话级cookie和长生命周期的cookie,长生命周期的cookie由服务器生成,由服务器发送到本地磁盘,保存在本地磁盘中,会话级cookie由浏览器生成,保存在浏览器当中,浏览器关闭cookie自动清除。二者的读取是一样的,会话级cookie没有删除的概念,当所有页面关闭后会自动清除,长生命周期的cookie没有直接删除的方法,可以把存储的时间更改为-1或者等存储时间到了自动删除,长生命周期的cookie写的时候需要加expires=标准日期对象

16、cookie和本地存储的区别有什么?

      cookie存储数据的大小是4K,本地存储的数据大小是4-5MB;cookie每次都会把数据发送在HTTP请求当中,浪费性能,本地存储不参与服务器通信;cookie的原生API极少,需要程序员自己封装,本地存储的API等同于cookie封装好的API;会话级cookie等同于sessionStorage,localStorage除非手动删除,否则将一直存储在本地。

17、什么是单例类?怎么实现?有什么作用?

     一个类只能创建一个对象,就是单例类。当这个类没有对象的时候,就创建一个对象,如果这个类已经有了一个对象,则无法继续创建对象,保持该类的对象永远有且只有一个。可以方便在整个项目之间传递共享数据

18、instanceof是什么?和typeof的异同点是什么?

     instanceof是判断引用类型的关键字,返回值是布尔值,如果A对象是B类型的,返回true,反之返回false。二者都是判断数据类型的关键字,typeof只能判断基本类型,无法判断引用类型,若判断引用类型返回Object,instanceof能够判断引用类型,且满足兼容性规则(高版本兼容低版本,低版本不兼容高版本)

19、借用构造方法继承的作用和缺陷是什么?

可以实现子类对象构造时,初始化父类派生给子类的属性,缺陷是无法继承父类原型对象上的属性和方法

20、原型继承的缺陷有哪些?

子类对象构造时,无法初始化父类派生给子类的属性,一旦实现继承关系,继承指向就不能修改,必须先实现继承关系才能为子类添加原型属性和方法

21、面向对象的编程思想包括哪些

1)封装:将若干个属性和方法封装成一个整体变成一个对象,通过对象调用这些属性和方法

2)继承:子类吸收父类已有的属性和方法,提升了代码的复用性(ES6类的方法直接绑定在原型对象上)

3)多态:在面向对象的过程中,有一种极致的复用性叫多态,降低了类和函数的耦合性,实现了类和函数的解耦

22、为什么子类的实例对象可以访问到子类的所有属性和方法,以及父类的所有属性和方法?

   所有实例对象在创建的过程中自带__proto__属性,指向类的原型对象prototype,所以可以访问类的原型上的属性和方法,类在构造对象的时候new出了空间,类的实例对象可以直接访问自己new出空间的属性和方法。子类的原型对象指向父类的实例对象,子类的实例对象可以访问父类构造对象时new出的属性和方法,子类的实例对象通过父类实例对象的__proto__可以访问父类原型上的的属性和方法

23、什么是arguments?作用是什么?

    实参列表,是函数对象的内置对象,作用域在函数内部,arguments的返回值是一个伪数组,只能访问元素和长度,其他API无法使用。作用有二,可以设置不定参函数,arguments.callee代表函数本身,常用于递归函数

24、什么是柯里化函数?柯里化的核心思想是什么?

   一个函数,只有一个参数,并且返回值是一个函数的函数。对函数参数的自由处理,让函数更加灵活,降低了通用性,提升了特定性

25、立即执行函数的写法

  (function (){}())

  (function (){})()

26、什么是回调函数?为什么需要回调函数?

一个被当作函数参数的函数,在不打开第三方函数的情况下实现完整功能,就需要回调函数

27、什么是匿名函数?作用是什么?

      没有名字的函数就是匿名函数,作用有三,把函数当作变量赋值,把函数当作参数(回调函数),把函数当作另一个函数的返回值(闭包)

28、import和require区别

1)加载方式:`require` 是运行时加载,而 `import` 是编译时加载。这意味着 `require` 在代码执行时加载模块,而 `import` 在代码编译阶段就加载了模块。

2)规范不同:`require` 是 CommonJS/AMD 规范,而 `import` 是 ECMAScript 6+ 规范。

3)特点不同:

     `require` 提供了服务器/浏览器的模块加载方案,是非语言层面的标准,只能在运行时确定模块        的依赖关系及输入/输出的变量,无法进行静态优化。

     `import` 支持编译时静态分析,便于引入宏和类型检验,具有动态绑定功能。

4)语法使用不同:

     `require` 可以写在代码的任意位置,而 `import` 只能写在文件的最顶端且不可在条件语句或函        数作用域中使用。

     `require` 通过 `module.exports` 导出的值就不能再变化,而 `import` 通过 `export` 导出的值可       以改变。

注:require导入的值被修改时,源对象不会被改变,相当于深拷贝;import导入的对象值被改变时,源对象也会被改变,相当于浅拷贝。

29、HTTP与HTTPS的区别

1、HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性加密传输协议

2、HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,HTTP的端口号是80,        HTTPS的端口号的443

3、HTTP的连接很简单,是无连接

4、HTTPS协议是由ssl+HTTP协议构建的可进行加密的传输,身份认证网络的协议,要比HTTP协议安全的多

30、HTTP返回的状态码

(1)200:请求被正常处理

(2)204:请求被受理,但没有资源返回

(3)206:客户端只请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过content-range指定范围资源

(4)301:永久性重定向

(5)302:临时重定向

(6)303:与302状态码有相似的功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上

(7)304:发送附带条件的请求时,条件不满足时返回,与重定向无关

(8)307:临时重定向,与302类似,只是想只要求使用post方法

(9)400:请求报文语法有误,服务器无法识别

(10)401:请求需要认证

(11)403:请求对应资源禁止访问

(12)404:服务器无法找到对应资源

(13)500:服务器内部错误

(14)503:服务器正忙

三、VUE

1、vue的响应式原理:

vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 数据劫持,来劫持各个属性的setter,getter,在数据更新时发布消息给订阅者,触发相应监听回调。

当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty() 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),

最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。

2、Vue中的的通信方式有几种?隔代组件的通信你用那种方式解决?

props/$emit 适用父子组件通信
ref与parent/children适用父子组件通信
attrs/listeners,provide/inject 适用于隔代组件通信
vuex,EventBus(事件总线) 适用于父子、隔代、兄弟组件通信
slot插槽方式

3、v-show 和 v-if指令的共同点和不同点?

v-show是css切换,v-if是完整的销毁和重新创建,如果频繁切换时用v-show,运行时较少改变用v-if

4、为什么使用key?

key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度

key具有唯一性

vue中循环需加 :key=“唯一标识” ,唯一标识可以使item里面id index 等,因为vue组件高度复用增加key可以标识组件的唯一性,为了更好地区别各个组件key的作用主要是为了高效的更新虚拟DOM

不带有key,并且使用简单的模板,基于这个前提下,可以更有效的复用节点,diff速度来看也是不带key更加快速的,因为带key在增删节点上有耗时。这就是vue文档所说的默认模式。但是这个并不是key作用,而是没有key的情况下可以对节点就地复用,提高性能。这种模式会带来一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。VUE文档也说明了。还有就是key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,但是这个含有争议

5、简述computed和watch的使用场景

computed:
支持缓存,数据变,直接会触发相应的操作;
监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
当一个属性发生变化时,需要执行对应的操作;即一个属性受多个属性影响,多对一或者一对一的关系;
监听的是这个属性自身的变化,且不会操作缓存
监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
是一个计算属性,类似于过滤器,对绑定到view的数据进行处理
    当一个属性受多个属性影响的时候就需要用到computed
    最典型的例子: 购物车商品结算的时候
watch:
1.是观察的动作,
2.应用:监听props,$emit或本组件的值执行异步操作
3.无缓存性,页面重新渲染时值不变化也会执行
watch是一个观察的动作
    当一条数据影响多条数据的时候就需要用watch
    例子:搜索数据

6、params和query的区别

  query
            1.是拼接在url后面的参数。
             2.参数在?后面,且参数之间用&符分隔
             3.query相当于get请求,可以在地址栏看到参数
   params
             1.是路由的一部分。以对象的形式传递参数
             2.使用params传参只能由name引入路由,如果写成path页面会显示警告,说参数会被忽略
             3.params相当于post请求,参数不会再地址栏中显示

具体传参与接收方式:

       1、query 传参配置的是 path,而 params 传参配置的是 name,在 params 中配置 path 无效
        2、query 在路由配置不需要设置参数,而 params 必须设置
        3、query 传递的参数会显示在地址栏中
        4、params 传参刷新会无效,但是 query 会保存传递过来的值,刷新不变 ;
        5、接收参数使用 this.$router 后面就是搭配路由的名称就能获取到参数的值

7、$route 和 $router 的区别

      $route:获取路由信息 指当前路由实例跳转到的路由对象
包括:
        $route.path 字符串,等于当前路由对象的路径,会被解析为绝对路径,如/home/ews
        $route.name 当前路由的名字,如果没有使用具体路径,则名字为空
        $route.router 路由规则所属的路由器
        $route.matchd 数组,包含当前匹配的路径中所包含的所有片段所对象的配置参数对

        $route.query 对象,包含路由中查询参数的键值对。会拼接到路由 url 后面
        $route.params 对象,含路有种的动态片段和全匹配片段的键值对,不会拼接到路由
的 url 后面
        r o u t e r :获取路由整个实例指整个路由实例,可操控整个路由通过‘ router:获取路由整个实例 指整个路由实例,可操控整个路由 通过‘router:获取路由整个实例指整个路由实例,可操控整个路由通过‘router.push’往其中添加任意的路由对象 钩子函数等

8、vue-router 有哪几种导航钩子?

1.全局导航钩子:router.beforeEach(to,from,next)作用:跳转前进行判断拦截、组件内的钩子、单独路由独享组件
2、路由独享钩子可以在路由配置上直接定义 beforeEnter
3、组件内的导航钩子有三种:
beforeRouteEnter 在进入当前组件对应的路由前调用
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 在离开当前组件对应的路由前调用

9、Vue实例的生命周期讲一下, mounted阶段真实DOM存在了嘛?

Vue实例从创建到销毁的过程,就是生命周期。
也就是:开始创建->初始化数据->编译模板->挂载dom->数据更新重新渲染虚拟 dom->最后销毁。这一系列的过程就是vue的生命周期。所以在mounted阶段真实的DOM就已经存在了。
beforeCreate:vue实例的挂载元素el和数据对象data都还没有进行初始化,还是一个 undefined状态
created: 此时vue实例的数据对象data已经有了,可以访问里面的数据和方法, el还没有,也没有挂载dom
beforeMount: 在这里vue实例的元素el和数据对象都有了,只不过在挂载之前还是虚拟的dom节点
mounted: vue实例已经挂在到真实的dom上,可以通过对 dom操作来获取dom节点
beforeUpdate: 响应式数据更新时调用,发生在虚拟dom打补丁之前,适合在更新之前访问现有的 dom,比如手动移除已添加的事件监听器
updated: 虚拟dom重新渲染和打补丁之后调用,组成新的 dom已经更新,避免在这个钩子函数中操作数据,防止死循环。
activated: 当组件keep-alive激活时被调用
deactivated:当组件keep-alive停用时被调用
beforeDestroy: vue实例在销毁前调用,在这里还可以使用,通过this也能访问到实例,可以在这里对一些不用的定时器进行清除,解绑事件。
destroyed:vue实例销毁后调用,调用后所有事件监听器会被移除,所有的子实例都会被销毁。

Vue2与Vue3生命周期对比图:

10、vuex有哪几种属性,怎么使用?哪种功能场景使用它

vuex是一个专门为vue.js开发的状态管理模式,每一个vuex应用核心就是store(仓库)。store基本上就是一个容器,它包含着你的应用中大部分的state(状态)
vuex的状态存储是响应式的,当 vue组件中store中读取状态时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变store中的状态的唯一途径就是显示 commit(提交)mutation,这样使得我们可以方便地跟踪每一个状态的变化。
State: 定义了应用状态的数据结构,可以在这里设置默认的初始状态
Getter: 允许组件从Stroe中获取数据, mapGetters辅助函数仅仅是将store中的getter映射到计算属性。
Mutation: 唯一更改store中状态的方法,且必须是同步函数。
Action: 用于提交muatation, 而不是直接变更状态,可以包含任意异步操作。
Module: modules,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理;如果所有的状态或者方法都写在一个store里面,将会变得非常臃肿,难以维护。

11、vuex中state存储的数据如果页面刷新此时数据还会有吗?(刷新之后销毁了)

12、v-bind和v-model的区别, v-model原理知道吗?

   v-bind用来绑定数据和属性以及表达式
   v-model使用在表单中,实现双向数据绑定的。

//语法糖写法
<input type="text" v-model="name" >
  
  //还原为以下实例
<input type="text" 
 v-bind:value="name" 
 v-on:input="name=$event.target.value">

13、Vue中的常见指令有那些? 

v-text/v-html/v-for/v-show/v-if/v-else/v-click/v-bind/v-on/v-model/v-slot…

14、vue路由的两种模式

1.hash模式
特点:在url地址上有#号
实现的原理:原生的hasChange事件来实现,来监听hash值的变化
window.onhaschange=function(){}
刷新页面的时候:不会去发送请求,页面不会有任何问题,不需要后端来配合

2.history模式
特点:在url地址上没有#号,比较与hash模式看起来好看一些
实现的原理:利用的是history的api 来实现的 popState() 来实现的
刷新页面的时候:会去发送请求然后会导致页面出现找不到的情况,需要后端来配合解决

15、vue的插槽(slot)主要分三种:

默认插槽,具名插槽,作用域插槽
vue中的插槽,指的是子组件中提供给父组件使用的一个占位符;
用标签表示,父组件可以在这个占位符中填充任何模板代码,比如HTML、组件等,填充的内容会替换掉子组件的标签(替换占位符)。

16、vue数据的双向绑定

“vue数据的双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持,在数据变化时发布消息给订阅者,触发相应的监听回调。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;”

17.MVVM模式

1、MVVM相比较于MVP,将Presenter变成ViewModel,ViewModel可以理解成是View的数据模型和Presenter的合体
2、MVVM中的数据可以实现双向绑定,即View层数据变化则ViewModel中的数据也随之变化,反之ViewModel中的数据变化,则View层数据也随之变化`
注:MVC指的是Model-View-Controller,分别代表着模型层、视图层、控制器。

18.Vue和React的区别是什么?

一、核心思想不同
Vue是一个灵活易用的渐进式双向绑定的MVVM框架。
React的核心思想是声明式渲染和组件化、单向数据流,React既不属于MVC也不属于MVVM架构。
注:React的单向数据流指的是数据主要从父节点通过props传递到子节点,
如果顶层某个props改变了,React会重新渲染所有的子节点,但是单向数据流并非单向绑定,
React想要从一个组件去更新另一个组件的状态,需要进行状态提升,即将状态提升到他们最近的
祖先组件中,触发父组件的状态变更,从而影响另一个组件的显示。单向数据流的好处是能够保证
状态改变的可追溯性,假如,父组件维护了一个状态,子组件如果能够随意更改父组件的状态,那
么各组件的状态改变就会变得难以追溯
二、组件写法上不同
Vue的组件写法是通过template的单文件组件格式。
React的组件写法是JSX+inline style,也就是吧HTML和CSS全部写进JavaScript中。
三、Diff算法不同
Diff算法是一种对比算法,主要是对比旧的虚拟DOM和新的虚拟DOM,找出发生更改的节点,并只
更新这些接地那,而不更新未发生变化的节点,从而准确的更新DOM,减少操作真实DOM的次数,
提高性能。
vue对比节点,如果节点元素类型相同,但是className不同,认为是不同类型的元素,会进行删
除重建,但是react则会认为是同类型的节点,只会修改节点属性。
vue的列表比对采用的是首尾指针法,而react采用的是从左到右依次比对的方式,当一个集合只
是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点
移动到最后一个,从这点上来说vue的对比方式更加高效。
四、响应式原理不同
React的响应式原理
React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染。

Vue的响应式原理
vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和
setter,每个Vue组件实例都有一个对应的watcher实例,在组件初次渲染的时候会记录组件用到
了那些数据,当数据发生改变的时候,会触发setter方法,并通知所有依赖这个数据的watcher实
例调用update方法去触发组件的compile渲染方法,进行渲染数据。

19.vue 中的 keep-alive

keep-alive 是 vue 中的内置组件,能够在组件切换过程中将状态保留在内存中,防止重复的渲染 DOM;
keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们;
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated 和 deactivated )

20.vue.nextTick()方法

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
使用:

this.$nextTick(function(){
            console.log('dom渲染完成');  //输出:修改后的值
        })

什么时候需要用的Vue.nextTick()?
1、Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。
2.vue改变dom元素结构后使用vue.$nextTick()方法来实现dom数据更新后延迟执行后续代码

21.Vue2与Vue3的区别

1.双向数据绑定原理不同
Vue2 的双向数据绑定是利用ES5的一个APIObject.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。

Vue3 中使用ES6的Proxy API对数据代理。

Vue3 使用数据代理的优势有以下几点:1)definePropert 只能监听某个属性,不能对整个对象进行监听 2)可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)3)可以监听数组,不用再单独的对数组做特异性操作,Vue3可以检测到数组内部数据的变化

2.是否支持碎片

Vue2 不支持碎片。Vue3 支持碎片,就是说可以拥有多个根节点

3.API 类型不同

Vue2 使用选项类型api,选项型api 在代码里分割了不同的属性:data,computed,method等。

Vue3 使用合成型api,新的合成型api 能让我们使用方法来分割,相比于旧的api 使用属性来分组,这样代码会更加简便和整洁。

4.定义数据变量和方法不同
Vue2是把数据放到了data 中,在 Vue2中 定义数据变量是data(){},创建的方法要在method:{}

Vue3 就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。使用以下三个步骤来建立反应性数据:

   1)从vue 引入 reactive;

   2)使用 reactive ()方法来声明数据为响应性数据;

   3) 使用setup()方法来返回我们的响应性数据,从而template 可以获取这些响应性数据。

5.生命周期钩子函数不同
Vue2 中的生命周期:beforeCreate 组件创建之前;created 组建创建之后;beforeMount 组件挂载到页面之前执行;Mounted 组件挂载到页面之后执行,beforeUpdate 组件更新之前;updated组件更新之后

Vue3 中的生命周期:setup 开始创建组件;onBeforeMount 组件挂载到页面之前执行;onMounted 组件挂载到页面之后执行;onBeforeUpdate 组件更新之前;onUpdated 组件更新之后;

而且 Vue3 生命周期在调用前需要先进行引入。除了这些钩子函数外,Vue3 还增加了 onRenderTracked 和onRenderTriggered 函数。

6.父子传参不同
Vue2 父传子,用props ;子传父用事件Emitting Events。在Vue2 中,会调用this$emit 然后传入事件名和对象。

Vue3 父传子,用props;子传父用Emitting Events 。在Vue3 中的setup()中的第一参数content 对象中就有 emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit 就可以在setup 方法中随意使用了。

7.指令与插槽不同
Vue2 中使用slot 可以直接使用slot ;v-for 与v-if 在Vue2中优先级高的是v-for 指令,而且不建议一起使用。

Vue3 中必须是使用v-slot的形式;vue 3中v-for 与v-if ,只会把当前v-if 当作v-for 的一个判断语句,不会相互冲突;

Vue3 中移除keyCode 作为v-on 的修饰符,当然也不支持config.keyCodes,取而代之的是使用键名来作为事件的修饰符来使用,于是Vue.config.keyCodes 也被弃用了-vue2我们在监听按键事件的时候,是可以通过在事件后面加上按键码来实现监听某一个按键的

四、VUE3

1、vue3编译流程

vue3的编译分两部分:编译期和运行期。编译期间,先通过vue-loader把单文件组件编译成符合原生JavaScript语法的模块;运行期间,会通过内置的模板引擎对模板进行编译,生成render()函数,且按照先父组件后子组件的顺序对模板进行编译。

2、vue3内的宏是什么?和语法糖的区别?

按照vue3官方的说法,defineProps()defineEmits()defineExpose()都是宏,它们使用形式上和函数调用差不多,区别是,第一,这些宏不需要通过模块导入;第二,这些宏会在编译期被编译成符合JavaScript原生语法的代码。语法糖也会被编译成符合JavaScript原生语法的代码,vue3官方对这两者进行区分是因为,语法糖创建的是不曾有过的语法,而宏指的是给现有语法新的含义。

3、<script setup/>的实现原理

<script setup/>本质是setup()函数的语法糖,vue-loader在编译期间会把<script setup/>内的代码编译成setup()函数,把defineExpose()内指定的变量编译为setup()函数的返回值。

4、组件实例是什么

每个单文件组件都有一个组件实例,组件实例是用来存放组件状态的,可以在setup()函数内,通过getCurrentInstance()获取组件实例

5、启动程序的时候,vue3单文件组件的初始化流程

单文件组件由组件实例、相应vnode、相应node三部分组成。初始化的时候,先创建组件的实例;接着用父组件vnode初始化实例的propsattrsslots成员;然后执行setup()函数;接着把模板编译成render()函数;接着创建vnode;如果当前组件有子组件,则切换到子组件,重复上面的步骤;子组件都初始化完成后,把vnode同步到node上。

6、createApp()mount()做了什么工作

程序在初始化的时候会依次调用createApp()mount()函数,createApp()负责执行平台相关的代码,mount()负责执行平台无关的代码。createApp()主要做了两件事,第一,创建一个渲染器,它可用于把vdom转化为平台相关的界面代码;第二,返回一个App实例。mount()的作用就是,创建组件实例、vdom、完成依赖收集、并通过渲染器把vdom渲染成平台相关的界面代码

7、组件的生命周期

单个组件的生命周期:初始化阶段,先执行setup函数初始化实例,接着执行onBeforeMount,最后执行onMounted;运行阶段,先执行onBeforeUpdate,再执行onUpdated;关闭阶段,先执行onBeforeUnmount,再执行onMounted
父子组件的生命周期:初始化阶段,先执行父组件的onBeforeMount,然后执行子组件的onBeforeMount,接着执行子组件的onMounted,最后执行父组件的onMounted;运行阶段、关闭阶段同理可推。

8、什么是虚拟DOM?diff算法?

虚拟dom是用JavaScript语言描述的一个树形结构,用于缓存发生变化的响应式数据,当到达下次刷新周期的时候,一次性把所有发生变化的数据同步到实际dom上。diff算法是同步vdom和dom数据的算法,采用就地复用的策略实现高效同步

9、任务调度系统

vue3内部维持了3类队列:pre队列——组件更新前置任务队列;queue队列——组件更新时的任务队列;post队列——组件更新后置任务队列。vue3内所有异步任务都会被推进这些队列,当本次宏任务执行结束的时候,会执行一个名为flushJob的微任务,该微任务会按照pre>queue>post的优先级,依次执行每个队列中的函数

10、异步刷新是什么?怎么实现的

异步刷新指的是在响应式数据发生更改后,只更新vdom,不立即更新dom,当下一个刷新周期到来的时候,次性把所有发生变化的数据同步到实际dom上。
异步刷新是通过job队列+Promise+diff算法实现的,当响应式数据发生更改时候,会立即更新vdom上的数据,待下个刷新周期到来前,开始执行flushJob微任务,flushJob会通过diff算法完成vdom和dom之间的数据同步

11、nextTick()的实现原理

nextTick()作用是,让回调函数在下一次刷新DOM的时候,具体来说在onUpdated后执行。vue3内的nextTick()是通过Promise实现,当微任务flushJob把Job队列中的所有函数都执行完了后,nextTick()回调会被推入微任务队列,然后开始执行

12、有哪三种Effect?作用

vue3组件内有3类Effect:computedEffectwatchEffectrenderEffect。正是通过这些Effect才实现数据可监听函数的绑定,其中computedEffectwatchEffectsetup()期间创建,而renderEffectonBeforeMountonMounted之间创建

13、watch()watchEffect()computed()的区别

三者都用于实现数据和监听函数的绑定。原理上来说,三者的共同点是,都会在setup期间、即onBeforeMount之前,创建一个Effect对象,并执行依赖收集函数进行依赖收集。区别是,第一,watchEffect、computed的依赖收集函数和监听函数是同一个函数,而watch的依赖收集函数和回调函数是两个函数,这就导致了watchEffect和computed参数中的监听函数会在程序初始化期间执行一次,而watch参数中的监听函数默认不会执行;第二,computed参数中的监听函数在变量发生变化后立即执行,watchEffect、watch参数中的监听函数执行时机可由用户指定,可以立即执行,也可以异步执行

14、响应式编程的实现原理

响应式编程的核心是通过发布订阅模式、Effect对象、重写get函数实现的。发布订阅模式实现数据和监听函数的绑定,Effect对象内封装有监听函数和调度策略,get函数则负责进行依赖收集和返回数据

15、ref()reactive()的区别

两者都用于实现响应式变量,区别在于reactive()只可以封装引用类型,ref()可以封装任意类型;reactive()通过Proxy实现,ref()通过类封装,并重写get/set实现。就目前而言,要访问ref()封装的变量,需要通过value函数获取

16、ref()ref(null)的区别

ref()的作用是封装普通数据,实现响应式数据;ref(null)的作用是获取子组件实例

17、attrsprops的区别

attrsprops的作用都是,以模板属性的形式,从父组件传递数据给子组件。区别是,子组件中获取从父组件传过来的props数据,首先需要用defineProps()声明,而获取attrs数据不需要声明。props的优势是增加了代码可读性,attrs的优势是传值更灵活、代码更简洁

18、provide()inject()实现原理

在祖先组件里通过provide()提供的数据,可以在任意子孙组件里通过inject()获取。它是通过原型链实现的

19、emit()的实现原理

emit()是通过发布订阅模式实现的,子组件发送一个事件,父组件收到该事件会立即执行相应的函数

20、单向数据流的原因

单向数据流指的是在 vue中约定,父组件传递给子组件的数据只能单向流动。也就是说,当一个父组件将其数据传递给一个子组件时,子组件不能直接修改该数据,而是需要通过向父组件发送事件来请求更改该数据。这种单向数据流的方式可以确保应用程序的数据流向清晰明确,易于理解和维护

21、模板的编译流程?在什么时候编译

模板会先被解析成AST对象,然后通过遍历AST对象生成渲染函数。这个过程发生在setup()函数执行结束后、onBeforeMount之前

22、组件注册是什么

若要在vue内使用自定义组件,需要先注册组件。组件注册可以看作是一种声明,用于告诉编译器,某个标签名是组件。组件注册分为局部注册和全局注册,全局注册的组件可以在整个程序内使用,局部注册的组件只可以在特定组件内使用

23、指令的实现原理

vue3内的指令是,本质上是一系列钩子函数的集合,可以为这些钩子函数指定执行时机,包括:onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted

24、各种指令的区别和作用

v-showv-if的区别:v-show=false的组件会被创建,但是不会显示;v-if=false的组件不会被创建。
v-onv-bind的区别:v-on用于绑定事件和事件处理函数;v-bind用于绑定变量。
v-textv-html和插值表达式的区别:插值表达式存在闪屏的问题,而v-textv-html不存在该问题;插值表达式和v-text都会直接以字符串的形式显示html,v-html则可以渲染html标签对

25、v-for为什么要加key?v-for为什么不能和v-if一起使用

v-for要加key是因为,第一,vue的同步算法采用就地复用的策略,在没有key值的情况下,diff算法认为标签类型相同的元素可以进行复用;有key值的情况下,diff算法认为标签类型相同且key值相等的元素才可以复用。第二,vue只在v-model实现了数据双向绑定,其它地方实现的都是单向绑定,<input/>框内的文字发生变化,不会实时同步到数据上。
在vue3中,v-if的优先级高于v-for,导致v-if访问不了v-for中的变量,因此不能把 v-if v-for 同时用在同一个元素上

26、v-model的实现原理

v-model实现了数据的双向绑定,它是通过响应式数据+监听输入框事件实现的

27、hook是什么

hook是一种编程封装思想,通过闭包实现,早在ES6诞生之前,这种编程封装思想在JavaScript中应用广泛,只不过在React17出现后,React官方把这种思想命名为hook,vue3借鉴react命名,也把这种封装思想称作hook

28、vue3插件是什么

vue3内的所谓的插件不是常规意义上的插件,而是指mount()前执行的初始化函数,vue插件通常用来为 vue 添加全局功能。vue3内通过use()函数引入插件,use()的作用就是执行参数对象内的install()函数

29、什么是异步组件

网页的渲染流程一般分两部,先从网络加载网页,然后渲染。异步组件指的是,首次加载和渲染网页的时候,不从网络上加载该组件对应的JavaScript文件,而是等到需要显示该组件的时候,再从网络上加载该组件并渲染显示出来

30、前端路由是什么?什么是路由懒加载

路由能够根据不同的URL地址返回给用户不同的内容或页面,如果路由位于浏览器端,就是前端路由;如果路由位于服务器端,就是后端路由。
对于SPA单页应用,打包构建时,JavaScript包会变得非常大,会使得页面首次加载的时间变长 。若将不同路由对应的组件分割成不同的文件,然后当路由被访问的时候才从服务器加载对应文件,这就是路由的懒加载

五、git

常用的git命令

git config
用法:git config –global user.name “[name]”
用法:git config –global user.email “[email address]”
git init
用法:git init [repository name]
该命令可用于创建一个新的代码库
git clone
用法:git clone [url]
该命令可用于通过指定的URL获取一个代码库。
git add 添加文件到仓库
git status 查看仓库当前的状态,显示有变更的文件。
git diff 比较文件的不同,即暂存区和工作区的差异。
git commit 提交暂存区到本地仓库。
git reset 回退版本。
git push
用法:git push [variable name] master
该命令可以将主分支上提交的变更发送到远程代码库。

六、webpack

1、webpack的作用是什么,谈谈你对它的理解?
现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决Scss,Less……新增样式的扩展写法的编译工作。
所以现代化的前端已经完全依赖于webpack的辅助了。
现在最流行的三个前端框架,可以说和webpack已经紧密相连,框架官方都推出了和自身框架依赖的webpack构建工具。
react.js+WebPack
vue.js+WebPack
AngluarJS+WebPack
2、webpack的工作原理?
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。在3.0出现后,Webpack还肩负起了优化项目的责任。
3、webpack打包原理
把一切都视为模块:不管是 css、JS、Image 还是 html 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。
按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
4、webpack的核心概念
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
5、Webpack的基本功能有哪些?
代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
6、gulp/grunt 与 webpack的区别是什么?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。grunt和gulp是基于任务和流(Task、Stream)的。
类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。webpack是基于入口的。
webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
7、webpack是解决什么问题而生的?
如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。

七、前端工程化理解(模块化、组件化、规范化、自动化)

JS的模块化、css的模块化、资源的模块化
从UI拆分下来的每个包含模板(HTML)+样式(CSS)+逻辑(JS)功能完备的结构单元
HTML规范、CSS规范、JS规范、图片规范、命名规范
图标合并、持续集成、自动化构建、自动化部署、自动化测试

八、组件封装过程

建立组件的模板,先把架子搭起来,写写样式,考虑好组件的基本逻辑。  
准备好组件的数据输入。即分析好逻辑,定好 props 里面的数据、类型。  
准备好组件的数据输出。即根据组件逻辑,做好要暴露出来的方法。  
封装完毕了,直接调用即可。

九、WebSocket前后端交互

1.什么是websocket
WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议

2.websocket的原理
websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"

3.websocket的心跳机制和重连机制
心跳机制:客户端每隔一段时间向服务端发送一个特有的心跳消息,每次服务端收到消息后只需将消息返回,此时,若二者还保持连接,则客户端就会收到消息,若没收到,则说明连接断开,此时,客户端就要主动重连,完成一个周期

断线重连:若某时间段内客户端发送了消息,而服务端未返回,则认定为断线;这个时候会触发到websocket中的onclose事件,需要重新连接服务

前端:

/**
 * 初始化websocket连接
 */
function initWebSocket() {
	let uId = 1;
	var websocket = null;
	if('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8008/webSocket"+uId );
	} else {
		alert("该浏览器不支持websocket!");
	}
	websocket.onopen = function(event) {
		console.log("建立连接");
		websocket.send('Hello WebSockets!');
	}
	websocket.onclose = function(event) {
		console.log('连接关闭')
		reconnect(); //尝试重连websocket
	}
	//建立通信后,监听到后端的数据传递
	websocket.onmessage = function(event) {
		let data = JSON.parse(event.data);
		//业务处理....
		if(data.step == 1){
		   alert(data.msg);
		}
	}
	websocket.onerror = function() {
		// notify.warn("websocket通信发生错误!");
		// initWebSocket()
	}
	window.onbeforeunload = function() {
		websocket.close();
	}
// 重连
function reconnect() {
	console.log("正在重连");
	// 进行重连
	setTimeout(function () {
		initWebSocket();
	}, 1000);
}

十、 浏览器从输入到页面渲染的过程


1)输入 URL: 用户在浏览器地址栏输入网址或点击链接,浏览器接收到 URL 请求。

2)发起请求: 浏览器向服务器发送 HTTP 请求,请求网页的资源(HTML、CSS、JavaScript、图片等)。

3)接收响应: 服务器接收到请求后,返回相应的资源文件给浏览器。

4)构建 DOM 树: 浏览器开始解析 HTML 文件,构建 DOM(文档对象模型)树,表示页面的结构。

5)构建 CSSOM 树: 解析 CSS 文件,构建 CSSOM(CSS 对象模型)树,表示页面的样式。

6)合并 DOM 和 CSSOM: 浏览器将 DOM 树和 CSSOM 树合并,生成渲染树(Render Tree),用于页面的布局和绘制。

7)布局(Layout): 浏览器根据渲染树计算每个节点在页面上的位置和大小,进行布局。

8)绘制(Paint): 浏览器根据布局信息,将页面上的内容绘制到屏幕上。

9)渲染页面: 浏览器渲染完页面后,显示在用户的屏幕上,用户可以看到页面内容。

10)交互和事件处理: 用户可以与页面进行交互,点击链接、按钮等,触发相应的事件处理函数。

简短版:在这个过程中,浏览器会进行网络请求、解析 HTML、构建 DOM 树、解析 CSS、计算布局、绘制页面等操作,最终将页面呈现给用户。

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值