web前端面试题

JavaScript

普通函数与箭头函数的区别?

  • 箭头函数没有原型对象prototype
  • 箭头函数是匿名函数不能作为构造函数不能被new
  • 箭头函数不能当作Generator函数,不能使用yeild关键字。
  • 箭头函数不绑定arguments,取而代之用…rest参数解决
  • 箭头函数不绑定this,会捕获他所在上下文的this值,作为自己的this值
  • 箭头函数的 this永远指向其上下文的this ,任何方法都改变不了其指向,如 call() , bind() , apply()

Generator函数

Generator函数也叫生成器函数是 ES6提供的一种异步编程解决方案
Generator函数像一个状态机,保存了许多状态,并将这些状态作为遍历器对象返回
为了和传统的函数区别,Generator函数function后面会跟着一个*号,函数内部通常有许多yield后跟着表达式表示状态。
Generator函数特性
1.传统的函数被调用后会立马执行,且一次执行到return结束,而Generator函数被调用时不会立马执行内部的语句,而是返回了一个遍历器对象,
2.由遍历器对象的next()方法启动,遇到yield后又会暂停,直到下一个next()才会继续启动。
Generator函数和构造函数的区别
1.Generator 函数返回的遍历器是 Generator 函数的实例,也继承了 Generator 函数的prototype对象上的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* gen()
{
yield 1;
console.log(yield 2); // undefined
yield 3;
return 4;
}
let ge=gen() //返回一个遍历器对象
console.log(ge.next()) // {value: 1, done: false}
console.log(ge.next()) // {value: 2, done: false}
console.log(ge.next()) // {value: 3, done: false}
console.log(ge.next()) // {value: 4, done: true}
console.log(ge.next()) // {value: undefined , done: true}

什么是Promise?

promise是解决异步的方法。

  • Promise对象有两个特点:
    • 1)对象的状态不受外界的影响。
    • 2)状态一旦改变,便不会再次改变。而且它的状态改变只会由(pending->fulfilled、pending->rejected)并且这两种情况只要发生其中一个,状态便固定了。
  • Promise的优点:
    • 支持链式调用(可以将异步操作以同步的方式显示出来)避免回调地狱

promise的使用场景?

1.获取文件信息
2.配合Ajax获取信息
3.解决回调地狱,实现单行任务队列
4.node中进行本地操作的异步过程

什么是回调地狱?

多层回调函数的相互嵌套 就形成了回调地狱
缺点:
代码耦合性太强 牵一发而动全身 难以维护
大量冗余的代码相互嵌套 代码的可读性变差

对this的理解?

1.任何情况下直接在script中写入的this都指向window
2.函数中的this在非严格模式下指向的是window,严格模式下是指向undefined
3.箭头函数中的this都指向函数外上下文环境的this指向
4.对象中的this指向对象外上下文环境的this
5.回调中的this指向:
①setTimeout,setInterval回调函数不管是不是严格模式都指向window
②通过函数内执行当前回调函数和递归中的this,在非严格模式下指向的是window,严格模式下指向的是undefined
③使用arguments执行函数时,this指向arguments
④事件中的回调函数,this指向事件监听的对象
6.call,apply,bind方法执行时,如果第一个参数传入的不是null或者是undefined,那么传入什么this指向什么;如果第一个参数传入的是null或undefined,非严格模式下this指向window
7.ES6的类中 this的指向
①构造函数中的this指向当前实例类所产生的实例对象
②类中实例化方法中this指向 谁执行该方法,this指向谁
③类中静态方法中this指向该类或者该类的构造函数
④类中实例化箭头方法,this仍然指向当前类实例化的实例对象
8.ES5的原形对象中的this指向
在原型的方法中,this指向实例化当前构造函数的实例化对象,就是谁执行该方法,this就指向谁

什么是事件循环

事件循环机制就是一种同步编程模型,用于异步处理操作,当代码中遇到需要等待操作结果的语句时 js引擎不会一直等待,而是将该语句放入事件的队列中,并执行下一步语句,异步操作完成的时候,就会将其对应的事件加入到事件队列中

事件循环机制的组成

  • 事件队列:
    用来存储事件的队列,包括鼠标点击、键盘输入、定时器等等
  • 执行栈:
    用来存储正在执行的代码
  • 宏任务:
    指的是需要被放入事件队列中的任务,例如setTimeout/setInterval等
  • 微任务:
    指的是需要当前任务执行完成后立即执行的任务,例如Promise的then/catch/finally方法

事件轮询的机制 也叫事件循环的机制(eventLoop)

一个用来等待和发送消息和事件的程序结构。
1、所有任务都在主线程上执行,形成一个执行栈。
2、主线程发现有异步任务,如果是微任务就把他放到微任务的消息队列里,如果是宏任务就把他放到宏任务的消息队列里。
3、执行栈所有同步任务执行完毕。
4、执行微任务队列,之后再执行宏任务队列。
5、以上步骤重复执行就是事件轮询

  • 宏任务:setInterVal setTimeout ajax
  • 微任务:promise async await .then
    • promise优先于setTimeout,setTimeout回调函数最后执行,promise一旦被定义就会立即执行

for in和for of区别?

  • for in 用于遍历对象的键,会遍历自身和原型链上的枚举属性 如果是数组将会把数组索引当做对象来遍历
  • 枚举属性是由enumerable值决定的,true为可枚举,false为不可枚举
  • 可枚举可以理解为是否可以被遍历
  • JS中预定义的原型属性一般是不可枚举的,而自己定义的属性一般可枚举
  • 可以通过propertyIsEnumerable方法判断该属性是否可枚举
  • for offorEach一样,是直接得到值
  • for of不能用于对象

new一个对象的过程?

  • 在堆内存中申请了一块空间 创建一个新对象
  • 将新对象的__proto__指向构造函数中的原型对象prototype
  • 新对象会绑定到函数调用的this(比如:实例对象捕获构造函数的this当做自己的this)
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 返回新对象。| 将初始化完毕的新对象地址,保存到等号左边的变量中

什么是闭包?

能够读取外层函数内部变量的函数

  • 当内层函数调用外层函数的变量或参数时产生闭包
    1.访问作用域
    2.函数嵌套
    3.在作用域外被调用
  • 闭包的优点:只有函数内部的子函数才能读取局部变量,可以避免全局污染(避免全局污染)
  • 闭包的缺点:变量常驻内存,得不到释放会使内存持续增压,导致内存泄漏
    使用场景
    1.setTimeout
    2.回调
    3.函数防抖

什么是堆内存和栈内存?

堆内存是一种非连续的树形存储数据结构,每个节点有一个值
栈内存是一种连续存储数据结构,具有先进后出的性质

  • 堆主要用于存放复杂类型的变量

    • 堆是先进先出
    • 空间较大
    • 堆的申请和释放是由程序员控制的,容易产生内存泄漏
  • 栈主要是存储基本类型的变量

    • 栈是先进后出
    • 空间较小
    • 栈是由系统自动分配释放
    • 栈的效率高

节流和防抖?

防抖是连续触发的事件,只会执行最后一个
节流是每隔一段时间触发一次
实现防抖通过定时器
实现节流通过时间戳

  • 使用场景
    防抖:
    search搜索时,用户在不断输入值时,用防抖来节约请求资源。
    登陆,发短信(倒计时),防止用户点击过快,以至于发送多次请求
    节流:
    鼠标不断触发某事件时,如点击,只在单位事件内触发一次.
    懒加载时要监听计算滚动条的位置,但不必要每次滑动都触发,可以降低计算频率,而不必要浪费CPU资源.

哪些数组方法可以改变原数组?

shift() unshift() pop() reverse() sort() splice() push()

虚拟DOM

本质上就是一个JS对象,当数据发生变化时,我们不直接操作真实DOM,因为很昂贵,我们去操作这个JS对象,就不会触发大量回流重绘操作,再加上diff算法,可以找到两次虚拟DOM之间改变的部分,从而去一次性更新真实DOM 性能得到了大大的提升

谈一谈垃圾回收机制?

js它具有自动回收机制 就是对那些不再用的变量对象进行回收 进行空间的释放

  • 回收的两种机制
    1.标记清除 当它进入执行环境的时候 它会被打上进入环境 离开的时候再被打上离开环境 被打上离开环境标记的都会被清除掉
    2.引用计数
  • 原理就是 垃圾收集器会定时找出那些不继续使用的变量 然后释放其内存 因为如果内存开销比较打 他的GC会停止响应其他操作 他会阻塞其他应用程序的执行
    垃圾回收是按照固定时间 周期性的去执行的

less 和 sass 的区别 ?

相同点:
Sass和Less都是一种CSS预处理器
区别:

  • Less在JS上运行,Sass在Ruby上使用;
  • 两者编写变量的方式不同;
    变量定义符不一样,less用的是@,而sass用$。
  • 在Less中仅允许循环数值,而在Sass中可以遍历任何类型的数据;
  • Sass有Compass,Less有Preboot。

CSS预处理器是什么?

CSS预处理器是一种脚本语言,用一种专门的编程语言来进行Web页面的样式设计,然后再转换为正常的CSS样式,进而实现构建动态CSS样式。
CSS 预处理器为 CSS 增加了一些编程的特性,无需考虑浏览器的兼容性问题。

纯函数

就是一个函数的返回结果只依赖于它的参数,并且在执行过程中没有副作用,我们就把这个函数叫做纯函数。
(redux中的reducer就是纯函数)
优点:

  • 可复用性 纯函数仅依赖于传入的参数,这意味着你可以随意将这个函数移植到别的代码中,只需要提供他需要的参数即可
  • 可测试性 纯函数非常容易进行单元测试,因为不需要考虑上下文环境,只需要考虑输入和输出。
  • 并行代码 纯函数是健壮的,改变执行次序不会对系统造成影响,因此纯函数的操作可以并行执行。

浏览器的同源策略机制?

同源策略,指的是浏览器限制当前网页只能访问同源的接口资源。
所谓同源 两方必须是同协议、且同域名、且同端口。只要有一个不相同,则会受到浏览器的约束,不允许请求。

token一般存放在哪里?为什么不存放在cookie内?

首先有两个存放位置
一个是本地存储 另一种是cookie
但是两种都有缺点
存在本地存储中 这意味着任何在你的网站上的运行的JavaScript都可以访问,所以容易受到XSS攻击
如果存在cookie内的话,浏览器的请求默认会在请求头中携带cookie,所以容易受到csrf攻击
我的意见是 放到本地存储中
撇开localStorage的各种优点不谈,如果做好适当的XSS防护,收益是远大于风险的。
因为localStorage具有更灵活,更大空间,天然免疫 CSRF的特征。Cookie空间有限,而JWT一半都占用较多字节,而且有时你不止需要存储一个JWT。

  • CSRF攻击
    简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如:发邮件、发信息、甚至财产操作如转账和购买商品)
  • XSS攻击
    是一种代码注入攻击。攻击者通过在目标网站注入恶意脚本,在用户的浏览器上运行。利用这些恶意脚本,攻击者可以获取用户的敏感信息如Cookie、SessionID等,进而危害数据安全。

token是什么

token其实就是访问资源凭证 一般在用户成功登陆后 服务器将登陆凭证做数字签名 加密后的字符串作为token

如何实现一条0.5像素的线

方法一: 定位+缩放
利用的是 transform 缩放功能,将 1px 缩放一半,同时利用定位,将伪元素覆盖整个 div 元素,从而达到伪元素与本身元素的合并效果。

方法二: box-shadow
利用的是 box-shadow 的扩散半径可以设置为 0.5px 原理
方法三: 直接使用border属性

1
border: 0.5px solid #f00;

构造函数

  • 用new关键字来调用定义的函数,称为构造函数。默认返回的是一个新对象,这个新对象具有构造函数定义的变量和函数以及方法

async/await

async/await 是ES7提出的基于Promise的解决异步的最终方案。
async、await使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性 async用于申明一个function是异步的 而await用于等待一个异步方法执行完成

transform的属性

none:不转换。
matrix(mei chui ke si)(n,n,n,n,n,n):定义2D转换,使用六个值的矩阵。
matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n) 定义3D转换,使用 16 个值的 4x4 矩阵。
translate(x,y):定义2D转换。
translate3d(x,y,z):定义3D转换。
translateX(x):X轴转换
translateY(y):Y轴转换
translateZ(z):Z轴3D转换
scale(si gei o)(x,y):定义2D缩放
scale3d(x,y,z):定义3D缩放
scaleX(x):X轴缩放
scaleY(y):Y轴缩放
scaleZ(z):Z轴3D缩放
rotate(angle):定义2D旋转
rotate3d(x,y,z,angle):定义3D旋转。
rotateX(angle):X轴的3D旋转
rotateY(angle):Y轴的3D旋转
rotateZ(angle):Z轴的3D旋转
skew(x-angle,y-angle):定义2D倾斜
skewX(angle):X轴的2D倾斜
skewY(angle):Y轴的2D倾斜

IE盒模型和标准盒模型

  • IE怪异盒子模型(怪异盒)的元素宽度width=content+padding+border
  • 标准盒模型(普通盒模型)的元素宽度width=content+padding+border+margin

css实现三角形

1
2
3
4
5
6
.box{
border-left:100px solid red;
border-right:100px solid transparent;
border-bottom:100px solid transparent;
}

axios

Axios是一个基于promise封装的http请求库
特点:
1、可以转换请求数据和响应数据,会把响应回来的数据转成 JSON类型的数据;
2、拦截请求和响应(相当于给请求加条件);
3、axios.all(promises): 批量发送多个(异步)请求;
4、axios在浏览器端/node 端都可以使用
5、安全性更高

JQ中$()符

叫做jQuery的构造函数。

1
$()就是jQuery(),在里面可以传参数,作用就是获取元素。

普通函数和构造函数的区别

构造函数:

  1. 调用方式不一样 new Fn()
  2. 构造函数内部会创建一个新的对象,构造函数new出来的实例
  3. 函数内部的this指向 构造函数new出来的实例
  4. 默认的返回值是构造函数new出来的实例,return 返回基本类型无效,引用类型有效
    普通函数:
  5. fn()
  6. 在函数的内部不会创建新的对象
  7. 函数内部的this指向调用函数的对象(如果没有对象调用,默认是window)
  8. 返回值由return语句决定

