面试记录
# vue
vue组件怎么封装?
- 当我们需要在多个页面或者多个项目中复用一段代码时,可以将这段代码封装为一个组件,以便于重复利用。
- 在需要使用该组件的父组件中,通过
import
引入该组件。 - 在父组件中,使用
<my-component>
标签即可使用该组件,并可以传入需要的属性。 - 父向子传递数据是通过
props
,子向父是通过$emit
触发事件; - 在组件内部不确定该位置是以何种形式的元素展示时,可以通过
slot
占据这个位置,该位置的元素需要父组件以内容形式传递过来。
vue二维表格怎么实现(有排序功能)?
- 查看组件的文档,看看有没有对应的排序功能
- 和后端沟通,是否有接口是否有排序的功能
- 如果后端有排序的功能,当表头被点击时,触发排序事件,对表格数据进行排序,并重新渲染表格。
vue导航守卫有什么?
常用的导航守卫包括
beforeEach
、beforeResolve
、afterEach
beforeEach
: 在进入路由之前执行,可以用来做登录校验等功能。beforeResolve
: 在路由确认之前、异步路由组件解析完毕之后执行,可以用来确保异步组件的加载完成。afterEach
: 在路由跳转完成后执行,可以用来做页面切换动画等功能。
导入Excel文件
- 创建
excel
导入页面 - 点击
excel
导入按钮,进入该页面 - 选中文件之后,使用JS库(如xlsx)读取Excel文件内容,并将其转换为JavaScript对象或数组
- 上传解析之后的数据
- 创建
分页
vuex怎么修改state的值
使用 mutations 修改状态: 在 Vuex 的 store 中,mutations 是用于修改状态的方法。你可以通过提交一个 mutation 来改变 state 中的值,这是一种同步的方式。
在某些情况下,你可能需要在异步操作完成后再修改状态,这时可以使用 actions。在 actions 中可以包含异步操作,然后再提交 mutations 来修改状态。
// 定义 action actions: { updateValueAsync({ commit }, payload) { setTimeout(() => { commit('updateValue', payload); }, 1000); } } // 在组件中分发 action this.$store.dispatch('updateValueAsync', newValue);
1
2
3
4
5
6
7
8
9
10
11
事件总线怎么使用
Vue3如果想要实现这种机制,则需要通过mitt来进行实现
import mitt from 'mitt' const emitter = mitt() // 监听一个事件 emitter.on('foo', e => console.log('foo', e) ) // 监听所有事件 emitter.on('*', (type, e) => console.log(type, e) ) // 触发事件 emitter.emit('foo', { a: 'b' }) // 情况所有事件 emitter.all.clear() // working with handler references: function onFoo() {} emitter.on('foo', onFoo) // 监听 emitter.off('foo', onFoo) // 卸载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20换主题颜色
- 主题配置:在前端项目中创建一个主题配置文件,用于存储不同主题的颜色变量或配置项。例如,可以创建一个
theme.js
文件,里面包含不同主题的颜色变量,如主题的主色调、背景颜色、文本颜色等。 - 样式文件:在项目中创建一个样式文件,用于定义各个页面和组件的样式。这个样式文件会引用主题配置文件中的颜色变量。
- 主题切换功能:实现一个主题切换的功能,使用户能够在前端后台界面中切换不同的主题。可以使用 Vuex、React Context 或其他状态管理工具来管理当前主题的状态。
- 主题切换逻辑:在主题切换功能中,根据用户选择的主题,更新存储在状态管理工具中的当前主题。同时,更新主题配置文件中的颜色变量。
- 样式更新:通过监听当前主题的变化,实时更新应用程序中的样式。可以使用 CSS 变量、样式预处理器(如 Less、Sass)或 JavaScript 动态修改样式的方式来实现。确保页面和组件的样式与当前主题保持一致。
- 持久化主题设置:如果希望用户下次访问时保持之前选择的主题,可以使用浏览器的本地存储(localStorage 或 sessionStorage)将用户的主题设置持久化存储,并在应用初始化时读取这些设置。
- 主题配置:在前端项目中创建一个主题配置文件,用于存储不同主题的颜色变量或配置项。例如,可以创建一个
断点
页面适配
通过flex和媒体查询结合使用
amis
vue2双向绑定中,例如为对象添加一个属性,vue为什么检测不到属性修改
在 Vue 2.x 中,双向绑定是通过数据劫持和依赖追踪来实现的。Vue 使用了一个叫做 "响应式系统" 的机制来追踪数据的变化并更新视图。这个系统会在组件实例创建时对数据进行劫持,并在数据发生改变时通知相关的视图更新。
然而,Vue 在数据劫持时只会对初始化时已经存在的属性进行劫持,对于后来添加的属性,Vue 默认是无法检测到其修改的。这是因为 Vue 的响应式系统在初始化时会遍历对象的所有属性,并将其转换成 getter 和 setter 来实现双向绑定。但是对于后来添加的属性,Vue 并没有对其进行劫持。
如果你需要动态地给一个对象添加新的属性,并且希望 Vue 可以检测到这些属性的修改,可以使用
Vue.set
方法或this.$set
方法来添加属性,这样 Vue 就会对新添加的属性进行劫持。Object.defineProperty的原理是通过将数据属性转变为存取器属性的方式实现的属性读写代理。而Proxy则是因为这个内置的Proxy对象内部有一套监听机制,在传入handler对象作为参数构造代理对象后,一旦代理对象的某个操作触发,就会进入handler中对应注册的处理函数,此时我们就可以有选择的使用Reflect将操作转发被代理对象上。
watchEffect和watch的区别
watchEffect
是Vue 3 中引入的一个新 API,只需要传入副作用函数,会自动追踪其内部使用的响应式数据,并在数据变化时自动重新执行副作用函数。适用于需要立即执行副作用函数的情况。watch
需要指定要监听的具体响应式数据,并在回调函数中处理数据变化的逻辑。可以设置更多的选项来控制监听器的行为。适用于需要对数据变化进行更细粒度控制的情况。适合在不同生命周期发送请求
created
生命周期:在组件创建时,需要获取初始数据进行初始化。比如获取用户信息、获取配置信息等。
mounted
生命周期:在组件挂载后,需要请求数据进行页面渲染。比如获取列表数据、获取广告数据等。
需要进行 DOM 操作的请求,比如获取某个元素的尺寸或位置信息,然后根据获取的信息动态设置样式或布局。
# react
- react的hooks
- useState :用于在函数组件中声明和使用状态。它接受一个初始状态,并返回一个包含当前状态值和更新状态值的数组。通过调用更新函数,可以更新状态,并且会重新渲染组件。
- useEffect:用于在函数组件中执行副作用操作,比如订阅数据、处理 DOM 更新等。它接受一个回调函数和一个依赖数组,用于指定副作用的触发条件。
- useContext:用于在函数组件中访问 React 的上下文(context)。它接受一个上下文对象(通过
React.createContext
创建),并返回当前上下文的值。 - useReducer:用于管理具有复杂状态和逻辑的组件。它类似于 Redux 中的 reducer 概念,可以帮助组件处理和更新状态。
- useCallback:用于在函数组件中缓存回调函数,以便在依赖项不变的情况下避免不必要的函数重新创建。
- useMemo
# html
- Doctype 是用于让标准通用语言解析器知道怎么解析
- <img style="display:none;”src="http://m.xxxx.cn/test.png”/>会加载test.png吗
# js
Promise三种状态
1.pending :等待状态,比如正在进行网络请求,或者定时器没有到时间的时候。
2.fulfilled: 满足状态,当我们主动回调了resolve()时,接口调用成功,就处于该状态,并且会回调.then()
3.rejcet:拒绝状态,当我们主动回调了reject()时,接口调用失败,就处于该状态,并且会回调.catch()
Promise怎么取最快的一个
Promise.any()
Promise.any()
返回一个 Promise,如果数组中的任何一个 Promise 成功,则该 Promise 解析(resolve)。如果数组中所有 Promise 都失败,则该 Promise 拒绝(reject)。Promise.race()
Promise.race()
返回一个 Promise,如果数组中的任何一个 Promise 完成(无论成功或失败),则该 Promise 立即解析(resolve)或拒绝(reject)。返回的结果则是所有 promise 中最先返回的结果,不关心是成功还是失败。
Promise.all/any/race的区别
Promise.all
等待所有 Promise 解析,并返回所有结果的数组。Promise.any
等待至少一个 Promise 解析,并返回第一个解析的结果。Promise.race
等待第一个 Promise 解析,并返回第一个解析的结果或失败的原因。列出你熟悉的es6特性
- 箭头函数(Arrow Functions):使用简洁的语法定义函数,并绑定了词法作用域。箭头函数没有自己的
this
值,而是继承外部作用域的this
值。 - const 和 let:引入块级作用域的变量声明关键字。
const
声明一个常量,而let
声明一个可变的变量。 - 模板字面量(Template Literals):使用反引号(
)包裹的字符串,可以在其中插入变量或表达式,使用
${}` 进行插值。 - 解构赋值(Destructuring Assignment):通过模式匹配的方式从数组或对象中提取值,并赋值给对应的变量。
- 默认参数(Default Parameters):在函数参数列表中为参数设置默认值,简化了函数的使用,并允许在调用函数时省略某些参数。
- 扩展运算符(Spread Operator):用三个点(...)表示,可以在函数调用、数组字面量、对象字面量等处展开数组或对象。
- 类和模块(Classes and Modules):引入了类和模块的概念,使得面向对象编程更加直观和易用。
- 箭头函数(Arrow Functions):使用简洁的语法定义函数,并绑定了词法作用域。箭头函数没有自己的
this
值,而是继承外部作用域的this
值。 - Promise:用于处理异步操作的对象,提供了一种更优雅的方式来处理回调函数地狱。
- 迭代器和生成器(Iterators and Generators):迭代器提供了一种遍历数据的通用接口,生成器是一种特殊的函数,可以通过暂停和恢复的方式产生多个值。
- 模块化(Module System):引入了模块化的概念,通过
export
和import
关键字实现模块的导出和导入。
- 箭头函数(Arrow Functions):使用简洁的语法定义函数,并绑定了词法作用域。箭头函数没有自己的
JavaScript 有哪几种数据类型?
基本数据类型:
- 字符串(String):表示文本数据,用于存储和操作文本信息。例如:
"Hello World"
。 - 数字(Number):表示数值数据,用于存储和操作数值信息。包括整数、浮点数、负数等。例如:
42
、3.14
。 - 布尔值(Boolean):表示逻辑值,只有两个取值:
true
(真)和false
(假)。用于控制程序的流程和逻辑判断。 - undefined:表示未定义的值,当声明了一个变量但未给它赋值时,该变量的值为
undefined
。 - null:表示空值或空对象引用。用于显式地表示一个变量没有值。
特殊的数据类型:
- 对象(Object):表示复杂数据结构,可以包含多个键值对。对象是属性和方法的集合,用于组织和存储相关的数据和功能。
- 数组(Array):表示一组有序的数据集合。数组可以包含任意类型的数据,并通过索引来访问和操作数组中的元素。
- 函数(Function):是一段可执行的代码块,用于封装和执行特定的任务。函数可以接收参数、执行特定的操作,并返回结果。
- Symbol:表示唯一的标识符,用于创建对象的私有属性或键。
- 字符串(String):表示文本数据,用于存储和操作文本信息。例如:
css3中transform的作用?
- 平移(Translate):使用
translate
函数可以将元素在水平和垂直方向上移动指定的距离。例如:transform: translate(100px, 50px);
将元素向右平移 100 像素,向下平移 50 像素。 - 缩放(Scale):使用
scale
函数可以按比例缩放元素的大小。例如:transform: scale(1.5);
将元素放大到原来的 1.5 倍。 - 旋转(Rotate):使用
rotate
函数可以使元素按指定角度进行旋转。例如:transform: rotate(45deg);
将元素顺时针旋转 45 度。 - 倾斜(Skew):使用
skew
函数可以使元素按指定角度进行倾斜。例如:transform: skew(10deg, 20deg);
将元素水平倾斜 10 度,垂直倾斜 20 度。 - 变形原点(Transform Origin):使用
transform-origin
属性可以设置元素变换的基准点,默认是元素的中心点。 - 组合变换(Transforms Combination):可以通过将多个变换组合在一起,实现复杂的动画和效果。例如:
transform: translate(100px, 50px) rotate(45deg) scale(1.5);
将元素先平移,再旋转,最后缩放。
transform
属性还可以与过渡(transition)、动画(animation)等属性结合使用,实现更丰富的动态效果。- 平移(Translate):使用
Js中0.1+0.2为什么不等于0.3? 怎么解决?
在 JavaScript 中,0.1 + 0.2 不等于 0.3 的原因是浮点数的精度问题。由于计算机内部使用二进制来表示浮点数,而二进制表示有时无法精确地表示某些十进制小数。因此,在进行浮点数计算时,可能会出现舍入误差,导致结果不完全精确。
使用整数进行计算:将需要计算的小数转换为整数,进行整数运算,最后再将结果转换回小数。例如,将 0.1 和 0.2 分别乘以 10,得到 1 和 2,然后进行整数相加,得到 3,最后再除以 10,得到 0.3。
const result = (0.1 * 10 + 0.2 * 10) / 10; // 结果为 0.3
1使用精确计算库:可以使用一些第三方库,如 Decimal.js、Big.js 等,来进行精确的十进制计算。这些库提供了高精度的十进制数值计算,可以避免浮点数精度问题。
// 使用 Decimal.js 示例 const Decimal = require('decimal.js'); const result = new Decimal(0.1).plus(0.2).toNumber(); // 结果为 0.3
1
2
3
4
一列数的规则如下:1、1、2、3、5、8、13、21、34......求第30 位数是多少,用递归方式解答。
斐波那契数列(Fibonacci Sequence)的规则是,从第三个数开始,每个数都是前两个数的和。
function fibonacci(n) { if (n === 1 || n === 2) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } const result = fibonacci(30); console.log(result); // 输出第 30 位数的值
1
2
3
4
5
6
7
8
9
10在上面的代码中,
fibonacci
函数接受一个参数n
,表示要求解的斐波那契数列的位置。当n
为 1 或 2 时,直接返回 1,作为递归的基准情况。否则,通过递归调用fibonacci
函数来计算前两个数的和,并返回结果。运行上述代码,将得到斐波那契数列中第 30 位的值,即 832040。
编写个函数来获取url 的参数值(请用正则方式解答)
var urI ="http://www.xxx. com/index.html?key0-0&key1=1&key2=2";
function getUrlParameter(url, parameter) { // 创建正则表达式,匹配 parameter=xxx 这样的字符串 const regex = new RegExp(`[?&]${parameter}=([^&#]*)`); // 在 URL 中查找匹配的参数值 const match = url.match(regex); // 如果有匹配的结果,则返回参数值,否则返回 null return match ? match[1] : null; } // 示例用法 const url = 'https://www.example.com/?name=John&age=25'; const parameterValue = getUrlParameter(url, 'age'); console.log(parameterValue); // 输出 25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15在上述代码中,
getUrlParameter
函数接受两个参数:url
是完整的 URL 字符串,parameter
是要获取的参数名。函数内部使用正则表达式创建一个模式,通过匹配
parameter=xxx
这样的字符串来提取参数值。然后,在给定的 URL 中使用match
方法进行匹配,并获取匹配结果数组。如果有匹配的结果,则返回第一个捕获组的值,即参数值。如果没有匹配结果,则返回 null。请问下面打印出来的结果是?
for (var i=0;i<3;i++) {
setTimeout(function () {
console.log("a:"+i);
},0);
console.log("b:"+i);
}
b:0
b:1
b:2
a:3
a:3
a:3
2
3
4
5
6
7
8
9
10
11
12
13
列举几种常见设计模式并简述其使用场景
- 单例模式(Singleton Pattern):用于确保类只有一个实例,并提供一个全局访问点。适用于需要全局共享资源、管理唯一对象或限制对象创建的场景。
- 工厂模式(Factory Pattern):通过工厂方法创建对象,而不是直接通过构造函数实例化对象。适用于需要根据不同条件创建不同对象的场景,提供了灵活的对象创建机制。
- 观察者模式(Observer Pattern):定义了对象间的一对多依赖关系,当一个对象状态改变时,其所有依赖者都会收到通知并自动更新。适用于对象间存在一对多的关系,当一个对象改变时,通知其他对象进行相应的处理。
- 策略模式(Strategy Pattern):定义一系列算法,将它们封装起来,并使它们可以相互替换。适用于需要在运行时根据不同情况选择不同算法的场景,提供了灵活的算法选择机制。
- 适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口,使原本不兼容的类可以合作。适用于需要将一个类的接口适配到另一个类的场景,提供了不同接口之间的桥梁。
- 装饰器模式(Decorator Pattern):动态地给一个对象添加额外的职责,而不影响其他对象。适用于需要动态添加功能、职责或行为的场景,提供了灵活的功能扩展机制。
- 代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。适用于需要控制对对象的访问、提供额外功能或隐藏对象的场景。
Vue.js 使用了以下几种设计模式:
- MVVM(Model-View-ViewModel)模式:Vue.js 基于 MVVM 模式进行开发。在 MVVM 模式中,视图(View)与数据模型(Model)之间通过一个称为 ViewModel 的中间层进行连接和通信。Vue.js 的响应式数据绑定机制和模板语法使得开发者可以直接操作数据,而不需要手动更新 DOM,从而实现了视图和数据的自动同步。
- 组件模式(Component Pattern):Vue.js 的核心思想是组件化,将应用程序拆分为多个可复用的组件。组件模式通过将 UI、数据和逻辑封装在一个组件中,提供了更高的可维护性和可复用性。
- 观察者模式(Observer Pattern):Vue.js 使用观察者模式实现了响应式数据绑定。当数据发生变化时,Vue.js 会自动通知相关的视图进行更新。这种机制使得开发者无需手动追踪数据变化,提高了开发效率。
- 依赖注入模式(Dependency Injection Pattern):Vue.js 使用依赖注入模式实现了组件之间的依赖关系管理。通过在组件定义中声明依赖关系,Vue.js 可以在组件实例化时自动注入依赖,简化了组件间的通信和协作。
- 虚拟 DOM 模式(Virtual DOM Pattern):Vue.js 使用虚拟 DOM 模式优化了 DOM 操作的性能。Vue.js 在内部维护了一个虚拟 DOM 树,通过比较新旧虚拟 DOM 树的差异,最小化了对实际 DOM 的操作,从而提高了渲染性能。
什么是闭包?请解释并写一个简单的闭包
闭包是指一个函数可以访问并操作其外部函数作用域中的变量和参数,即使外部函数已经执行完毕,这些变量依然可以被访问。
function f1(){ var n=999; function f2(){ console.log(n); } return f2; } var result=f1(); result(); // 999
1
2
3
4
5
6
7
8
9如何理解作用域,es5中和es6有什么区别?
作用域 (Scope) 是指程序中定义变量的区域,定义在该区域内的变量只能在该区域内访问。在JavaScript中,作用域分为全局作用域和函数作用域两种。
在ES5中,作用域主要通过函数来实现。每个函数都有自己的作用域,函数内定义的变量只能在该函数内部访问,函数外部无法访问。如果在函数内部使用var关键字定义变量,该变量的作用域为函数作用域。如果定义在函数外部的变量,则其作用域为全局作用域,可以在整个程序中被访问。
在ES6中,引入了块级作用域 (Block Scope) ,通过let和const关键字来定义变量。使用let和const定义的变量的作用域为块级作用域,即在花括号0内定义的变量只能在花括号内访问,花括号外部无法访问。在块级作用域中,变量会形成一个封闭的作用域,不会被外部的变量所干扰。而且,ES6中的const关键字定义的变量是常量,不能被重新赋值,保证了代码的安全性。
总的来说,ES6中引入了块级作用域,让变量的作用域更加精细化,增强了代码的可读性和可维护性。
写出三种用js实现的类方法
基于原型的实现方式
// 手机(父级) function Phone(brand,price){ this.brand = brand this.price = price } Phone.prototype.call = function(){ console.log("我可u打电话!") } // 智能手机(子级) function SmartPhone(brand,price,color,size){ Phone.call(this,brand,price) // 调用父类的构造函数 this.color = color this.size = size } // 设置子级构造函数的原型(我的实例对象原型有父级的方法) SmartPhone.prototype = new Phone // 声明子类的方法 SmartPhone.prototypr.photo = function(){ console.log("我可以拍照!") } SmartPhone.prototypr.playGame = function(){ console.log("我可以玩游戏!") } const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch')
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基于类的实现方式
// 父类 class Phone{ // 构造方法 名字不能修改 constructor(brand,price){ this.brand = brand this.price = price } // 方法必须使用该语法,不能使用ES5的对象完整形式 call(){ console.log("我可u打电话!") } } class SmartPhone extends Phone{ // 构造方法 constructor(brand,price,color,size){ super(brand,price) // 调用父类的构造函数 this.color = color this.size = size } photo(){ console.log("拍照") } playGame(){ console.log("玩游戏") } } const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch')
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工厂函数的实现方式
写出一个原型链继承的例子
原型链继承是JavaScript中常见的一种继承方式,它基于原型链的特性来实现继承。通过将子类的原型设置为父类的实例,来实现子类继承父类的属性和方法。
function Person(name) { this.name = name this.printNama = function () { console.log('Person printNama'); } } Person.prototype.commen = function () { console.log('commenMethods') } function Student(name, score) { Person.call(this, name) this.score = score this.printScore = function () { console.log('Student printScore'); } } Student.prototype = new Person() let person = new Person('小红', 30) let student = new Student('小敏', 20) console.log(Student.commen === Person.commen) console.log('student', student) console.log('person', person)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22数组去重
使用 Set
const arr = [1, 2, 2, 3, 3, 4, 5, 5]; const uniqueArr = [...new Set(arr)];
1
2使用 indexOf 或 includes
可以遍历原始数组,将元素逐个添加到一个新数组中,但在添加之前先判断新数组中是否已经包含该元素。
const arr = [1, 2, 2, 3, 3, 4, 5, 5]; const uniqueArr = []; arr.forEach(item => { if (!uniqueArr.includes(item)) { uniqueArr.push(item); } });
1
2
3
4
5
6
7使用 reduce
对数组进行遍历,如果结果数组中已经包含当前元素,则不添加,否则添加到结果数组中。
const arr = [1, 2, 2, 3, 3, 4, 5, 5]; const uniqueArr = arr.reduce((acc, curr) => { if (!acc.includes(curr)) { acc.push(curr); } return acc; }, []);
1
2
3
4
5
6
7使用 filter
对数组进行筛选,只保留第一次出现的元素。
const arr = [1, 2, 2, 3, 3, 4, 5, 5]; const uniqueArr = arr.filter((item, index, self) => { return self.indexOf(item) === index; });
1
2
3
4获取对象的 key 值
Object.keys() 方法
const obj = { a: 1, b: 2, c: 3 }; const keys = Object.keys(obj); console.log(keys); // ['a', 'b', 'c']
1
2
3for...in 循环
const obj = { a: 1, b: 2, c: 3 }; for (let key in obj) { console.log(key); // 'a', 'b', 'c' }
1
2
3
4Reflect.ownKeys() 方法
const obj = { a: 1, b: 2, c: 3 }; const keys = Reflect.ownKeys(obj); console.log(keys); // ['a', 'b', 'c']
1
2
3
实现页面中的图表数据实时更新
- 设置定时器或使用 WebSocket:在页面加载或数据更新后,使用定时器/轮询(
setInterval
)或 WebSocket 来定期发送请求获取最新的数据。 - 更新图表数据:在定时器的回调函数中或 WebSocket 接收到新数据时,更新图表的数据。
- 刷新图表:调用 ECharts 提供的刷新图表方法(
echartsInstance.setOption()
)来更新图表的显示
- 设置定时器或使用 WebSocket:在页面加载或数据更新后,使用定时器/轮询(
for循环遍历出0 1 2
使用立即执行函数
我们使用了立即执行函数(IIFE)来创建闭包,并将
i
的值作为参数传递给闭包内的函数。这样每次循环都会创建一个新的闭包,并且在setTimeout
的回调函数中使用闭包内保存的num
值,保证了打印的结果为 0、1、2。function test() { for (var i = 0; i < 3; i++) { // 使用立即执行函数,创建一个闭包并传入 i 的值 (function (num) { setTimeout(() => { console.log(num); }, 0); })(i); } } test();
1
2
3
4
5
6
7
8
9
10
11
12修改关键字
由于
let
创建的变量具有块级作用域,每次循环都会创建一个新的变量i
,因此在setTimeout
的回调函数中使用的i
值是每次循环时的当前值。function test() { for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 0); } } test();
1
2
3
4
5
6
7
8
9
箭头函数与普通函数的区别
- 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承
this
。 - 箭头函数继承而来的this指向永远不变
- .call()/.apply()/.bind()无法改变箭头函数中this的指向
- 箭头函数不能作为构造函数使用
- 箭头函数没有自己的arguments
- 箭头函数没有原型prototype
- 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承
在 JavaScript 中,实现继承的方式
- 原型链继承是 JavaScript 中最基本的继承方式。**通过将父类的实例作为子类的原型,子类可以继承父类的属性和方法。**但是这种方式有一个缺点,就是所有子类实例共享同一个父类实例,可能会导致属性共享和修改的问题。
- 构造函数继承是通过在子类的构造函数中调用父类的构造函数来实现继承。通过这种方式,子类可以拥有独立的属性,避免了原型链继承的属性共享问题。
- 原型式继承是基于现有对象创建新对象的一种继承方式。它通过 Object.create() 方法创建一个新对象,并指定现有对象作为新对象的原型。
- ES6 中实现继承的使用
class
和extends
实现继承,解决了原型链继承和构造函数继承的问题,子类可以继承父类的属性和方法,而且子类实例也不会共享同一个父类实例。class
语法只是一种语法糖,本质上还是基于原型链的继承方式。
# ts
type和interface有什么区别
相同点
- 用来描述对象或函数的类型,只是语法不同
- interface 用 extends 来实现扩展,type 使用 & 实现扩展
不同点
- type不仅可以为对象指定类型,实际上可以为任意类型指定别名/联合类型/元组
- interface能够合并声明
- ts怎么取函数的返回类型
在 TypeScript 中,要获取函数返回值的类型,可以使用
ReturnType<T>
工具类型。这个工具类型可以从函数类型中提取返回值类型。function add(a: number, b: number): number { return a + b; } type AddReturnType = ReturnType<typeof add>; // AddReturnType 的类型将被推断为 number
1
2
3
4
5
6在 TypeScript 中,常见的属性
number:表示数字类型。
string:表示字符串类型。
boolean:表示布尔类型,值为
true
或false
。Array:表示数组类型,可以使用
Type[]
或者Array<Type>
两种形式。object:表示对象类型。
any:表示任意类型,可以赋予任何类型的值。
void:表示没有返回值的函数类型。
null 和 undefined:表示 null 和 undefined 类型。
Tuple:表示元组类型,可以包含多种类型的值。
enum:表示枚举类型,用于定义一组命名的常数。
union:表示联合类型,可以指定多种类型中的一种。
interface:表示接口类型,用于定义对象的结构。
# uniapp
- uniapp 路由跳转方式有什么?
- 通过
uni.navigateTo
方法进行跳转,该方法会将目标页面压入栈中,可以通过返回键返回上一个页面。 - 通过
uni.redirectTo
方法进行跳转,该方法会关闭当前页面,打开目标页面。 - 通过
uni.reLaunch
方法进行跳转,该方法会关闭所有页面,打开目标页面。 - 通过
uni.switchTab
方法进行跳转,该方法只能用于跳转到 tabBar 页面,并且会关闭所有非 tabBar 页面。 - 通过
uni.navigateBack
方法进行跳转,该方法用于返回上一个页面。
- 通过
- request异步改同步
- 如果非要将
uni.request
异步请求改为同步请求,可以考虑使用async/await
或者Promise
来实现。 - 使用时可以直接调用
requestSync
函数,并使用await
关键字等待请求结果的返回:
- 如果非要将
- 组件生命周期
- beforeCreate在实例初始化之前被调用。
- created在实例创建完成后被立即调用。
- beforeMount在挂载开始之前被调用。
- mounted挂载到实例上去之后调用。
- beforeUpdate数据更新时调用,发生在虚拟 DOM 打补丁之前。
- updated由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
- beforeDestroy实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyedVue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
- 页面生命周期
- onInit
- onLoad
- onShow
- onReady
- onHide
- onUnload
- onResize
- onPullDownRefresh
- onReachBottom
- onTabItemTap
- onShareAppMessage
- onPageScroll
- onNavigationBarButtonTap
- onBackPress
- onNavigationBarSearchInputChanged
- onNavigationBarSearchInputConfirmed
- onNavigationBarSearchInputClicked
- onShareTimeline
- onAddToFavorites
# CSS
在 CSS 中实现居中可以有多种方式?
- 使用
text-align: center
居中内联元素。 - 使用
margin: auto
居中块级元素,前提是该元素有固定宽度。 - 使用
position: absolute
和transform: translate
居中任意元素。通过设置top: 50%
和left: 50%
将元素的左上角移动到父元素的中心,然后使用transform: translate(-50%, -50%)
将元素的中心与父元素的中心对齐。 - 使用 Flexbox 布局,在父容器上设置
display: flex
,并设置justify-content
和align-items
属性实现居中。 - 使用 Grid 布局,在父容器上设置
display: grid
,并设置place-items: center
属性实现居中。
- 使用
不改变child1和child2的z-index,请让child2覆盖在child1上面
为box1添加
transform: translateZ(1px);
<template> <div class="box1"> <div class="child1">child1</div> </div> <div class="box2"> <div class="child2">child2</div> </div> </template> <style scoped> .box1,.box2{ position: relative; z-index: auto; } .child1{ width: 200px; height: 100px; background: #168bf5; position: absolute; top: 0; left: 0; z-index: 2; } .child2{ width: 100px; height: 200px; background: #32c292; position: absolute; top: 0; left: 0; z-index: 1; } </style>
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请描述一下盒子模式,并如何实现对盒子模式的切换
所有的网页元素都看成一个盒子,它具有: content,padding,border,margin (opens new window) 四个属性,这就是盒子模型。
# 盒子模型有几种模式?
1、W3C标准盒子模型 2、IE盒子模型
标准模式下,一个块的宽度 = width+padding(内边距)+border(边框)+margin(外边距) 怪异模式下,一个块的宽度 = width+margin(外边距) (即怪异模式下,width包含了border以及padding)
box-sizing:content-box; 将采用标准模式的盒子模型标准
box-sizing:border-box; 将采用怪异模式的盒子模型标准
请描述css对页面元素进行隐藏的方式和特性
display属性
display: none;
visibility属性
visibility: hidden;
opacity属性
opacity: @;
position和z-index属性
clip属性
position: absolute;clip: rect(@,9,@,@);
父元素高度塌陷是什么,如何解决?
清除浮动
.parent{ overflow: hidden; } /*或*/ .parent{ display: table; clear: both; }
1
2
3
4
5
6
7
8
9使用伪元素清除浮动
.parent::after{ content:''; display: block; clear: both; }
1
2
3
4
5使用BFC
.parent{ overflow: auto; display: flow-root;/*或使用display: block;*/ }
1
2
3
4
如何用 CSS 实现一个三角形
利用盒模型的
border
属性上下左右边框交界处会呈现出平滑的斜线这个特点,通过设置不同的上下左右边框宽度或者颜色即可得到三角形或者梯形。div{ width: 0; height: 0; border: 10px solid red; border-top-color: transparent; border-left-color: transparent; border-right-color: transparent; }
1
2
3
4
5
6
7
8
# 计算机网络
TCP/IP TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是互联网相关的基础协议,由TCP和IP两部分组成。其中,TCP负责提供可靠的数据传输,而IP负责在网络中寻址和路由。
TCP/IP协议簇是互联网的基础,它定义了计算机之间的通信规则,包括了应用层、传输层、网络层和数据链路层四个层次。
应用层主要定义了应用程序之间的通信规则,如HTTP、FTP、SMTP等。
传输层主要负责数据的传输,保证数据的可靠性和完整性,常用的协议有TCP和UDP。
网络层主要负责网络寻址和路由,如IP协议。
数据链路层主要负责物理层面上的数据传输,如以太网协议。
通过TCP/IP协议簇,不同的计算机和设备可以在互联网上进行数据通信,实现了信息的全球性传输。
http 请求返回状态码 204 表示请求是成功的处理,但没有返回实体内容
https和http的区别
- 安全性:
- HTTP:是一种不安全的协议,数据传输是明文的,容易被攻击者窃听和篡改。
- HTTPS:是一种安全的协议,通过使用 SSL/TLS 加密技术,对数据进行加密和认证,保证数据传输的安全性和完整性。
- 加密:
- HTTP:不对数据进行加密,数据以明文形式传输。
- HTTPS:使用 SSL/TLS 加密技术,将数据加密后再进行传输,使得数据无法被窃听和破解。
- 默认端口:
- HTTP:默认端口是 80。
- HTTPS:默认端口是 443。
- 证书和身份验证:
- HTTP:不涉及证书和身份验证。
- HTTPS:需要使用 SSL/TLS 证书来对服务器进行身份验证,并确保数据传输的安全性。
- SEO 影响:
- HTTP:对搜索引擎优化(SEO)更友好,搜索引擎更容易抓取和索引 HTTP 网页。
- HTTPS:由于安全性的考虑,搜索引擎越来越倾向于将 HTTPS 网页作为排名因素,并给予更高的优先级。
总结来说,HTTPS 提供了更高的安全性和数据保护,适用于需要保护敏感信息的网站和应用程序,如电子商务网站、银行网站等。而 HTTP 适用于对安全性要求不高的网站或无需保护敏感信息的场景。
在实际开发中,对于需要传输敏感数据或保护用户隐私的网站和应用,强烈建议使用 HTTPS 来确保数据的安全和完整性。
- 安全性:
当你在浏览器的地址栏中输入一个 URL会发生什么
- URL 解析: 浏览器会解析输入的 URL,将其分解为以下几个部分:协议(例如 HTTP、HTTPS)、主机名、端口号(如果有)、路径(指定请求的资源路径)、查询参数附加在(URL 后面的键值对参数)和片段标识符(指定文档内的特定位置)。
- DNS 查询: 浏览器将解析后的主机名发送给 DNS(域名系统)服务器,以获取主机的 IP 地址。DNS 解析将域名转换为相应的 IP 地址,以便浏览器能够与主机建立连接。
- 建立连接: 浏览器使用解析后的 IP 地址和端口号(如果指定了端口号)与服务器建立网络连接。通常使用 TCP(传输控制协议)作为传输协议,建立可靠的连接。
- 发送请求: 浏览器向服务器发送一个 HTTP 请求,其中包含请求方法(GET、POST 等)、请求头(包含浏览器和请求的相关信息)和请求体(对于 POST 请求,可能包含数据)。
- 服务器处理请求: 服务器接收到浏览器发送的请求后,根据请求的路径和其他参数来处理请求。服务器可能会执行相应的处理逻辑,读取数据库、生成动态内容等,并返回相应的响应。
- 接收响应: 浏览器接收到服务器发送的响应,响应包括状态码、响应头和响应体。常见的状态码有 200 表示成功、404 表示资源未找到等。
- 渲染页面: 浏览器根据接收到的响应内容进行页面的渲染和呈现,解析 HTML、CSS 和 JavaScript,并将其显示在浏览器窗口中。浏览器会按照指定的渲染流程处理页面元素、布局和样式,最终将页面呈现给用户。
- 关闭连接: 当页面渲染完成后,浏览器和服务器之间的连接会被关闭,释放网络资源。
# 构建工具
Webpack 打包速度提升
- 减少打包文件的体积:通过使用代码分割(Code Splitting)和懒加载(Lazy Loading)等技术,将代码拆分成多个小模块,按需加载,减少初始加载的文件体积,提高页面的响应速度。
- 配置合理的模块解析规则:在 Webpack 的配置中,可以通过配置 resolve.extensions 和 resolve.alias 来优化模块解析速度。将常用的文件类型设置为 extensions,配置别名可以加快模块查找的速度。
- 使用缓存和持久化缓存:Webpack 提供了缓存机制,可以通过配置 cache 和 cacheLoader 来启用缓存,减少重复的构建过程。另外,可以使用持久化缓存,例如使用 babel-loader 的缓存选项,将转译后的文件缓存起来,避免每次重新转译。
- 使用多进程/多实例构建:通过配置 parallel-webpack 或者 HappyPack 插件等,可以启用多进程/多实例构建,利用多核 CPU 并行处理任务,提高构建速度。
- 优化 loader 和插件配置:合理配置 loader 和插件选项,避免不必要的处理和转换。例如,针对特定的文件类型,使用更轻量的 loader 或者排除一些不必要的目录。
- 使用 Tree Shaking:通过配置 Webpack 的 optimization.usedExports 选项,可以启用 Tree Shaking,去除未使用的代码,减少最终打包文件的体积。
- 使用压缩工具:在生产环境下,使用压缩工具(例如 UglifyJS、Terser)对打包后的代码进行压缩和混淆,减小文件体积。
- 开启缓存组:对于使用了大量第三方库的项目,可以通过配置 optimization.splitChunks.cacheGroups 来开启缓存组,将公共模块抽离出来,避免重复打包。
Webpack图片怎么打包
在 Webpack 中,处理图片资源的打包可以通过
file-loader
或url-loader
来实现。这两个 loader 可以将图片文件转换成打包后的文件,并返回文件的 URL,使得图片能够被正确引用和加载。- 安装
url-loader
和file-loader
:
npm install url-loader file-loader --save-dev
1在 Webpack 配置中添加 loader:
module.exports = { // ...其他配置 module: { rules: [ { test: /\.(png|jpe?g|gif|svg)$/i, use: [ { loader: 'url-loader', options: { limit: 8192, // 图片大小小于 8KB 时,将会转成 base64 编码,否则使用 file-loader name: 'images/[name].[hash:8].[ext]', // 输出的文件路径和名称规则 }, }, ], }, ], }, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20在上面的配置中,我们使用
url-loader
来处理图片资源。当图片大小小于 8KB (limit: 8192
) 时,会将图片转换成 base64 编码,以减少 HTTP 请求,提高加载速度。对于大于 8KB 的图片,url-loader
会使用file-loader
来处理,将图片复制到输出目录中,并返回相应的文件路径。你可以根据自己的需求,调整
limit
参数和name
参数来控制打包后图片的处理方式和输出路径。
- 安装
# 小程序
正式发布后,新旧版本有冲突,缓存何时会被清除?
在旧版代码里配置发现更新重启小程序更新的话,新版本上线24小时内用户第二次打开小程序(冷启动)时会更成新版本。24小时以后打开时会先更新最新版本再打开。使用这个方法进行UpdateManager | 微信开放文档 (qq.com) (opens new window)处理
const updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate(function (res) { // 请求完新版本信息的回调 console.log(res.hasUpdate) }) updateManager.onUpdateReady(function () { wx.showModal({ title: '更新提示', content: '新版本已经准备好,是否重启应用?', success: function (res) { if (res.confirm) { // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 updateManager.applyUpdate() } } }) }) updateManager.onUpdateFailed(function () { // 新版本下载失败 })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23小程序性能优化
- 减少 HTTP 请求:合并和压缩资源文件,减少网络请求次数,提高加载速度。
- 图片优化:使用适当的图片格式,压缩图片大小,使用懒加载技术加载图片,减少首屏加载时间。
- 页面渲染优化:避免使用过多的 DOM 节点和样式,减少页面复杂度,提高渲染速度。
- 脚本优化:避免使用过多的全局变量和闭包,优化 JavaScript 代码,减少不必要的计算和重复操作。
- 数据缓存:合理使用小程序的缓存机制,将重要数据缓存到本地,减少网络请求。
- 使用合适的组件和 API:选择适合的小程序组件和 API,避免使用过多的无效功能,提高性能。
- 避免频繁的数据更新和渲染:减少页面中频繁的数据更新和渲染操作,尽量使用批量更新的方式进行操作。
- 分包加载:对于较大的小程序,可以将页面和资源进行分包加载,按需加载,减少首次加载时间。
- 使用 Web Worker:对于一些复杂的计算任务,可以考虑使用 Web Worker 进行异步处理,避免阻塞主线程。
- 定期检查性能:使用性能分析工具进行定期检查和优化,查找性能瓶颈并针对性地进行优化。
# 其他
- 学习前端新技术怎么学
- 先去看看官方文档
- 如果同一个技术有多种实现的时候
- 去github看看star
- 看看stack overflow对这些实现的评价
- 实践是学习新技术的关键。尝试在自己的项目中应用所学的知识
- 加入相关的技术社区,如论坛、社交媒体群组、开发者社区等。与其他开发者交流和分享经验,参与讨论,解答问题。
- 优缺点
- 具备持续学习的意识
- 与团队成员合作,具备良好的沟通和协作能力
- 目前处于初级阶段,可能在某些特定的技术或领域上还有待提升,需要更多的学习和实践。