浮动与定位

float只是行内的 左右的改变,如果后面的元素不清除浮动(clear:both)的话 会影响后面元素的位置,
而positon定位的影响比较广,既能定义一个容器的定位也能定义一个容器里面的任意定位

递归函数

递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用,每调用一次就进入新的一层。递归函数必须有结束条件。
优点:
代码简洁。
缺点:
1、时间和空间的消耗比较大
2、重复计算
3、栈溢出

label标签

ajax执行步骤

1.首先创建一个XMLHttpRequest异步对象
2.然后使用open设置请求方式和请求地址
3.用send发送请求
4.监听状态变化
5.接收返回的数据

最后一个元素选中css

:last-child

统计字符串中出现最多的字母

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let str = 'aabbbccdd'
function getChar(str) {
if (typeof str !== 'string') return // 判断参数是否为字符串
const obj = new Object() // 键为字母,值为次数
for (let i = 0; i < str.length; i ++) { // 遍历字符串每一个字母
let char = str.charAt(i) // 当前字母
obj[char] = obj[char] || 0 // 保证初始值为0
obj[char] ++ // 次数加1
}
console.log(obj)
let maxChar // 存储字母
let maxNum = 0 // maxChar字母对应的次数
for(let key in obj) { // 遍历obj
if (obj[key] > maxNum) {
maxChar = key // 比较后存储次数多的字母
maxNum = obj[key] // 以及它对应的次数
}
}
return maxChar // 返回结果
}
console.log('出现次数最多的字母为:' + getChar(str))

自执行函数

  • 属于匿名函数,直接调用;
  • 在初次加载的时候,会执行一次(自执行函数只能执行一次)
  • 自执行函数会形成一个独立的作用域
    优点:将全局变量写在立即执行函数里,作为局部变量,防止变量污染全局(避免多次声明造成变量覆盖)
    缺点:不能重复调用

数组 对象区别

创建方式不同:数组表示有序数据的集合,而对象表示无序数据的集合
调用方法不同
对象键值唯一,数组可以重复
对象没有长度,不能用for循环

元素绑定事件

1、在HTML上绑定点击事件
2、使用js获取元素并添加绑定事件

1
2
3
4
5
6
<input type="button" value="click me" id="btn">
<script>
document.getElementById("btn").onclick = function(){
alert("hello world!");
}
</script>

3、事件侦听注册事件 addEventListener

1
2
3
4
5
6
7
8
9
<body>
<button>方法监听注册事件</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function () {
alert(22);
})
</script>
</body>

4、jQuery 绑定事件
使用click 要引入jquery.js

1
$("button").click(function(){});

数组拉平

  • 递归
  • 原始数组.flat();

postion属性的值有哪些

static(si da tei ke):默认值,元素没有开启定位
relative:元素的相对定位,以自身为参照物
absolute:元素的绝对定位,以开启了定位的祖先元素为参照物
fixed(fei ke si te):元素的固定定位

静态方法 实例方法和原型方法

  • 静态方法
    • 定义在构造函数上的方法
    • 只能被构造函数访问
  • 实例方法
    • 构造函数中this上添加的属性都属于实例属性
    • 只能被实例对象访问
  • 原型方法 是共享的方法
    • 通过构造函数的prototype定义的方法
    • 能被实例直接访问,构造函数需通过prototype才可访问

documentFragment

documentFragment是一个保存多个element的容器对象(保存在内存)当更新其中的一个或者多个element时,页面不会更新。只有当documentFragment容器中保存的所有element更新后再将其插入到页面中才能更新页面。
documentFragment用来批量更新

创建对象的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
- 利用字面量创建对象
优点:简单方便
缺点:无法量产
var obj = {
uname: '张三疯',
age: 18,
sex: '男',
sayHi: function () {
console.log('hi~');
}
}
- 使用对象。
调用对象的属性,我们采取对象名.属性名
console.log(obj.uname);
调用属性另一种方法 对象名['属性名']
console.log(obj['uname']);
调用对象的方法对象名.方法名( )
obj.sayHi();//千万别忘记加上函数的小括号

- 利用new Object创建对象
通过这种方式,我们可以调用任意的构造函数(无参的和带参数的)
var obj = new Object();//O要大写
obj.uname = '张三疯';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function () {
console.log('hi~');
}
console.log(obj.uname);
console.log(obj.age);
console.log(obj['sex']);
obj.sayHi();
利用等号赋值的方法添加对象的属性和方法
每个属性和方法之间用分号结束
- 利用构造函数创建对象
构造函数的语法格式
//创建
function 构造函数名() {
this.属性 = 值;
this.方法 = function () {
}
}
//调用
new 构造函数名();
// 使用
console.log(new 构造函数名().属性)

- 使用Class类的newInstance方法
这个方法调用无参的构造函数创建对象。
- 使用Constructor类的newInstance方法
这个方法调用有参数的和私有的构造函数。
- 使用clone方法
当我们调用一个对象的clone方法,jvm就会创建一个新的对象,将前面对象的内容全部拷贝进去。用clone方法创建对象并不会调用任何构造函数。
- 使用反序列化
当我们序列化和反序列化一个对象,jvm会给我们创建一个单独的对象。在反序列化时,jvm创建对象并不会调用任何构造函数。

本地对象 内置对象 宿主对象

  • 本地对象
    与宿主无关,无论在浏览器还是服务器中都有的对象,就是ECMAScript标准中定义的类(构造函数)
    在使用过程中需要我们手动new创建
    Object、Function、Array、String、Boolean、Number、Date
  • 内置对象
    与宿主无关,无论在浏览器还是服务器中都有的对象 ECMAScript已经帮我们创建好的对象 在使用过程中无需我们动手new创建
    Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)
  • 宿主对象
    浏览器提供的对象。所有的BOM和DOM都是宿主对象
    什么是宿主?
    宿主就是指JavaScript运行环境,js可以在浏览器中运行,也可以在服务器上运行(nodejs)
    对于嵌入到网页中的js来说,其宿主对象就是浏览器,所以宿主对象就是浏览器提供的对象
    所有的BOM和DOM都是宿主对象

SPA

​ SPA(Single Page Application)单页面应用程序,页面内容的变化通过ajax局部更新实现,同时支持浏览器地址栏的前进与后退操作,又称单页面多视图。其实现原理是基于url地址的hash变化,hash改变会导致浏览器访问记录的改变,但不会触发新的url请求。SPA最核心的技术点就是前端路由。
优点:

  • 良好的交互体验 内容的改变不会重新加载页面 页面数据通过ajax异步获取 没有页面之间的跳转 不会出现跳转白屏的现象
  • 良好的前后端分离工作模式 前端只需要专注于页面的渲染 更利于前端工程化的发展 后端只需要专注于API接口的提供 更易实现API接口的复用
  • 减轻服务器的压力 服务器只提供数据不负责页面的合成和逻辑的处理 吞吐能力提高几倍
    缺点:
  • 首屏加载慢 解决方案 路由懒加载 CDN加速 代码压缩 网络传输压缩
  • 不利于SEO(搜索引擎优化) 解决方案:SSR服务器端渲染

JQ链式调用

.:用于链式调用  jq方法的返回值,除了获取,几乎均返回jq对象
链式调用是通过return this的形式来实现的;通过对象上的方法,最后加上return this,把对象再返回来,对象就可以再继续调用方法,实现链式操作了;

  • jq中链式结构断开怎么办?用end()
    end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态。

链式调用的好处:节省代码量,代码看起来更优雅
链式调用的问题:所有对象的方法返回的都是对象本身,也就是说没有返回值,所以这种方法不一定在任何环境下都适合。

网页渲染过程

1.解析HTML文件,构建 DOM Tree(dom树)
2.解析CSS,构建 CSSOM Tree(CSS规则树)
3.将 DOM Tree 和 CSSOM Tree合并,构建Render tree(渲染树)
4.reflow(重排):根据Render tree计算节点信息(Layout)
5.repaint(重绘):根据计算好的信息绘制整个页面(Painting)

获取元素属性

innerHTML、outerHTML、innerText 、outerText、value 属于原生javascript的方法。
  text()、html(),val()属于jQuery中的方法。

回调函数

函数当作参数传递
函数当作返回值返回
回调函数就是一个通过函数指针调用的函数

css权重

Css的权重是指样式的优先级
内联样式,权重值为1000
ID选择器,权重值为100
类、伪类,权重值为10
标签选择器,权重为1

delete与vue.delete

delete只是将删除的元素变成了undefined 其他的元素的键值还是不变。数组长度也不变。
Vue.delete是直接删除该元素,也改变了数组的键值,长度发生变化。

盒子模型

包括:外边距,边框,内边距,和实际内容。
Margin(外边距)
Border(边框)
Padding(内边距)
Content(内容)

javaScript的组成

ECMAScript:描述了JS的语法和基本对象。
DOM:处理网页内容的方法和接口
BOM:与浏览器交互的方法和接口

JSON方法

JSON 指的是 JavaScript 对象表示法
JSON 是轻量级的文本数据交换格式
JSON 具有自我描述性,更易理解
JSON.stringify() 我们可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串。
JSON.parse() 我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。

http状态码

1、200 :请求成功
2、204: 服务器成功处理了请求,但没有返回任何内容。
3、301: 请求的网页已永久移动到新位置。。
4、302: 请求的网页临时移动到新位置。
5、400: 服务器不理解请求的语法。
6、403: 服务器拒绝请求。
7、404: 服务器找不到请求的网页。
8、410 :请求的资源永久删除
9、500 :服务器遇到错误
10、503: 服务器目前无法使用

最后一个圆角

1
2
3
ul li:last-child/:nth-last-child(1)||(正数):first-child/:nth-first-child(1){
border-radius:50%;
}

document.reday与window.onload的区别

document.reday表示文档结构已经加载完成 不包含图片等非文字媒体文件
window.onload表示包含图片等文件的所有元素都加载完成。

if有作用域吗

  • 只有函数有作用域,if是没有作用域的。
    • 但是有一种情况会让if看上去有作用域,就是在if {}语句中,使用const、let,他们会有块级作用域。(因为const、let才拥有块级作用域 )

ajax生命周期/状态

0 初始化xhr 请求对象
1 与服务器建立链接并开始向服务器发送请求
2 服务器已经接受请求
3 处理请求
4 请求已处理完成,响应就绪,js可以在此阶段获取数据

同步异步

同步,执行完函数或方法后,需要等待系统返回值或消息,这时程序是阻塞的,必须接收到返回的值或消息后才往下执行其他的命令。
异步,执行完函数或方法后,不必等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。

会话cookie和持久cookie

如果 cookie 不包含到期日期,则可视为会话 cookie。 会话 cookie 存储在内存中,决不会写入磁盘。 当浏览器关闭时,cookie 将从此永久丢失。
如果 cookie 包含到期日期,则可视为持久性 cookie。 在指定的到期日期,cookie 将从磁盘中删除。

  • cookie过期时间设置方式:
    - cookie.setMaxAge(0);//不记录cookie
    - cookie.setMaxAge(-1);//会话级cookie,关闭浏览器失效 –会话cookie
    - cookie.setMaxAge(60*60);//过期时间为1小时 –持久cookie

typeof返回值

是以字符串的形式返回你查看的这个值是什么类型的
typeof可以判断类型也有六种,分别是:
number
string
Boolean
object
undefined
function
检测array返回object
检测null返回object
检测NaN返回number

获取时间

1、获取当前的日期和时间
方法:new Date()
2、获取当前日期
new Date().toLocaleDateString()
3、返回当前时间
new Date().toLocaleTimeString()
4、从Date()对象返回当前 年份
new Date().getFullYear()
5、从Date()对象返回当前 月份
new Date().getMonth()+1
6、从Date()对象返回月份的当前 日
new Date().getDate()
7、从Date()对象返回星期几
new Date().getDay()
8、从Date()对象的 当前 小时
new Date().getHours()

url请求过程

十步版 请求报文 响应报文
1.在浏览器地址栏中输入网址。
2.浏览器通过用户输入的URL构建HTTP请求报文。
3.浏览器发起DNS(寻址)解析,将域名转换为IP地址。
4.浏览器将请求报文发送给服务器。
5.服务器接收请求报文(request),并解析。
6.服务器处理用户请求,并将处理结果封装成HTTP响应报文(response)。
7.服务器将HTTP响应报文发送给浏览器。
8.浏览器接收服务器响应的HTTP响应报文,并解析。
9.浏览器解析 HTML 页面并展示
10.最终浏览器展示出了页面。
完整版:
1.DNS 解析:将域名地址解析成 IP 地址
按照以下顺序进行 DNS 解析:
Browser DNS cache(浏览器 DNS 缓存)
OS DNS cache(系统 DNS 缓存)
Router DNS cache(路由器 DNS 缓存)
ISP DNS cache(网络运营商 DNS 缓存)
Recursive search(递归搜索)(若以上 4 种都未找到,则会进行 Recursive search)
2. TCP 连接:TCP 三次握手
第一次握手:由浏览器发起,告诉服务器我要请求数据
第二次握手:由服务器发出,告诉浏览器我准备好接受数据了,你可以发送请求了
第三次握手:由浏览器发出,告诉服务器我马上就发,你准备接受
3. 发送 HTTP 请求/处理请求:请求报文
4. 接受响应:响应报文
5. 浏览器解析、渲染页面
6. 断开连接:TCP 四次挥手
第一次挥手:由浏览器发器,发送给服务器,我东西发完了(请求报文),你准备关闭吧
第二次挥手:由服务器发器,告诉浏览器,我东西接受完了(请求报文),我准备关闭了,你也准备好
第三次挥手:由服务器发器,告诉浏览器,我东西发送完了(响应报文),你准备关吧
第四次挥手:由浏览器发器,告诉服务器,我东西接受完了,我准备关了,你也准备好吧

数据类型

基本数据类型->string、number、Boolean、null、undefined、symbol
引用数据类型->array、object、function
基本数据类型是保存在栈内存中,操作的是值,改变源数据不会影响新的变量
引用数据类型保存在堆内存中,操作的是地址,改变其中一个会影响另一个

回流和重绘

改变某个元素的结构之后,会重新绘制元素的样式(重绘),此时会引起浏览器重绘

  • 改变页面布局之后,会发生回流布局重新排列,(回流会引起重绘,但是重绘不会引起回流)

如何减少回流和重绘

  • 1.浏览器中的优化机制
    浏览器会维护一个队列,队列中存放的是会触发回流和重绘的操作,当队列中的操作达到一定阀值或者到了一定的时间间隔时,浏览器就会清空队列,进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。

  • 2.自己进行优化
    使用className。集中修改样式
    使用定位让元素脱离文档流。
    在设置display:none;的元素上操作,最后显示出来
    使用文档片段(document fragment),在当前DOM外构建一个子树,在它上面操作所有DOM,再把它拷贝回文档。

GET 和 POST

GET把参数包含在URL中,POST通过request body传递参数。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST没有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

内存泄漏

1)意外的全局变量引起的内存泄漏
原因:
解决:可以使用严格模式避免全局变量,不会被回收

2)闭包引起的内存泄漏
原因:闭包可以维持函数内局部变量,使其得不到释放
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用

3)没有清理的DOM元素引用
原因:对象中还存在对dom的引用
解决:手动删除即可

4) 忘记的定时器或者回调
原因:定时器中有dom的引用,即使dom删除了,但是定时器还在
解决:手动清除定时器和dom

深拷贝

  • 深拷贝拷贝的是键和值
    深拷贝会开辟一个新的栈,新对象跟原对象不共享内存,修改新对象不影响原对象
    深拷贝实现方式:
    JSON.parse(JSON.stringify())、手写递归、jquery的$.extend

浅拷贝

拷贝出来的目标对象的指针和源对象的指针指向的是同一块内存空间,
Object.assign(目标对象,源对象)

继承

原型链继承(通过改变原型的指向实现继承)
- 缺点:
1、不能传递参数,
2、如果父类的属性是引用类型,子类实列修改了该属性,其他的子类实列会共享该属性。
借用父级构造函数实现继承(通过call修改this指向)(不会继承prototype)
- 缺点:
1、子类无法继承父类在原型链上的属性和方法。
2,每个实例都拷贝一份,占用内存大,尤其是方法过多的时候 (函数复用又无从谈起了,本来我们用prototype就是解决复用问题的)
- 优点:
1、解决了子类实列修改了父类属性,其他的子类实列会共享该属性的问题
组合继承(原型链继承+借用构造函数继承)
组合继承是js最常用的继承模式,
- 缺点:
1、组合继承最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型时,另一次是在子类构造函数内部。
寄生组合继承(常用)
- 寄生组合继承就是避免两次调用父类构造函数,通过赋值直接继承父类的原型

事件冒泡

事件冒泡:当某个元素的某类型事件被触发时(如 onclick),它父级的同类型事件也会被触发,它的父级的父级同类型事件也会被触发,以此类推,一直触发到根元素。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。

H5新标签

新增的语义化布局标签

  1. header和footer(fu te)标签:页面中一个内容区块的头部和尾部布局;
  2. nav:导航区域;
  3. article(a ti ke)标签:页面中独立的内容部分布局;
  4. aside(e sai de)标签:在独立内容之外,但是又与article有关联的部分布局;
    新增媒体标签
  5. audio(o diu)(音频);
  6. video(v diu)(视频);
    新增canvas和svg绘画元素
  7. canvas表示位图区域;
  8. svg定义矢量图;
    新增表单增强元素
    表单元素 input 的 type 属性扩充:(下面都属于type的类型)
    date(输入日期);
    email(输入邮件);
    url(输入url地址);
    search(呈现搜索常规的文本域);
    range(输入一定范围内的数值);
    month(输入月份);
    color(颜色);
    number(输入数值);
    以及表单元素 input 通过属性进行表单验证:required(必填项)、pattern(验证表单输入)。

浏览器内核

浏览器内核又可以分成两部分:渲染引擎和JS引擎
渲染引擎:负责获取网页的内容并显示
JS引擎:负责解析 Javascript 语言,执行 javascript 语言来实现网页的动态效果

  • 常用内核(内核种类很多) (怎么分析内核)
    常见浏览器内核可以分这四种:Trident(拆呢te(IE内核))、Gecko(带构(火狐))、Blink(be(四声)琳ke(谷歌))、Webkit(web凯te(谷歌))
    1、IE浏览器内核: Trident内核,也是俗称的IE内核;
    2、Chrome浏览器内核: 是Blink内核;
    3、苹果Safari浏览器内核: Webkit内核,
    5、搜狗、QQ浏览器内核: Trident(兼容模式)+Webkit(高速模式);
    6、百度浏览器: IE内核

跨域

为什么有跨域,因为有同源策略
同源策略:同源策略是一种约定,它是浏览器最核心也最基本的安全功能
当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域

异步加载js

defer(script标签中增加defer属性,异步加载):
但要等dom文档全部解析完(dom树生成)才会被执行。
只有IE能用;
async(script标签中增加async属性,异步加载):
加载完就执行;async只能加载外部脚本
不能把js写在script标签里。
.w3c标准,IE9以下不支持
封装一个函数兼容性的异步加载js文件并且可以按需执行该文件里面的函数(按需加载)

px,em和rem的区别

1、px代表像素,呈现的大小和屏幕分辨率有关系,分辨率越高元素尺寸越小,分辨率越低尺寸约大;
2、em是相对于父元素大小的相对尺寸;
3、rem是元素相对于根元素html的相对尺寸;
4、所有浏览器默认的字体大小是16px;

link和@import的区别

1、link属于XHTML标签,而@import完全是CSS提供的一种方式。
2、加载顺序的差别。当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。
3、兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。

性能优化

1.1.1 雪碧图
雪碧图是根据css sprite(si bai te)音译过来的,就是将很多小图标放在一张图片上就称之为雪碧图,可以减少网站http请求数量,不过随着字体图片、svg图片的流行该技术慢慢退出了舞台

1.1.2 Base64
将图片的内容以Base64格式内嵌到HTML中,可以减少http请求数量,但是编码之后的大小比图片大了

1.1.3 使用字体图标来代替图片
1.2 减少重定向
尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验
如果一定要使用重定向的话,如http重定向到https,要使用301永久重定向,而不是302临时重定向,因为如果使用302则每一次访问http都会重定向到https页面,而永久重定向在第一次从http重定向到https之后,每次访问http,会直接返回https的页面

1.3 使用缓存
使用cache-control或expires这类强缓存的时候,缓存不过期的情况下不会向服务器发起请求。强缓存过期的时候,会使用last-modified或etag这类协商缓存向服务器发起请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源,如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200

1.4 不使用css@import
使用css@import会造成额外的请求

1.5 避免使用空的src和href
a标签设置空的href,会重定向到当前页面的地址
form设置空的method,会提交表单到当前页面的地址

2.1 html压缩
html代码压缩就是压缩在文本文件中有意义,但是在html中不显示的字符,包括空格,制表符

2.2 css压缩
css压缩包括无效代码删除与css语义合并

2.3 js压缩与混乱
js压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码的可读性、实现代码的保护

2.4 图片压缩

3.1 使用CDN
CDN是内容分发网络,它能够实时地根据网络流量和各个节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上,其目的是使用户可以就近的取得所需内容,解决网络拥挤的状况,提高网站的响应速度

3.2 使用DNS预解析
当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址,在解析过程中,按照浏览器缓存、系统缓存、路由器换算、DNS缓存、域名服务器的顺序,逐步读取缓存,直到拿到ip地址

3.3 持久连接
使用keep-alive或者persistent来建立持久连接,降低了延时和连接建立的开销

4、优化资源加载
4.1 资源加载位置
通过优化资源加载位置,更改资源加载时机,使尽可能快地展示出页面内容,尽可能快地使用功能可用
1、css文件放在head中,先外链,后本页
2、js文件放在body底部,先外连,后本页
3、处理页面、处理页面布局的js文件放在head中,如babel-polyfill.js文件、flexible.js文件
4、body中尽量不写style标签和script标签

4.2 资源加载时机
1、异步script标签
defer:异步加载,在html解析完成后执行。defer的实际效果与将代码放在body底部类似
async:异步加载,加载完成后立即执行
2、模块按需加载
在SPA等业务比较复杂的系统中,需要根据路由来加载当前页面所需要的业务模块
按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积

webpack提供了两类技术,优先选择的方式是使用符合ECMAScript提案的import语法,第二种就是使用webpack特定的require.ensure

3、使用资源预加载preload和资源预读取prefetch
preload让浏览器提前加载指定资源,需要执行时候再执行,可以加快当前页面的加载速度
prefetch告诉浏览器加载下一个页面可能会用到的资源,可以加速下一个页面的加载速度
4、资源懒加载与资源预加载
资源延迟加载也称为资源懒加载,延迟加载资源或符合某些条件的时候才加载某些资源
资源预加载是提前加载用户所需的资源,保证良好的用户体验
资源懒加载和资源预加载都是一种错峰操作,在浏览器忙碌的时候不能操作,浏览器空闲的时候再加载资源,优化了网络性能

img 中 title和alt区别

图片中的 alt属性是在图片不能显示时出现的文本提示。alt有利于SEO优化
图片中的 title属性是在鼠标在移动到图片上的文本提示。

DOM与BOM分别是什么?

DOM是文档对象模型
它指的是把文档当作一个对象来对待,提供访问和操作网页内容的方法和接口
- 方法:
document.head 获取一个html的head部分
document.body 获取一个html的body部分
document.innerHTML 获取标签
document.innerText 获取文本
document.querySelector 获取元素
document.getElementById 根据id获取元素
onclick 点击事件
addEventListener 点击事件
BOM是浏览器对象模型
它指的是将浏览器当作一个对象来对待,提供与浏览器交互的方法和接口
- 方法
prompt 显示可提示用户输入的对话框
alert 显示带有一个提示信息和一个确定按钮的警示框
confirm 显示一个带有提示信息、确定和取消按钮的对话框
close 关闭浏览器窗口
open 根据给定的url打开一个新的浏览器窗口
setTimeout 在指定的毫秒数后调用函数或计算表达式
setInterval 按照指定的周期(以毫秒计)来调用函数或表达式

数组去重

一、利用ES6 Set去重(ES6中最常用)
二、利用for嵌套for,然后splice去重(ES5中最常用)
三、利用filter配合indexOf去重
四、利用sort()
五、利用includes
六、利用递归去重

区分数组对象

1.通过constructor
2.通过Object.prototype.toString.call()
3.通过instanceof
4.ES5特地新增isArray()检测变量是否是数组

反转字符串

第一种:
字符串转数组,反转数组,数组转字符串。
split(“”):根据空字符串拆分数组
reverse():数组反转元素位置
join(“”):数组转回字符串,且不带分隔符

第二种:
定义新的空字符串,遍历str,通过charAt()是取字符串的最后一个字符,再取倒数第二个…以此类推。都放到新的字符串前面。这样就是倒序的了

事件委托

事件委托,又称事件代理,不是直接给标签添加事件 是给标签的父级添加事件 通过事件对象判断触发事件的标签是谁 执行不同的事件处理函数

原型对象和对象的原型

原型对象prototype 是创建函数 js引擎自动创建的对象
对象的原型__proto__ 是实例化对象的原型 会自动继承原型对象中的属性

形参和实参是什么?

实参可以是常量、变量、表达式、函数等,在进行函数调用时,它们都必须有确定的值。
通常将函数处理的数据,影响函数功能的因素或者函数处理的结果作为形参。
实参是用来填充形参的
funcation a(b){
var x=b;
}
a(‘zs’)
b是形参,’zs’为实参

arguments是什么?

arguments是一个对应于传递给函数的参数的类数组对象。
类数组:是数组的形式,有length,但不具有数组的一切方法。
作用:
可以用arguments 对象判断传递给函数的参数个数并获取参数

解决跨域的方法

第一种方法
在后端服务器设置 请求头 res.setHeader(“Access-Control-Allow-Origin”,”*”)
第二种方法
允许指定源访问

1
2
3
4
5
    let arr=["浏览器请求路径"]
if(arr.includes(req.headers.origin)){
res.setHeader("Access-Control-Allow-Origin",req.headers.origin)
}
注意:谷歌浏览器不允许本地file文件load 右键谷歌浏览器--设置--目标:加上"--allow-file-access-from-files"注意前面有空格

第三种方法
后端中下载插件 npm i cors
然后引入
1
2
const cors=require("cors")
app.use(cors())

第四种方法
在前端通过script标签解决跨域问题
Script src link 都不受同源策略的影响
可以直接
缺点:
1、不能接收JSON数据 只能接收js代码 前端通过变量或者是函数名来接收
2、无法携带拼接的参数 只能携带固定字符串 不能携带拼接在?后面的内容
3、script标签是同步操作 第一个script执行完才能执行第二个script
第五种方法
通过jsonp动态创建script
jsonp的本质
前端发起请求 发起请求前声明一个函数 function fn(data){console.log(data)}
后端返回一个函数调用 res.send(fn(333))后端返回的函数名和前端声明的函数名要一致
前端的函数中的参数也就是data接受的就是后端返回的数据 333
动态scipt标签是可以传递拼接在?后边的参数的

    动态创建script标签的三个步骤
    创建一个空标签 let s=docoument.createElement("script")
    给标签添加属性 s.src="http://localhost:3000/getData"
    将创建好的标签追加到尾部 document.documentElement.appendChild(s)

第六种方法
nginx(代理)
总的结论:
正向代理隐藏用户
反向代理隐藏服务器
正向代理:
1.用户发送请求到自己的代理服务器
2.自己的代理服务器发送请求到服务器
3.服务器将数据返回到自己的代理服务器
4.自己的代理服务器再将数据返回给用户
反向代理:
1.用户发送请求到反向代理服务器(访问的其实是反向代理服务器,但用户不知道)
2.反向代理服务器发送请求到真正的服务器
3.真正的服务器将数据返回给反向代理服务器
4.反向代理服务器将数据返回给用户

面向对象

  • 在js中对象是一个无序的数据集合或者也可以说是属性和方法的集合,可以动态的添加属性和方法
  • 面向对象是一种软件开发的思想和面向过程是相对应的,就是把程序看做一个对象,将属性和方法封装其中,以提高代码的灵活性、复用性、可扩展性
  • 面向过程是按需求一步一步的用代码从上往下实现,这样做代码不易维护、复用、扩展
  • 所以大型项目中我们需要以面向对象的方式去开发这样就体现了用面向对象的方法写出来的代码易维护、易复用、易扩展。
面向对象的特征:封装、继承、多态、抽象。
  • 封装
    我对封装的理解就是把属性和方法封装其中,将不需要对外公开的内容隐藏起来提供接口让用户访问属性和方法。
  • 继承
    继承就好比我继承了我爸部分的相貌特征但我和我爸又不完全长一个样子,而且我自己没有钱但我爸有钱,我爸的钱可以给我花。
    就是指子类构造函数继承父类构造函数的一些属性和方法,但其本身也有一些自己的方法和属性
  • 多态
    多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果
    比如一个我养了条狗和一个猫,我对它们发出“叫”的指令时它们一个是“汪汪汪”的叫一个是“喵喵喵”的叫,我给的指令是一样的它们发出来的声音却不一样。
  • 抽象
    先不去考虑细节的东西,从大的方向开始。比如学生就是一个抽象实体,他的属性并不足以描述出一个人,需要更多的细节才能描述一个人的方方面面。使用抽象可以尽可能避免过早考虑一些细节

原型 原型链

原型:
所有的函数默认都有一个“prototype”这样公有且不可枚举的属性,它会指向另一个对象,这个对象就是原型。
原型链:
当访问对象的属性或者方法时,首先对象从自身去找,找不到就会往原型中去找,也就是他构造函数的prototype中,如果原型中找不到,即构造函数中也没有该属性,就会往原型后面的原型上去找,这样就形成了链式的结构,称为原型链

this指向的情况有哪些

事件绑定中的 this
普通函数执行中的 this
箭头函数执行中的 this
构造函数中的 this
基于 call/apply/bind 强制改变中的 this

Null和undefined的区别?

1、null是JavaScript的关键字,而undefined是JavaScript的一个全局变量,也就是挂载在window对象上的一个变量,并不是关键字。
2、在使用typeof运算符进行检测时,Undefined类型的值会返回undefined.而Null类型的值返回为object
3、在需要进行字符串类型的转换时,null会转换成字符串null,而undefined会转换字符串undefined.
4、在进行数值类型的转换时,undefined会转换为NaN,无法参与计算,而null会转换为0,可以参与计算。
undefined +0;// NaN
null+0 ;// 0
5、建议:无论在什么情况下都没有必要将一个变量显示的赋值为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null.

Call、apply、bind的区别?

call是一个方法,是函数的方法
call可以调用函数,call可以改变函数中this的指向

call和apply
相同点:都会调用函数
不同点:传参的方式不同,call传参时一直往后加参数,apply传参数是以数组的形式传参

call和bind
相同点:传参的方式一样
不同点:call会调用函数,bind不会调用函数,它会作为一个返回值返回一个函数,然后才可以调用

DOCtype(document type)的作用

  • DOCTYPE是document type (文档类型) 的缩写。
  • 用于告诉浏览器该以什么文档标准去解析这个文档
  • 主要作用是告诉浏览器的解析器使用哪种HTML规范或者XHTML规范来解析页面。

JS语言主要分哪几部分 三部分

  • ECMAscript 提供核心语言功能
  • Dom 文档对象模型,提供访问和操作网页内容的方法和接口
  • Bom 浏览器对象模型,提供与浏览器交互的方法和接口

原型对象、实例对象、构造函数三者之间的关系

  实例是由new构造函数生成;
  原型是构造函数的prototype,构造函数原型上的constructor属性,是构造函数本身;
实例的_proto_属性,指向构造函数原型

判断对象自身是否包含此属性

  • console.log(boj1.hasOwnProperty(‘age’))

认识函数

  • 函数内部定义函数:闭包
  • 函数内部调用其他函数:函数调用
  • 函数内部调用参数传过来的函数:回调函数
  • 函数内部调用自己这个函数:递归

JavaScript数组常用方法有哪些?

1、push:在数组末尾添加一个或者多个元素 返回新数组的长度
2、pop:移除并返回数组末尾的元素
3、unShift:在数组头部添加一个或多个元素 返回新数组的长度
4、shift:移除并返回数组头部的元素
5、concat:合并两个或者多个数组 并返回合并后的新数组 该方法不会影响原数组
6、slice:从数组的指定位置截取元素,返回一个新数组,不会影响原始数组
7、splice:从数组指定位置删除或替换元素,可修改原始数组
8、indexOf:查找指定元素在数组的索引位置,如果没找到返回-1
9、lastIndexOf:从数组尾部查找指定元素的索引位置,如果没找到返回-1
10、includes:查找数组中是否有指定元素,返回布尔值
11、json:数组转字符串,并用指定分隔符连接它们
12、reverse:反转数组,影响原始数组
13、sort:数组排序,默认根据字母顺序排列,会修改原始数组
14、forEach:对数组每一个元素执行提供的函数
15、filter:遍历数组 返回所有符合条件的元素
16、map:创建一个新数组,其中包含对数组中的每一个元素操作后的结果
17、reduce:将数组中的元素进行累积操作,返回一个单一值
18、some:遍历数组 判断数组中是否有符合条件的元素 返回布尔值
19、every:遍历数组,判断数组中元素是否都符合条件 返回布尔值
20、find:遍历数组,返回第一个符合条件的元素本身

导致JavaScript中this指向混乱的原因是什么

在js中this的指向是动态的 也就是this指向会根据上下文的环境变化而发生变化 导致他的指向变得混乱或难以预测。常用的导致this指向混乱的原因包括一下几个方面:

1、函数调用方式不同:
  • 当一个函数被调用时,它的this指向取决于调用方式,如果使用普通函数调用方式(如fn()),则this会指向全局对象window,如果是方法调用(如obj.fn())则this指向调用该方法的对象
2、箭头函数的使用:
  • 箭头函数不具有自己的this值,他会捕获上下文中的this值,因此,如果在箭头函数中访问this,它会指向外层作用域中的this值
3、使用apply、call、bind方法
  • apply、call、bind方法都可以改变this指向,其中apply、call方法可以立即执行函数并传入参数,而bind方法可以返回一个新函数,该函数的this值被绑定到指定对象上
4、对象的嵌套和继承:
  • 当一个对象被嵌套在另一个对象中或者使用继承时,this的指向可能变得混乱。这是因为this的指向取决于函数被调用时的上下文环境,而不是对象本身,因此,在嵌套对象或继承类中使用this时需要特别注意他的指向

怎么实现虚拟列表

  • 虚拟列表是一种优化长列表渲染性能的技术,他只渲染可视化区域内的内容,从而降低了页面渲染的复杂度
  • 具体而言,实现虚拟列表需要以下几个步骤
    1、计算可视化区域:首先计算出可见区域内的列表数量和位置
    2、渲染可见区域:只渲染可见区域内的内容,而不是整个列表
    3、动态调整列表高度:由于只渲染了部分列表项,因此需要动态调整列表容器的高度,确保滚动条可以正确显示并且用户可以滚动整个列表
    4、延迟加载非可见区域:当用户滚动列表的时,需要根据当前滚动条0位置动态加载,非可见区域的列表项,以便在用户滚动到该区域是能够及时显示.

说说对轮询的理解

什么是轮询
  • 轮询是指在一定时间内,定时向服务器发送请求,获取最新数据的过程
  • 轮询通常用于从服务器获取实时更新的数据
轮询和长轮询有什么区别
  • 轮询是在固定的时间间隔内向服务器发送请求,既是服务器没有数据更新,也会继续发送请求,而长轮询则是发送一个请求,服务器如果没有数据更新,则不会返回,而是一直挂着,直到有数据更新再返回结果
前端轮询的实现方法有什么

有两种:基于定时器的轮询和基于递归的轮询。基于定时器的轮询使用setInterval方法来定时发送请求,基于递归的轮询则使用setTimeout方法来控制下一次请求的时间

轮询有什么缺点
  • 轮询会产生大量的无效请求,浪费宽带和服务器资源,并且对服务器压力比较大,同时在短时间内频繁对服务器发起请求,可能会被服务器视为恶意行为,导致IP被封禁等问题
如何避免轮询的缺点
  • 可以使用webSocket和SSE等技术来实现实时数据更新
    WebSocket是一种双向通信协议,能够实现服务器与客户端之间的实时通信。
    而SSE是一种基于HTTP的单向通信协议,可以实现服务器向客户端推送实时数据。
  • 这些技术都能够减少无效请求,提高数据传输效率,并且对服务器资源的消耗也比较小

作用域和作用域链

作用域

  • 作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合

  • 换句话说:作用域决定了代码区块中变量和其他资源的可见性

  • 我们一般将作用域分为:
    全局作用域
    局部作用域
    块级作用域

全局作用域
  • 不在任何函数中和大括号中的变量 我们都视为全局作用域,全局作用域下的变量可以在任意位置访问到
函数作用域

函数作用域也称局部作用域 如果一个变量在函数内部声明,那么这个变量只能在这个函数体内部才能被访问到,函数外是访问不到的

块级作用域

ES6中引入let和const关键字,和var关键字不同的是,在大括号中使用let和const声明的变量存在于块级作用域,在大括号外是访问不到的

词法作用域

  • 又叫静态作用域,变量被创建时就确定好了,JavaScript就是遵循的词法作用域
  • 相同层级的两个函数没有办法访问打彼此作用域中的变量

作用域链

  • 当js中使用一个变量时,首先js引擎会在当前作用域下去查找,如果没找到,则会去它的上层作用域查找,依次类推直到找到该变量或者找到了全局作用域
  • 如果在全局作用域找不到,在非严格模式下回隐式声明该变量,严格模式直接报错

ES6有哪些新特性

1、let、const 块级作用域以及和 var 的区别

声明方式 变量提升 作用域 初始值 重复定义
var 是 函数级 不需要 允许
let 否 块级 不需要 不允许
const 否 块级 必需 不允许

2、解构-快速提取数组/对象中的元素

数组解构
  • 单独解构-根据数组索引,将数组解构成单独的元素

    1
    2
    3
    4
    5
    const arr = [1, 2, 3]
    const [a, b, c] = arr
    console.log(a, b, c) //1,2,3
    const [, , d] = arr
    console.log(d) //3
  • 默认值,解构时可以给变量设置默认值,数组没有这个元素的话

    1
    2
    3
    const arr = [1, 2, 3]
    const [, , , defaultVal = '4'] = arr
    console.log('设置默认值', defaultVal)
  • 剩余解构用 “…+变量名” 解构剩余参数到新数组,只能用一次

    1
    2
    3
    const arr = [1, 2, 3]
    const [e, ...rest] = arr
    console.log(rest) //[2, 3]
对象解构
  • 单个/多个解构-跟数组解构差不多

    1
    2
    3
    const obj = { name: 'zzm', age: 18, height: undefined }
    const { name, age } = obj
    console.log(name, age) // 'zzm', 18
  • 解构+重命名-给解构出来的变量重命名

    1
    2
    3
    const obj = { name: 'zzm', age: 18, height: undefined }
    const { name: objName } = obj
    console.log(objName)
  • 默认值-给解构变量设置默认值

    1
    2
    3
    const obj = { name: 'zzm', age: 18, height: undefined }
    const { next = 'default' } = obj
    console.log(next)

3、模板字符串

用法:使用``将字符串包裹起来
功能:可以换行、插值、使用标签函数进行字符串操作

1
2
3
4
5
6
7
//换行
const str = `fdsjak
fdsa`
console.log(str)
// 插值
const strs = `random: ${Math.random()}`
console.log(strs)

4、字符串扩展方法

  • includes-是否包含
  • startsWith-是否以什么开始
  • endsWith-是否以什么结束

5、参数默认值&剩余参数

  • 给函数形参设置默认值
    1
    2
    3
    4
    5
    // 带默认参数的形参一般放在后面,减少传参导致的错误几率
    const defaultParams = function (name, age = 0) {
    return [age, name]
    }
    console.log(defaultParams(1))
  • 使用…rest 形式设置剩余形参,支持无限参数
    1
    2
    3
    4
    5
    // 剩余参数,转化成数组
    const restParams = function (...args) {
    console.log(args.toString()) //1, 2, 3, 4, 5
    }
    restParams(1, 2, 3, 4, 5)

6、展开数组

  • 使用…将数组展开
    1
    2
    3
    4
    const arr = [1, 2, 3]
    console.log(...arr)
    // 等价于es5中以下写法
    console.log.apply(console, arr)

7、箭头函数

特性&优势:
  • 1、简化了函数的写法
  • 2、没有 this 机制,this 继承自上一个函数的上下文,如果上一层没有函数,则指向 window
  • 3、作为异步回调函数时,可解决 this 指向问题

8、对象字面量增强

  • 同名属性可以省略 key:value 形式,直接 key,
  • 函数可以省略 key:value 形式
  • 可以直接 func(),
  • 可以使用计算属性,比如:{[Math.random()]: value}

9、Object.assign(target1, target2, targetN)-复制/合并对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Object.assign(target1, target2, ...targetn)
* 后面的属性向前面的属性合并
* 如果target1是空对象,可以创建一个全新对象,而不是对象引用
*/
const obj1 = {
a: 1,
b: 2,
}
const obj2 = {
a: 1,
b: 2,
}

const obj3 = Object.assign({}, obj1)
obj3.a = 5
console.log(obj3, obj2, obj1)

10、Object.is(value1, value2)

  • 作用:比较两个值是否相等
  • 特性:
    没有隐式转换
    可以比较+0,-0、NaN
    1
    2
    3
    4
    5
    console.log(NaN === NaN) //false
    console.log(Object.is(NaN, NaN)) //true
    console.log(0 === -0) // true
    console.log(Object.is(0, -0)) //false
    console.log(Object.is(1, 1)) //true

11、Proxy(object, handler)

  • 作用:
    代理一个对象的所有,包括读写操作和各种操作的监听
  • 用法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const P = {
    n: 'p',
    a: 19,
    }
    const proxy = new Proxy(P, {
    get(target, property) {
    console.log(target, property)
    return property in target ? target[property] : null
    },
    defineProperty(target, property, attrs) {
    console.log(target, property, attrs)
    // throw new Error('不允许修改')
    },
    deleteProperty(target, property) {
    console.log(target, property)
    delete target[property]
    },
    set(target, property, value) {
    target[property] = value
    },
    })

    proxy.c = 100
    console.log('pp', P)
  • 与 Object.defineProperty 对比
  • 优势:
    拥有很多 defineProperty 没有的属性方法
    对数组的监视更方便
    以非侵入的访视监管对象的读写

12.Reflect

  • 作用:
    用于对对象的统一操作,集成 Object 相关的所有方法

13.Promise

  • 作用:解决异步编程中回调嵌套过深问题

14.class&静态方法&继承

定义
  • 使用 class 关键字定义类
    1
    2
    3
    4
    5
    class Person {
    constructor(props) {
    this.props = props
    }
    }
方法
  • 实例方法,需要实例化之后才能调用,this 指向实例
  • 静态方法,用 static 修饰符修饰,可以直接通过类名调用,不需要实例化,this 不指向实例,而是指向当前类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Person {
    constructor(props) {
    this.props = props
    }
    // 实例方法
    eat() {}
    // 静态方法
    static run() {}
    }
    // 调用静态方法
    Person.run()
    const person = new Person('props')
    // 调用实例方法
    person.eat()
继承:子类使用 extends 关键字实现继承,可以继承父类所有属性
1
2
3
4
5
6
7
8
9
10
class Student extends Person {
constructor(props) {
super(props)
}
printProps() {
console.log(this.props)
}
}
const student = new Student('student')
student.printProps()

15.Set

说明:
  • Set 是一种类似于数组的数据结构
特性:
  • 元素唯一性,不允许重复元素
  • 使用 add 增加重复元素,将会被忽略
用途:
  • 数组去重
  • 数据存储
    1
    2
    3
    4
    5
    6
    const arr = [1, 3, 1, 1, 1]
    const set = new Set(arr)
    set.add(1).add(1)
    console.log(set.size) //2
    const newArr = Array.from(set)
    console.log(newArr) //[ 1, 3 ]

16.Map

说明:
  • 类似 Object,以 key、value 形式存储数据
区别:
  • Map 键不会隐式转换成字符串,而是保持原有类型
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
const map = new Map()
map.set(1, 1)
map.set('name', 'map')
map.set(obj, obj)
console.log(map.get(1)) //1
/**
1 1
name map
{ '1': 1, true: true, a: 'a' } { '1': 1, true: true, a: 'a' }
*/
map.forEach((val, key) => {
console.log(key, val)
})

17.Symbol

说明:
  • JavaScript 第六种原始数据类型,用来定义一个唯一的变量
作用:
  • 创建唯一的变量,解决对象键名重复问题

  • 为对象、类、函数等创建私有属性

  • 修改对象的 toString 标签

  • 为对象添加迭代器属性

  • 如何获取对象的 symbol 属性?
    Object.getOwnPropertySymbols(object)

  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 对象属性重名问题;
    const objSymbol = {
    [Symbol()]: 1,
    [Symbol()]: 2,
    }
    console.log(objSymbol)
    // 2、为对象、类、函数等创建私有属性
    const name = Symbol()
    const obj2 = {
    [name]: 'symbol',
    testPrivate() {
    console.log(this[name])
    },
    }
    obj2.testPrivate()
    // 定义toString标签;
    console.log(obj2.toString())
    obj2[Symbol.toStringTag] = 'xx'
    console.log(obj2.toString()) //[object xx]

18.for…of…

用途:
  • 已统一的方式,遍历所有引用数据类型
特性:
  • 可以随时使用 break 终止遍历,而 forEach 不行
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 基本用法
// 遍历数组
const arr = [1, 2, 3, 4]
for (const item of arr) {
if (item > 3) {
break
}
if (item > 2) {
console.log(item)
}
}
// 遍历set
const set = new Set()
set.add('foo').add('bar')
for (const item of set) {
console.log('set for of', item)
}
// 遍历map
const map = new Map()
map.set('foo', 'one').set('bar', 'two')
for (const [key, val] of map) {
console.log('for of map', key, val)
}
//迭代对象
const obj = {
name: 'xiaohui',
age: '10',
store: [1, 2, 3],
// 实现可迭代的接口
[Symbol.iterator]: function () {
const params = [this.name, this.age, this.store]
let index = 0
return {
next() {
const ret = {
value: params[index],
done: index >= params.length,
}
index++
return ret
},
}
},
}
for (const item of obj) {
console.log('obj for of', item)
}

19. 迭代器模式

作用:
  • 通过 Symbol.interator 对外提供统一的接口,获取内部的数据
外部可以通过 for…of…去迭代内部的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const tods = {
life: ['eat', 'sleep'],
learn: ['js', 'dart'],
// 增加的任务
work: ['sale', 'customer'],
[Symbol.iterator]: function () {
const all = []
Object.keys(this).forEach((key) => {
all.push(...this[key])
})
let index = 0
return {
next() {
const ret = {
value: all[index],
done: index >= all.length,
}
index++
return ret
},
}
},
}
for (const item of tods) {
console.log(item)
}

20、Generator函数

  • 函数前添加 *,生成一个生成器
  • 一般配合 yield 关键字使用
  • 最大特点,惰性执行,调 next 才会往下执行
  • 主要用来解决异步回调过深的问题
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function* gen()
    {
    yield 1;
    console.log(yield 2); // undefined
    yield 3;
    return 4;
    }
    let ge=gen() //返回一个遍历器对象
    console.log(ge.next()) // {value: 1, done: false}
    console.log(ge.next()) // {value: 2, done: false}
    console.log(ge.next()) // {value: 3, done: false}
    console.log(ge.next()) // {value: 4, done: true}
    console.log(ge.next()) // {value: undefined , done: true}

21.includes 函数-es2016

  • 判断数组是否包含某个元素,包含 NaN,解决 indexOf 无法查找 NaN 问题
    1
    2
    3
    4
    //  includes函数
    const arr = ['foo', 'bar', 'baz', NaN]
    console.log(arr.includes(NaN)) //true
    console.log(arr.indexOf(NaN)) //-1

22. 运算符-es2016

  • 指数运算
    1
    2
    3
    4
    5
    // 指数运算符 **
    // es5中2十次方
    console.log(Math.pow(2, 10))
    // es6中2十次方
    console.log(2 ** 10)

23.values 函数-es2017

  • 将对象的值以数组的形式返回
    1
    2
    3
    4
    5
    6
    const obj = {
    foo: 1,
    bar: 2,
    baz: 3,
    }
    console.log(Object.values(obj)) //[ 1, 2, 3 ]

24.entries 函数-es2017

  • 将对象以键值对二维数组返回,使之可以使用 for…of…遍历
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const obj = {
    foo: 1,
    bar: 2,
    baz: 3,
    }
    console.log(Object.entries(obj))
    const entry = Object.entries(obj)
    for (const [key, value] of entry) {
    console.log(key, value)
    }

25.Object.getOwnPropertyDescriptors(obj)-es2017

  • 获取对象的描述信息
  • 可以通过获得的描述信息,配合 Object.defineProperties 来完整复制对象,包含 get,set 方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // getOwnPropertyDescriptors
    // 普通get方法
    const objGet = {
    foo: 1,
    bar: 2,
    get getCount() {
    return this.foo + this.bar
    },
    }
    // assign方法会把getCount当做普通属性复制,从而getCount为3,修改bar不管用
    const objGet1 = Object.assign({}, objGet)
    objGet1.bar = 3
    console.log(objGet1.getCount) //3
    // descriptors
    const descriptors = Object.getOwnPropertyDescriptors(objGet)
    console.log('des', descriptors)
    // 通过descriptors来复制对象,可以完整复制对象,包含get,set
    const objGet2 = Object.defineProperties({}, descriptors)
    objGet2.bar = 3
    console.log(objGet2.getCount) //4

26.padStart, padEnd 函数-es2017

  • 在字符串前,或者后面追加指定字符串
参数:
  • targetLenght: 填充后的目标长度
  • padString:填充的字符串
规则:

1、填充的字符串超过目标长度,会在规定长度时被截断
2、填充字符串太短会以空格填充
3、padString 未传值,以空格填充

作用:
  • 一般用来对齐字符串输出
    1
    2
    3
    4
    5
    6
    /**
    foo.................|1
    barbar..............|2
    bazbazbaz...........|3
    */
    console.log(`${key.padEnd(20, '.')}${value.toString().padStart(2, '|')}`)

函数式编程

是什么

主要编程范式有三种:命令式编程、声明式编程、函数式编程

  • 简单来说,函数式编程就是把过程逻辑写成函数,定义好输入参数,只关心他的输出结果

概念

  • 纯函数 就是一个函数的返回结果只依赖于它的参数,并且在执行过程中没有副作用,我们就把这个函数叫做纯函数。
  • 高阶函数 就是以函数作为输入或者输出的函数被称为高阶函数
  • 柯里化函数 柯里化是把一个多参数函数转化成一个嵌套的一元函数的过程
  • 组合与管道 组合函数,目的是将多个函数组合成一个函数

优缺点

优点
  • 更好的管理状态:因为他的宗旨是无状态,或者说更少的状态,能最大化的减少这些未知、优化代码、减少出错情况
  • 更简单的复用:固定输入->固定输出,没有其他外部变量影响,并且无副作用,这样的代码复用时,完全不需要考虑他的内部实现和外部影响
  • 更优雅的组合:往大了说,网页是由各个组件组成的。往小了说,一个函数也可能由多个小函数组成的。更强的复用性,带来更强大的组合
  • 隐性好处。减少代码量,提高维护性
缺点
  • 性能:函数式编程相对于指令式编程,性能绝对是一个短板,因为他往往对一个方法过度包装,从而产生上下文切换的性能开销
  • 资源占用:在js中为了实现对象状态不可变,往往会创建新的对象,因此,他对垃圾回收所产生的压力远远超过其他编程方式
  • 递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作

cookie、localStorage和sessionStorage 三者之间有什么区别

生命周期

  • cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
  • localStorage:除非手动删除,否则将会永久保存
  • sessionStorage:仅在当前网页会话下有效,关闭页面或浏览器后就会被清除

存放数据大小

  • cookie:4KB左右
  • localStorage和sessionStorage:可以保存5MB的信息

易用性

  • cookie:需要程序员自己封装,原生的cookieAPI不友好
  • localStorage和sessionStorage:原生的API可以接受,亦可再次封装来对object和array有更好的支持

浏览器有哪几种缓存,各种缓存的优先级是什么样的?

强制缓存:

1、浏览器发送HTTP请求
2、服务器返回HTTP响应(响应头中的缓存标识:cache-Control(http1.1)、Expires(http1.0))
- Cache-Control:max-age=300,响应内容浏览器本地缓存300秒,缓存时间内再次加载资源,就会命中强缓存
- Expires:Wed,26 Jul 2023 15:50:40 GMT,失效具体时间(2023年7月26日星期三15:50:40 GMT),缓存时间内再次加载资源,就会命中强缓存
- 同时设置Cache-Control和Expires,Cache-Control优先级更高
3、浏览器再次发送相同HTTP请求
4、不会再进过服务器,直接从浏览器本地缓存拿出进行返回

协商缓存:

协商缓存步骤如下:(缓存标识对:Etag/If-None-Match(http1.1)、Last-Modified/If-Modified-Since(http1.0))
1、浏览器发起HTTP请求
2、服务器返回HTTP响应(响应头中的缓存标识:Etag、Last-Modified)
- Etag:”64d452af-70b8”,内容的hash值,只有内容改变了,hash才会变化
- Last-Modified:Thu, 10 Aug 2023 02:59:59 GMT,修改时间,哪怕内容没有变化,重新保存可能都会导致修改时间变化
- 同时设置Etag和Last-Modified,Etag优先级更高
3、浏览器再次发送相同的HTTP请求(请求头中的缓存标识If-None-Match(http1.1)、If-Modified-Since(http1.0))
4、服务器会检查Etag === If-None-Match或者Last-Modified === If-Modified-Since是否相等,相等就命中缓存,服务端返回304,从本地缓存中取即可,否则未命中缓存,返回最新的数据和响应头中的缓存标识

Service Worker 缓存:

Service Worker 是一种特殊的 JS 脚本,可以拦截网络请求并返回缓存的响应,以实现离线访问和更快的加载速度等功能。

Web Storage 缓存:

包括 localStorage 和 sessionStorage。localStorage 用于存储用户在网站上的永久性数据,而 sessionStorage 则用于存储用户会话过程中的临时数据。

这些缓存的优先级如下:

Service Worker 缓存:由于其可以完全控制网络请求,因此具有最高的优先级,即使是强制缓存也可以被它所覆盖。
强制缓存:如果存在强制缓存,并且缓存没有过期,则直接使用缓存,不需要向服务器发送请求。
协商缓存:如果强制缓存未命中,但协商缓存可用,则会向服务器发送条件请求,询问资源是否更新。如果服务器返回 304 Not Modified 响应,则直接使用缓存。
Web Storage 缓存:Web Storage 缓存的优先级最低,只有在网络不可用或者其他缓存都未命中时才会生效。

项目首屏提速

  • 大致可以分两种

用户感知提速

  • 因为用户 在很多情况下对于速度的感知是非常主观的,所以说呢,我们可以通过一些加载动画来拖慢用户的这种感知

技术加载提速

  • 我们可以在服务端通过prerender进行一个预渲染,然后以SSR的形式,完成首页的一个服务端渲染,然后把我们后续的渲染交给CSR客户端去进行渲染,这样的话我们就组成了一种同构渲染的方式,来完成一个渲染的提速

  • 还有一些静态资源的渲染,比如说图片的加载,比如说数据的加载,我们可以借助intersectionObserver来完成一些懒加载的处理

css元素隐藏

  • 1、display:none 渲染树不会渲染对象
  • 2、visibility(v 死 biu 了 踢):hidden 元素在页面内仍占据空间,但是不会响应绑定的监听事件
  • 3、opacity(哦 怕 死 踢):0 元素在页面中仍占据空间 并且能够响应元素绑定的监听事件
  • 4、position:absolute 通过使用绝对定位将元素移除到可视化区域外,来隐藏元素
  • 5、z-index:-9999 使其他元素覆盖给元素
  • 6、transform:scale(si 给 o)(0,0) 将元素缩放为0 元素仍在页面中占据位置 但是不会响应绑定的监听事件

css元素居中

  • flex布局设置居中
    给容器设置:
    display: flex;写在父元素上这就是定义了一个伸缩容器
    justify-content 主轴对齐方式,默认是横轴
    align-items 纵轴对齐方式,默认是纵轴

  • 绝对定位设置居中
    使用绝对定位的方式实现水平垂直居中。
    容器设置position: relative。
    子元素设置
    position:absolute;
    left:50%;
    top:50%;
    transform:translate(-50%,-50%);

  • 绝对定位设置居中
    使用绝对定位的方式实现水平垂直居中。
    容器设置position: relative。
    子元素设置
    position:absolute;
    width:300px;
    height:300px;
    margin-left:-150px // 盒子宽的一半
    margin-top:-150px // 盒子高的一半

  • 还有一种奇葩的方法
    这个奇葩方式和使用绝对定位相似,
    容器设置position: relative。
    子元素设置
    position: absolute;
    设置固定宽度和高度
    top、left、bottom、right都设置为0;
    margin设置为auto;
    也能实现垂直水平居中。

em和rem的区别

em
  • 是一个相对单位,相对于当前父级文本的font-size,如果当前文字的字体尺寸没有设置,则相对于浏览器的默认字体尺寸,即1em=16px
    特点:
    1、em的值并不是固定的
    2、em会继承父元素的字体大小
rem
  • 是相对单位,是相对HTML根元素,比如HTML标签设置font-size:100px;那么1rem就相当于100px
    特点:
    1、rem为元素设定字体大小的时候,是相对于根元素进行计算的
    2、当我改变根元素下的字体大小时,下面的大小都会改变
    3、通过rem既可以做的只修改根元素就可以成比例的调整所以字体,又可以避免字体大小逐层复合的连锁反应

HTTP1.0和HTTP2.0的区别

1、连接复用
  • HTTP1.0:每个HTTP请求都需要建立一个新的TCP连接,请求结束后立即关闭连接,这样的方式会导致每个请求都需要重新建立连接,增加了延迟和开销
  • HTTP2.0:引入了多路复用技术,允许在同一个TCP连接上发送多个请求和响应,避免了建立和关闭多个连接的开销,提高了性能和效率
2、请求-响应方式
  • HTTP1.0:采用的是单向请求-响应模式,即每个请求只能对应一个响应,请求和响应是一一对应的
  • HTTP2.0:引入了Server Push机制,服务器可以在客户端请求之前主动推送相关资源,避免了客户端重复请求的等待事件,提高了页面加载速度
3、头部压缩
  • HTTP1.0:每个请求和响应的头部都包含大量的重复信息,造成了较大的网络传输开销
  • HTTP2.0:使用HPACK算法对头部进行压缩,减少了头部的大小,降低了网络传输开销
4、二进制协议
  • HTTP1.0:采用文本形式进行数据传输,易于阅读和调试,但是传输效率较低
  • HTTP2.0:采用二进制格式传输数据,减少了解析的复杂性,提高了传输效率
5、流控制和优先级
  • HTTP1.0:没有流控制和优先级的概念,所有请求头都是按照发生的顺序进行处理
  • HTTP2.0:引入了流控制和优先级的机制,可以根据需求对请求进行优先级排序和流量控制,确保重要请求的及时处理
6、长连接支持
  • HTTP1.0:基本都是短连接,每个请求响应完成之后立即关闭连接
  • HTTP2.0:支持长连接,即一个TCP连接可以承载多个请求和响应,减少连接的建立和关闭次数,提高了性能

(DFS)深度优先搜索算法

  • 其过程为沿着每一个可能的路径向下进行搜索,直到不能再深入为止,并且每一个节点只能访问一次。

BFS(宽度优先算法)

  • 它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。简单来说,bfs好像是一个耳听六路眼观八方的人

冒泡排序

  • 原理:
    相邻的数据进行两两比较,小数放在前面,大数放在后面,这样一趟下来,最小的数就被排在了第一位,第二趟也是如此,如此类推,直到所有的数据排序完成。

快速排序

  • 原理:
    选择数组中的一个值作为基准,将数组中小于该值的数置于该数之前,大于该值的数置于该数之后,接着对该数前后的两个数组进行重复操作直至排序完成。

JavaScript中常见的数据结构

Queue 队列
  • 队列是一个先进先出的数据结构,一般JavaScript中采用队列解决问题时会用到
    入队push ():在数组的尾部添加元素
    出队shift ():移除数组中第一个元素
    queue (0) :取数组的第一个元素
    isEmpty ():确定队列是否为空
    size ():获取队列中元素的数量
Stack 栈
  • 栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进⬇后出⬆的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

JavaScript中没有栈,但是可以用Array实现栈的功能。
栈中数组长度减一即为栈尾元素,也就是最后进入的那个元素,最先出去的那个元素
JavaScript中对栈的操作一般会使用到
push()方法,将元素压入栈顶
pop()方法,从栈顶弹出(删除)元素,并返回该元素
peek()方法,返回栈顶元素,不删除
clear()方法,清空栈
length拿到栈中元素数量

Linked List 链表
  • 链表是由多个元素组成的列表,链表中的元素储存不连续,用next指针连接在一起。
  • 数组:增删非数组元素需要移动元素
  • 链表:增删非首尾元素不需要移动元素只需要更改next的指向即可
  • 链表是一个链式数据结构,每个节点由两个信息组成:节点的数据和指向下一个节点的指针。链表和传统数组都是线性数据结构,具有序列化的存储方式。
  • JavaScript中没有链表,但是可以用object来模拟链表
集合
  • 集合:一种无序且唯一的数据结构,集合区别队列、栈、链表最大的区别就是元素不能重复
  • JavaScript中ES6中新增了集合这种数据结构,可以通过实例化Set对象来创建集合const set = new Set()

前端🎄树结构还是比较常见的,例如级联选择、层级目录等都是树形结构。
javascript中没有树这个数据结构,但是一般用object和array来模拟树。

  • 树的常用遍历方式
    1、深度优先遍历
    2、广度优先遍历
二叉树
  • 叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成

  • 树的每个节点最多只能有两个子节点

  • js中自然也没有二叉树这个数据结构,一般还是用object对象来模拟二叉树

  • 二叉树遍历(递归)
    (1)前序遍历
    DLR:根节点——左子树——右子树
    每次遍历到一个节点都重复一次前序遍历
    (2)中序遍历
    LDR:左子树——根节点——右子树
    每次遍历到一个节点都重复一次中序遍历
    (3)后序遍历
    LRD:左子树——右子树——根节点
    每次遍历到一个节点都重复一次后序遍历
    注意:
    前序遍历第一个为根节点
    中序遍历根节点左边为左子树,右边为右子树
    后序遍历最后一个为根节点

  • 堆是一种特殊的完全二叉树
  • 所有的节点都大于等于(最大堆)或者小于等于(最小堆)他的子节点

es6中map和object的区别是什么

  • 1、Map的键可以是任意值,而Object的键必须是一个String或是Symbol。
  • 2、Map中的key是有序的,而Object的键是无序的。
  • 3、Map的键值对个数可以轻易地通过size属性获取,而Object的键值对个数只能手动计算。
  • 4、Map可以直接被迭代,而Object不可以直接被迭代。
  • 5、Map在频繁增删键值对的场景下表现更好,而Object的效率比较差。

Vue

Vue的最大优势是什么?

  • 比较轻量,中国人自己写的框架,文档易读(这里比较轻松,拿出平时和朋友聊天的语气)
    • 双向数据绑定,
    • 数据驱动视图,
    • 组件化开发
    • 数据和视图分离
    • 单页面应用可以实现页面数据局部刷新

MVVM和MVC区别是什么?

MVC : 传统的设计模式。

  • 设计模式: 一套广泛被使用的开发方式
    M: model 模型
    模型:就是数据的意思
    V : view视图
    视图:就是页面的意思
    C:controller控制器
    控制器:在这里写js业务逻辑,把数据M 渲染到 视图 V (有点类似于我们之前学习的,把数据渲染到页面)

MVVM: vue所使用的设计模式

M: model数据模型 (data里定义)
V: view视图 (页面标签)
VM: ViewModel视图模型 (vue.js源码)
  • MVVM通过数据双向绑定让数据自动地双向同步 不再需要操作DOM
    V (修改视图) -> M(数据自动同步)
    M(修改数据) -> V (视图自动同步)

  • 设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。(代码分层, 架构设计)

1. 在vue中,不推荐直接手动操作DOM!!!
2. 在vue中,通过数据驱动视图,不要在想着怎么操作DOM,而是想着如何操作数据!!(思想转变)

vue第一次加载页面

1、创建vue实例
2、在创建Vue实例的时候,执行了init()初始化,在init过程中先调用了beforeCreate钩子函数;
3、同时监听data数据,初始化vue内部事件,进行属性和方法的计算
4、模板开始编译,把vue里面的数据和语法编译成HTML

当页面第一次加载时会触发 beforeCreate, created, beforeMount, mounted

Vue常用修饰符有哪些?

.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.once: 只执行一次这个事件
.enter:监听键盘enter键

对Vue渐进式的理解

- 主张最少,
- 自底向上,
- 增量开发,
- 组件集合,
- 便于复用
  • 个人见解
    使用模块化规范,实现自助餐式开发,用什么导什么。 最大程度上节省资源。

vue 生命周期

什么是vue生命周期和生命周期钩子函数?
beforeCreated:在实例初始化之后,el 和 data 并未初始化
(这个时期,this变量还不能使用,在data下的数据,和methods下的方法,watcher中的事件都不能获得到)
created:完成了 data 数据的初始化,el没有
(这个时候可以操作vue实例中的数据和各种方法,但是还不能对”dom”节点进行操作)
beforeMount:完成了 el 和 data 初始化 //这里的el是虚拟的dom;
mounted :完成挂载,在这发起后端请求,拿回数据,配合路由钩子做一些事情
(挂载完毕,这时dom节点被渲染到文档内,一些需要dom的操作在此时才能正常进行)
beforeUpdate:是指view层数据变化前,不是data中的数据改变前触发;
update:是指view层的数据变化之后,
beforeDestroy: 你确认删除XX吗?
destroyed :当前组件已被删除,清空相关内容
A、什么是vue生命周期?
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程。
B、vue生命周期的作用是什么?
它的生命周期有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
C、vue生命周期总共有几个阶段?
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
D、第一次页面加载会触发哪几个钩子?
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
E、DOM 渲染在 哪个周期中就已经完成?
DOM 渲染在 mounted 中就已经完成了。
F、简单描述每个周期具体适合哪些场景?
生命周期钩子的一些使用方法: beforeCreate : 可以在这加个loading事件,
在加载实例时触发 created : 初始化完成时的事件写在这里,如在这结束loading事件,
异步请求也适宜在这里调用 mounted : 挂载元素,获取到DOM节点 updated : 如果对数据统一处理,

说出至少4个Vue指令及作用

  • v-on 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
  • v-bind 动态绑定 作用: 及时对页面的数据进行更改, 可以简写成:分号
  • v-slot: 缩写为#, 组件插槽
  • v-for 根据数组的个数, 循环数组元素的同时还生成所在的标签
  • v-show 显示内容
  • v-if 显示与隐藏
  • v-else 必须和v-if连用 不能单独使用 否则报错
  • v-text 解析文本
  • v-html 解析html标签

为什么避免v-for和v-if在一起使用

Vue 处理指令时,v-for 比 v-if 具有更高的优先级, 虽然用起来也没报错好使, 但是性能不高, 如果你有5个元素被v-for循环, v-if也会分别执行5次.

v-if与v-show区别逐字稿

面试官你好,我是这么理解v-if和v-show的。 v-if本质其实是动态的创建 或者 删除元素节点。一般不用频繁切换, 要么显示, 要么隐藏的情况, 我都会用 v-if。因为v-if 是惰性的, 如果初始值为 false, 那么这些元素就直接不创建了, 这样就可以节省一些初始渲染开销。v-show本质是在控制元素的 css 样式,display: none;,一般元素需要频繁的切换显示隐藏, 用 v-show。因为v-if在频繁切换会大量的创建和删除元素, 消耗性能。

Vue中key值作用

  • 1.vue在渲染的时候,会 先把 新DOM 与 旧DOM 进行对比, 如果dom结构一致,则vue会复用旧的dom。 (此时可能造成数据渲染异常)
  • 2.使用key可以给dom添加一个 唯一标识符,让vue强制更新dom
面试官你好,我是这么理解key值的,key值的主要作用是给元素添加一个唯一标识符,用于提高vue渲染性能,当data发生变化的时候,vue会使用diff算法来对比新旧虚拟DOM.如果key值相同,才会考虑复用元素.如果key值不同,则会强制更新元素.一般通过给元素key设置为id,来保证vue更新数据的时候可以最大限度复用相同的key值的元素.

v-for指令使用key值几种情况

[v-for指令使用key值几种情况逐字稿]

  1. 没有key值(默认是下标) : 不复用,就地更新
  2. key值为下标(相当于没设置) : 不复用,就地更新
  3. key值是id : 复用相同的key,更新不同的key
    总结 : key值优先设置id, 没有id就用下标

Vue中:key作用, 为什么不能用索引

  • :key是给v-for循环生成标签颁发唯一标识的, 用于性能的优化
  • 因为v-for数据项的顺序改变,Vue 也不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素

v-model的作用及原理

  • 作用
    • 数据双向绑定指令,专门给表单元素
  • 原理
    • v-model是一个语法糖,他背后本质上是包含两个操作
      • v-bind绑定一个value属性
      • v-on指令给当前元素绑定input事件
        1
        2
        3
        4
        5
        v-model实现原理的例子
        <input type="text" v-model="message">
        // 等于
        // @input input框的事件
        <input type="text" v-bind:value="message" @input="message = $event.target.value">

Vue中有时候数组会更新页面,有时候不更新,这是为什么?

  • 因为vue内部只能监测到数组顺序/位置的改变/数量的改变, 但是值被重新赋予监测不到变更, 可以用 Vue.set() / vm.$set()

请说下封装 vue 组件的过程

  • 有复用的地方就有封装
    1.先分析需求:确定业务需求,把页面中可以复用的结构,样式以及功能
    2.具体步骤:Vue.component 或者在new Vue配置项components中, 定义组件名, 可以在props中接受给组件传的参数和值,子组件修改好数据后,想把数据传递给父组件。可以采用$emit方法

vue组件传值

  • 父传子
    1.子组件props定义变量
    2.父组件在使用子组件时通过行内属性给props变量传值
    特点:单向数据流
  • 子传父
    1.子组件:$emit触发父的事件
    2.父在使用组件用@自定义事件名=父的方法 (子把值带出来)
    特点:事件监听
  • 非父子组件
    vuex
    事件总线(Event Bus)
    事件总线是一种通过中央事件管理器来实现组件通信的方式。在Vue中,可以使用Vue实例作为事件总线来发送和接收事件。

Vue 组件 data 为什么必须是函数

因为组件是需要在多个地方使用的
    如果data是一个对象,对象是引用类型。 一旦某一个地方修改,就会全部修改
    data是一个函数,每一次复用组件的时候就会从这个函数返回一个新的对象。 这样组件在复用的时候就可以做到数据互不干扰。

讲一下组件的命名规范

  • 一种是使用链式命名”my-component”,一种是使用大驼峰命名”MyComponent”,
  • 因为要遵循W3C规范中的自定义组件名 (字母全小写且必须包含一个连字符),避免和当前以及未来的 HTML 元素相冲突

scoped作用与原理

作用:组件css作用域,避免子组件内部的css样式被父组件覆盖
默认情况下,如果子组件和父组件css选择器权重相同,优先加载父组件css样式
原理:给元素添加一个自定义属性 v-data-xxxxx

一针见血答案: 通过属性选择题来提高css权重值

Vue 的 nextTick 的原理是什么?

为什么需要 nextTick
Vue 是异步修改 DOM 的并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改–刷新后的 DOM 做相应的处理,这时候就可以使用 Vue.nextTick(callback)这个 api 了。
最终答案:
nextTick 的原理是 vue 通过异步队列控制 DOM 更新
nextTick底层是promise,所以是微任务。这个一定要知道

子组件修改父组件的数据

子组件通过this.$emit(“自定义事件名称a”,传递的参数)
父组件通过子组件标签接收
<组件标签 @自定义事件名称a=”自定义名称b”/>
methods:{
自定义名称b(v){
console.log(v)
}
}

  • $eventBus 灵活
    • this.$emit(‘事件名’,传递的参数)
    • this.$on(‘事件名’,接收的回调)
  • vuex 也算是一种

vue事件冒泡

当一个父元素div1 包裹着一个子元素div2 同时都有点击事件,我们点击子元素,不想触发父元素的事件,我们可以采用阻止事件冒泡解决
.stop 清除事件冒泡
.prevent 阻止默认行为
.once 只触发一次
.self 只允许元素自己触发

响应式

 简单说就是用户更改数据(Data)时,视图可以自动刷新,页面UI能够响应数据变化。
原理:在生成vue实例时,为对传入的data进行遍历,使用Object.defineProperty把这些属性转为getter/setter
每个vue实例都有一个watcher实例,它会在实例渲染时记录这些属性,并在setter触发时通知render重新渲染。

插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
更高级的组件复用方式。 接收dom片段或内容,加工处理后返回显示。
匿名插槽(默认插槽)
写在插槽组件中的内容或者template没有命名都会视为匿名插槽,相当于#default
<b>我是匿名插槽接收的数据</b>
b组件<template><solt>我要接收匿名插槽数据的</solt></template>
具名插槽
插槽组件中在template模板上使用v-slot命令绑定一个名称
插槽组件使用内置组件slot,用name属性去匹配v-slot名称
<b><template slot='aa'>要传的内容</template></b>
b插槽组件内:<template><div> <slot name='aa'>要传的内容会在这里显示</slot> </div></template>
作用域插槽
父组件中的插槽模板template里, 其作用域属于插槽组件。
父传子:父组件使用v-bind向插槽组件进行传参
子传父:插槽组件在slot组件上使用v-bind反向传参, 父组件使用v-slot='参数'接收数据
子集<slot :data="data"></slot>
父级:<b><template slot-scope="user">{{user.data.name}}</template>
  • 什么时候使用插槽
    当子组件被复用时,需要在特定的区域展示不同的定制化的内容。
  • 插槽的使用场景

v-show  v-if

共同点: v-if 和 v-show 都是动态显示DOM元素。
区别
编译过程:
v-if 是真正的条件渲染,因为它会在切换过程对元素和组件适当的销毁和重建 。
v-show的元素始终会被渲染。只是基于CSS属性display的切换
编译条件:
v-if 是惰性的:如果在初始渲染时条件为假,则什么也不做。直到条件第一次变为真时,才会开始渲染。
v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换
性能消耗:
v-if有更高的切换消耗。
v-show有更高的 初始渲染消耗`。
应用场景:
v-if适合条件很少改变时使用。
v-show适合频繁切换

自定义指令的方法有哪些?它有哪些钩子函数?还有哪些钩子函数参数?(必会)

全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令:directives
钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)
钩子函数参数:el、binding

vue路由作用与原理

路由作用: 实现单页面应用
原理:监听location的hash值

路由之间是怎么跳转的?有哪些方式?

1、
2、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面
3、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面
4、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数

再用动态路由的时候防止刷新白屏

router.addRoutes(routes) routes新的路由
next({…to,replace:true})

vue-router怎么配置路由(路由配置六个流程)

1.引入组件
2.配置路由path和组件, 和生成路由对象routes
3.创建路由对象router
4.把路由对象挂载到new Vue()
5.页面使用 承载路由
6. 设置路由导航(声明式导航方式/编程式跳转)

vue-router的钩子函数都有哪些(导航守卫)

关于vue-router中的钩子函数主要分为3类
1.全局钩子函数beforeEach(全局前置守卫,所有路由生效)
beforeEach函数有三个参数,分别是:
to:router即将进入的路由对象
from:当前导航即将离开的路由
next:function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的)否则为false,终止导航。
2.单独路由独享组件(只对这个路由生效)
beforeEnter,
3 组件内钩子
beforeRouterEnter,(渲染路由组件前)
beforeRouterUpdate,(路由改变)
beforeRouterLeave(路由离开)

完整的导航解析流程

  • 1.导航被触发。
  • 2.在失活的组件里调用 beforeRouteLeave 守卫。
  • 3.调用全局的 beforeEach 守卫。
  • 4.在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  • 5.在路由配置里调用 beforeEnter。
  • 6.解析异步路由组件。
  • 7.在被激活的组件里调用 beforeRouteEnter。
  • 8.调用全局的 beforeResolve 守卫(2.5+)。
  • 9.导航被确认。
  • 10.调用全局的 afterEach 钩子。
  • 11.触发 DOM 更新。
  • 12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

什么是路由守卫?

路由守卫又叫导航守卫,就是路由跳转 前,中,后过程中的一些钩子函数,这个函数能够让你操作一些其他的事。

路由传值的方式有哪几种

Vue-router传参可以分为两大类,分别是编程式的导航和声明式的导航
1.编程式导航:router.push
字符串:直接传递路由地址,但是不能传递参数
this.$router.push(“home”)
对象:
命名路由 这种方式传递参数,目标页面刷新会报错 name+params
this.$router.push({name:”news”,params:{userId:123}})
查询参数 和path配对的是query
this.$router.push({path:”/news’,query:{usersId:123}})
接收参数 this.$route.query
2.声明式导航
字符串 <router-link to:”news”>
命名路由 <router-link :to:”{name:’news’,params:{userId:1111}}”>
还可以to=”/path/值” - 需要提前在路由 规则里值 /path/:key
查询参数
还可以to=”/path?key=value

Vue路由传参方式,如何接收对应的值?

三种: 分别是query,params,动态路由传参
接收:
通过query方式传递过来的参数一般是通过this.$route.query接收
通过params方式传递过来的参数一般是通过this.$route.params接收
通过动态路由传参方式传递过来的参数一般是通过this.$route.params接收

Vue的路由实现模式:hash模式和history模式

1.路径不同
hash有#, history没有#
2.工作模式不同
hash : 修改当前页面hash,不需要服务器额外配
history: 会给服务器发送请求,需要服务器配置

1.hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用 window.location.hash 读取。特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。

2.history模式:history采用HTML5的新特性;且提供了两个新方法: pushState(), replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更

请说出路由配置项常用的属性及作用

路由配置参数:
path : 跳转路径
component : 路径相对于的组件
name:命名路由
children:子路由的配置参数(路由嵌套)
props:路由解耦
redirect : 重定向路由

$route和$router的区别?

$router 对象。用于跳转路由和传递参数
$route 对象。用于接收路由跳转参数

keep-alive

keep-alive组件 作用就是保持一个组件活着

  • 缓存状态组件,他不会渲染成真实dom,只是将被包裹的自定义组件的状态缓存到内存中
  • 多用于缓存 表单填写的组件
  • 它不能缓存v-for循环渲染出来的组件
     include属性,表示在被keep-alive管理的组件中,哪些需要保持活跃,有多个需要保持的,以逗号隔开
     exclude属性,表示在被keep-alive管理的组件中,哪些不需要保持活跃,

跟keep-alive有关的生命周期是哪些?

1.前言:在开发Vue项目的时候,大部分组件是没必要多次渲染的,所以Vue提供了一个内置组件keep-alive来缓存组件内部状态,避免重新渲染
2.生命周期函数:在被keep-alive包含的组件/路由中,会多出两个生命周期的钩子:activated 与 deactivated。
activated钩子:在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用。
deactivated钩子:组件被停用(离开路由)时调用

vuex作用及五大组成部分

vuex作用: 全局数据管理 解决复杂的父子组件传值
state作用:存储数据
getter作用:派生数据。相当于state计算属性
mutations作用:修改state中的数据
actions作用: 异步更新数据
module作用:模块化处理vuex数据

vuex中action工作流程

1.组件给actions发送消息
2.actions异步请求数据
3.actions将请求到的数据提交给mutations
4.mutations同步更新state中的数据

vuex刷新页面丢失数据原因和解决方法?

原因:- 因为js的数据都是保存在浏览器的堆和栈内的,刷新浏览器页面,以前堆栈申请的内存被释放(这就是浏览器的运行机制),那么堆栈里的数据自然就被清空了

  • 解决方法
    • 使用cookie或localstorage
    • 第二种方法是 vuex-along
      • 安装 vuex-along - npm install vuex-along –save
    • 第三种方法是 vuex-persistedstate po si te si de te
      • 安装 vuex-persistedstate - npm install –save vuex-persistedstate
    • 第四种方法是 vuex-persist
      • 安装 vuex-persist - npm install –save vuex-persistor

说一下你在vue中踩过的坑

1、操作data中的数据,发现没有响应式
原因: 数组中有很多方法,有的会改变数组(例如pop push),有的不会改变数组(例如slice, filter)
解决方案:通过Vue.set(对象,属性,值)这种方式就可以达到,对象新添加的属性是响应式的
2、在created操作dom的时候,是报错的,获取不到dom,这个时候实例vue实例没有挂载
解决方案:Vue.nextTick(回调函数进行获取)

小程序

小程序的登录流程是什么

  1. 用户点击登录按钮 通过wx.login() 获取用户的code码
  2. 通过wx.request() 网络请求将code码发送给开发者服务器
  3. 后端开发者拿到code码后需要通过appid+appsecret+code向微信接口服务器请求当前用户的session_key和openid
  4. 后端得到session_key 和openid后 会自定义一个登录态(token)和session_key及openid进行绑定
  5. 后端会将token返回给小程序端//tao kin
  6. 将小程序端得到token后 要将token存到本地

小程序项目允许的最大体积是多少

不适用分包的情况下 小程序总体积不能超过2MB
如果使用分包的情况下 总体积不能超过20MB 单个包体积不能超过2MB

小程序和普通网页区别

运行环境不同:网页在浏览器运行,小程序在微信环境运行

开发模式不同: 网页开发用浏览器+代码编辑器 小程序有自己的一套标准开发模式,使
用小程序开发工具

api不同 运行环境不同所以小程序没有办法调用bom和dom的api 小程序可以调用微信环境
提供的各种api 比如扫码,支付,地理定位,摇一摇,附近的人等等…

小程序的项目构成

pages 用来存放所有小程序页面
utils 用来存放工具性质模块,比如格式化时间.wxs文件,封装请求数据组件.js文件

app.js 小程序项目的入口文件 类似vue的 app.vue
app.json 小程序项目的全局配置文件
app.wxss 小程序项目的全局样式文件
project.config.json 项目的配置文件
sitemap.json 用来配置小程序是否允许被微信搜索引擎搜索到,比如:在微信小程序搜
京东,会弹出京东小程序,
如果关闭,搭建的项目,微信是无法搜索到的。

小程序页面的组成部分**

每个页面由四个基本文件组成,在app.json文件中配置好pages后小程序会自动生成文件

  1. .js文件 存放页面脚本文件,存放页面的数据,事件处理函数等 (写逻辑代码)
  2. .json文件 当前页面配置文件,配置窗口的外观,表现等
  3. .wxml文件 存放页面布局,类似html但不同,div=view span=text img=image
  4. .wxss文件 存放样式类似css但是和css不同,.wxss新增了rpx像素单位,只能引入外
    链文件和样式背景图等都必须外链文件。

## app.json中的配置有哪些

1、pages 用于保存页面的路径
2、window 配置页面头部的导航栏
3、tabBar 配置页面底部菜单栏
4、entryPagePath 页面加载时初始化的页面
5、networkTimeout 配置网络超时时间
6、subpackage 配置小程序的分包功能

window配置中的配置项有哪些

  • navigationBarBackgroundColor:导航栏背景色 只支持十六进制的颜色表示法 //nav一给身霸拜克哥软的卡乐
  • navigationBarTextStyle:导航栏的文本样式,只支持两个值 block white
  • navigationBarTitleText:导航栏的标题文本
  • backgroundColor:设置下拉刷新的背景色
  • backgroundTextStyle:下拉刷新界面的文本样式 只支持两个值 dark light
  • enablePullDownRefresh:是否启用下拉刷新功能 //以内bo pou当瑞服瑞吃
  • navigationStyle:导航栏的样式 只支持两个值 default与custom default表示默认显示导航栏,custom表示隐藏导航栏 只保留右侧胶囊按钮 可以自己定义

tabBar配置中的配置项有哪些

  • list 配置底部的菜单栏列表项
  • color 设置默认状态的文本颜色
  • selectedColor 设置激活状态的文本颜色
  • backgroundColor 设置菜单栏的背景色
  • borderStyle 设置菜单栏的上边框样式 支持white和black
  • position 菜单栏的位置 只支持top和bottom
  • custom 自定义菜单栏 true 或者 false

如何自定义tabBar

首先在app.json中 配置“tabBar”,对象然后创建list数组,写实际的路径,最少两个,最多五个。

networkTimeout配置中的配置项有哪些

  • request:配置网络请求的超时时间
  • connectSocket:配置既时通讯的超时时间
  • uploadFile:配置上传内容的超时时间
  • downloadFile:配置下载文件的超时时间

小程序渲染数据如何渲染

使用插值表达式渲染数据

列表渲染的指令是什么

wx:for

列表渲染如何修改item和index的名字

wx:for-item   wx:for-index

列表渲染可以渲染的数据类型有哪些

Array  Object  String  Number

block标签的作用是什么

专门用于条件渲染和列表渲染使用的,block标签不会渲染到页面视图中

小程序中如何实现响应式的数据修改

获取数据使用this.data.变量名
通过this.setData() 方法来修改数据

小程序生命周期函数有哪些

onLaunch:整个小程序初始化完成
onShow:小程序加载完成或者当小程序切回前台时触发
onHide:小程序切换到后台时触发

页面级生命周期函数有哪些

onLoad:当前页面加载时触发的时间
onShow:当前页面在前台显示时触发
onReady:当前页面初次渲染完成后触发
onHide:当前页面隐藏是触发
onUnload:当前页面卸载是触发

组件级生命周期函数有哪些

created:当前组件初始化完成时触发,无法调用setData方法
attached:当前组件在页面中挂载是会触发
ready:当前组件已经全部渲染完成后触发
moved:当前组件从节点树中的一个位置移动到另一个位置时触发
detached:当前组件被移除是触发--*

组件的主要生命周期

在小程序中,最重要的生命周期函数有三个分别是,
created,attached,detached
1.组件实例刚刚被创建好触发created,不能调用setData
用来给组件的this添加一些自定义属性
2.组件完全初始化完毕,进入页面节点树后,触发
attached。this.data已经被初始化完毕,初始化工作比如发送请求。
3.组件离开页面节点树,触发detached生命周期函数,清理性质工作

input组件如何实现双线数据绑定

需要通过 value=’‘ 将数据渲染到输入框中 再通过bind:input 事件 获取用户输入的内容,来修改数据的值

input组件用户输入内容时,如果修改键盘右下角文字提示,可以修改成哪些值

send	   发送
search	 搜索
next	   下一个
go	     前往
done	   完成

input组件中有几种键盘类型,分别是什么

input 键盘类型
text              文本输入键盘  
number            数字输入键盘  
idcard            身份证输入键盘 
digit             带小数点的数字键盘 
safe-password     密码安全输入键盘 指引 
nickname          昵称输入键盘

scroll-view组件想要纵向滚动时,必须要设置的内容有哪些

scroll-y 必须设置高
navigate  redirect  switchTab reLaunch  navigateBack  exit
navigate:保留当前页面
redirect:关闭当前页面
共同点:不能跳到tabBar页面 可以携带参数
navigate:保留当前页面 不可以跳转tabBar 可以携带参数
switchTab:跳转到tabBar页面 并关闭其他所有非tabBar页面 不可以传参
没有共同点
navigate:保留当前页面 不可以跳转tabBar
reLaunch:关闭所有页面 允许跳转任意页面
共同点:可以传递参数

图片组件是否有默认宽高,默认宽高是多少?

宽:320px 高240px

wxss和css的不同之处有哪些

wxss文件
在小程序中,不能使用通配符选择器(*)
新增了尺寸单位 rpx
新增样式导入   使用@import语句可以导入外联样式表  该语句后面必须要以分号结尾,不然会报错
可以配置全局样式与局部样式,全局样式直接写在app.wxss中

小程序有几种绑定事件的方式,有什么区别

小程序的事件系统
两种绑定事件的语法 bind 与catch
通过bind绑定通用事件,会产生事件流,如果祖级元素有相同类型的事件,会同时触发
catch绑定通用事件,不会产生事件流,
bind与catch绑定时,语法为bind:事件名,但是中间的:可以省略

常见移动端事件有哪,说出5个以上

移动端事件:
tap 触摸事件
longtap 长按事件(被longpress代替)
touchstart 手指触摸动作开始事件
touchend 手指触摸动作结束事件
touchmove 手指触摸后移动事件
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
touchforcechange  在支持 3D Touch 的 iPhone 设备,重按时会触发

触摸事件的事件对象中有哪些常见属性,作用是什么

事件对象
在小程序的事件绑定函数上,不允许加括号调用
触摸事件的事件对象
 changedTouches:[] 记录发生改变的手指的数量
 currentTarget: 表示真正触发事件的元素
 offsetLeft: 元素与设备左侧的距离
 offsetTop: 元素与设备上方的距离
 detail: 记录参数信息,手指触摸的坐标,
 target: 手指触摸的元素
 touches:[] 记录手指触摸的个数
 type: 事件类型

target 和currentTarget两者的区别是什么

target表示的是手指触摸的元素,而currentTarget表示响应事件的元素

我们可以通过target中的内容来进行判断,实现事件委托的操作
我们可以通过target中的id属性来进行判断触发的是哪一个组件,实现事件委托的操作

小程序中的事件如何传递数据

通过data-属性来传递数据  例 data-变量名="传递的数据"  通过e.currentTarget.dataset.变量名接收数据

小程序中全局数据如何使用,有什么特点

在app.js中,配置一个globalData属性,在这个属性中保存的内容就是全局数据
在其他页面中使用时,通过getApp()方法,可以获取到app实例,从而使用app.globalData就可以拿到数据
在globalData中的数据不能通过setData()方法来更新

如何自定义一个组件

在项目中新建一个components 目录 该目录用于保存自定义组件 在页面的json文件中的usingComponents字段中 引入对应的组件路径 之后就可以在对应页面的wxml中使用该组件

关于父子组件之间样式是如何影响的

在组件的js文件中可以添加一个options,该选项是一个对象,对象中可以添加一个styleIsolation属性用于控制父子组件之间的样式隔离问题
styleIsolation属性有3个可选值:
    isolated :默认不会相互影响
    apply-shared 父影响子
    shared:相互影响

组件的外部样式类如何设置

- 在子组件希望有样式的标签上添加一个类名
- 在子组件的component构造器中添加externalclasses:[]添加上的类名 
- 在父组件的子组件标签上 添加对应的class的属性名 值是一个父组件的类名
- 在父组件的wxss中设置对应的class的样式即可生效

小程序想要实现多个插槽使用如何设置

如果想要同时使用多个 需要在组件的js文件的options选项内开启配置multipleSlots:true 才会生效

父组件如何传值给子组件

1. 在父组件的子组件标签上动态绑定一个自定义属性,将要需要传递的数据放到属性值中
2. 在子组件的js文件中 通过properties属性来接收父组件传递的数据

子组件如何传值给父组件

1. 在子组件的事件函数中 通过this.triggerEvent()向父组件发出一个自定义事件 该方法有两个参数 第一个是自定义事件名 第二个是传递的参数
2. 在父组件的子组件标签上 通过bind监听子组件发出的自定义事件 在事件对象event的detail属性中可以接收到子组件传递过来的数据

组件如何监听页面级生命周期

通过pageLifetimes属性来监听
该属性对象中有三个响应函数 show hide resize
    1. show 表示组件所在页面被展示时的函数
    2. hide 表示组件所在页面被隐藏时的函数
    3. resize 表示组件所在页面尺寸发生变化时的函数

小程序中的交互API有哪些

wx.showToast:消息提示框
wx.showModal:显示模态对话框
wx.showLoading 显示 loading 提示框。需主动调用 wx.hideLoading 才能关闭提示框
wx.showActionSheet 显示操作菜单
wx.hideToast 隐藏消息提示框
wx.hideLoading 隐藏loading提示框

小程序中域名无法识别如何处理

在小程序本地设置中勾选不校验合法域名选项即可请求
在发布阶段,如果希望能够请求服务器资源,那么我们必须为这个服务器配置白名单

网络请求限制,如何配置合法域名?

1.出于安全考虑,小程序对数据接口的请求有限制要求,小程序只能请求https类型接口,
必须将接口的域名添加到信任列表中否则控制台会提示(警告)
2.登录小程序公众平台--->开发管理---->开发设置--->服务器域名--->修改合法域名

小程序中base64与二进制数据流之间的转换如何转换

wx.base64ToArrayBuffer
wx.arrayBufferToBase64

你是怎么封装微信小程序的数据请求的?

在根目录下创建util目录及api.js文件和apiConfig.js文件
在apiConfig.js封装基础的get,post和put,upload等请求方法,设置请求体,带上token和异常处理等
在api中引入apiConfig.js封装好的请求方法.根据页面数据请求的urls,设置对应的方法并导出
在具体的页面中导入或将所有的接口放在统一的js文件中并导出
在app.js中创建封装请求数据的方法

小程序支付如何实现?

小程序注册,要以公司的身份去注册一个小程序,才有微信支付权限

绑定商户号

在小程序填写合法域

调用wx.login()获取appid

调用

1
2
3
4
5
6
7
8
9
10
11
wx.requestPayment(
{
'timeStamp': '',//时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
'nonceStr': '',//随机字符串,长度为32个字符以下。
'package': '',//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
'signType': 'MD5',//签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
'paySign': '',//签名,具体签名方案参见微信公众号支付帮助文档;
'success':function(res){},//成功回调
'fail':function(res){},//失败
'complete':function(res){}//接口调用结束的回调函数(调用成功、失败都会执行)
})

小程序如何进行分包

在subpackages字段里面的页面都是分包内容
root 分包根目录
name 分包别名
pages 分包页面路径

普通分包和独立分包有什么不同

普通分包 那么该包必须依赖主包才能加载 不允许单独加载
独立分包 独立运行 不需要先加载主包

分包有哪些注意事项

tabBar页面不允许放在分包中 只能放在主包
一个分包的根目录不能是另一个分包的子目录
一个分包不能require导入另一个分包的js文件 但是可以使用主包的js文件
一个分包不能使用另一个分包中的资源 但是可以使用主包资源

uni-app

uni-app有哪些优势?

  • 开发者/案例的数量最多
  • 平台能力不受限制
  • 性能体验优秀
  • 周边生态丰富
  • 学习成本低
  • 开发成本低

uni-app语法、组件 及 生命周期 的 相同之处?

  • 所有的语法接近与vue 可以使用vue的所有的指令,以及vue中的语法
  • 所有的组件接近与微信小程序
  • 所有的生命周期接近于小程序

uni-app的不同之处?

  • 条件编译 可以在任意位置出现
    是以注释的形式来添加条件编译

#ifdef 表示包含那些端渲染,起始位置
#idndef 表示排除那些端渲染,起始位置
#endif 表示结束位置
各端的条件:
H5 表示移动端
MP 表示所有小程序
MP-WEIXIN 表示微信小程序
APP-PLUS 表示APP端

  • APP端的Nvue开发
    如果你不开发APP,那么是不需要使用Nvue的
  • HTML5+引擎
    APP内置了HTML5+引擎,可以让js具有直接调用原生能力,在H5和小程序端没有HTML5+引擎的,因此如果需要使用这个引擎,还是需要加条件编译

网络封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let baseUrl="网址";
export function request(confing){
return new Promise((res,rej)=>{
wx.request({
url:baseUrl+confing.url,
method:confing.method||"GET",
data:confing.data,
header:{
"content-type":"application/x-www-form-urlencoded"
},
success:(r)=>{
res(r)
}
})
})
}

Vue3

vue2和Vue3的区别

一. 根节点不同
vue2中必须要有根标签
vue3中可以没有根标签,会默认将多个根标签包裹在一个fragement虚拟标签中,有利于减少内存。
二. 组合式API和选项式API
在vue2中采用选项式API,将数据和函数集中起来处理,将功能点切割了当逻辑复杂的时候不利于代码阅读。
在vue3中采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。
三. 生命周期的变化
创建前:beforeCreate -> 使用setup()
创建后:created -> 使用setup()
挂载前:beforeMount -> onBeforeMount
挂载后:mounted -> onMounted
更新前:beforeUpdate -> onBeforeUpdate
更新后:updated -> onUpdated
销毁前:beforeDestroy -> onBeforeUnmount
销毁后:destroyed -> onUnmounted
异常捕获:errorCaptured -> onErrorCaptured
被激活:onActivated 被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
切换:onDeactivated 比如从 A 组件,切换到 B 组件,A 组件消失时执行
四. v-if和v-for的优先级
在vue2中v-for的优先级高于v-if,可以放在一起使用,但是不建议这么做,会带来性能上的浪费
在vue3中v-if的优先级高于v-for,一起使用会报错。可以通过在外部添加一个标签,将v-for移到外层
五. diff算法不同
  • vue2中的diff算法
    遍历每一个虚拟节点,进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方。
    用patch记录的消息去更新dom
    缺点:比较每一个节点,而对于一些不参与更新的元素,进行比较是有点消耗性能的。
    特点:特别要提一下Vue的patch是即时的,并不是打包所有修改最后一起操作DOM,也就是在vue中边记录变更新。(React则是将更新放入队列后集中处理)。

  • vue3中的diff算法
    在初始化的时候会给每一个虚拟节点添加一个patchFlags,是一种优化的标识。
    只会比较patchFlags发生变化的节点,进行识图更新。而对于patchFlags没有变化的元素作静态标记,在渲染的时候直接复用。

六. 响应式原理不同
  • vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现,Object.definedProperty()会遍历每一个属性。

  • vue3通过proxy代理的方式实现。
    proxy的优势:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。
    当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升

更多vue3请看我的vue3 + ts 哦!