1. 原型链
2. 闭包
3. promise
- promise是什么
- 补全代码(手写)
- promise
4. Set
5. Map
6. Map结构和对象object有什么区别
- 键的类型
// Map 允许任何类型的键,包括对象、函数、原始类型(数值、字符串、布尔值等)。
const map = new Map();
const objKey = {};
const funcKey = function() {};
map.set(objKey, 'value associated with objKey');
map.set(funcKey, 'value associated with funcKey');
map.set(123, 'value associated with number 123');
console.log(map.get(objKey)); // 输出: 'value associated with objKey'
console.log(map.get(funcKey)); // 输出: 'value associated with funcKey'
console.log(map.get(123)); // 输出: 'value associated with number 123'
// Object: 对象的键名必须是字符串(或能转换为字符串的值)或符号。非字符串类型的键会被强制转换为字符串。
const obj = {};
const numKey = 123;
const objKey = {};
const funcKey = function() {};
obj[numKey] = 'value associated with number 123';
obj[objKey] = 'value associated with objKey';
obj[funcKey] = 'value associated with funcKey';
console.log(obj['123']); // 输出: 'value associated with number 123'
console.log(obj['[object Object]']); // 输出: 'value associated with funcKey'
console.log(obj['function() {}']); // 输出: undefined (结果可能会根据函数的字符串化不同)
- 保留的顺序
// Map 保留键值对的插入顺序,可以按顺序迭代。
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
for (let [key, value] of map) {
console.log(key, value);
}
// 输出: a 1, b 2, c 3
// Object: 对象属性的顺序主要基于它们的插入顺序,但整数键会优先排序。一般来说,不要依赖对象属性的顺序。
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key, obj[key]);
}
// 输出: a 1, b 2, c 3(但不保证所有 JavaScript 引擎都保持这一顺序)
// 遍历一个对象的属性时,它们通常会保持插入的顺序,但这并不是由规范严格保证的。尤其是对于整数键,它们可能优先于字符串键,以排序顺序的形式出现
const obj = { a: 1, 1: 'one', b: 2, 3: 'three', c: 3 };
for (let key in obj) {
console.log(key, obj[key]);
}
// 输出顺序通常是:1 'one', 3 'three', 'a' 1, 'b' 2, 'c' 3 // 整数键优先于字符串
// 而不是:'a' 1, '1' 'one', 'b' 2, '3' 'three', 'c' 3
- 迭代
// Map 提供专用的迭代器,可以轻松迭代键、值或键值对。
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
for (let key of map.keys()) {
console.log(key);
}
// 输出: a, b, c
for (let value of map.values()) {
console.log(value);
}
// 输出: 1, 2, 3
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 输出: a 1, b 2, c 3
// object没有内置的迭代器。通常需要使用 `for...in` 或 `Object.keys`、`Object.values`、`Object.entries`
const obj = { a: 1, b: 2, c: 3 };
// 使用 for...in 迭代键
for (let key in obj) {
console.log(key);
}
// 输出: a, b, c(不保证顺序)
// 使用 Object.keys 迭代键
Object.keys(obj).forEach(key => {
console.log(key);
});
// 输出: a, b, c
// 使用 Object.values 迭代值
Object.values(obj).forEach(value => {
console.log(value);
});
// 输出: 1, 2, 3
// 使用 Object.entries 迭代键值对
Object.entries(obj).forEach(([key, value]) => {
console.log(key, value);
});
// 输出: a 1, b 2, c 3
- 性能
// Map 专为频繁增加和删除键值对的操作优化,通常在这些操作中性能优于对象。
// object在大部分情况下性能也很好,但在涉及频繁的键值对增加和删除时可能不如 Map 高效。
- 方法和属性
// Map 提供一系列专用方法,如 `set`、`get`、`has`、`delete` 和 `clear`
// Map 可以使用 `size` 属性获取键值对的数量
const map = new Map();
map.set('a', 1);
map.set('b', 2);
console.log(map.get('a')); // 输出: 1
console.log(map.has('b')); // 输出: true
console.log(map.size); // 输出: 2
map.delete('a');
console.log(map.get('a')); // 输出: undefined
map.clear();
console.log(map.size); // 输出: 0
// object 没有像 Map 那样的专用方法。对操作进行键值对的增加、删除、检查等操作,需要使用运算符和手动实现
const obj = { a: 1, b: 2 };
console.log(obj['a']); // 输出: 1
console.log('b' in obj); // 输出: true
delete obj['a'];
console.log(obj['a']); // 输出: undefined
console.log(Object.keys(obj).length); // 输出: 1
// 清空对象属性
Object.keys(obj).forEach(key => delete obj[key]);
console.log(Object.keys(obj).length); // 输出: 0
- 何时使用 Map 或 Object
- 使用 Map
- 当你需要存储键值对并需要键是非字符串类型(如对象或函数)。
- 当你需要频繁添加和删除键值对以确保操作性能。
- 当你需要保留键值对的插入顺序。
- 使用 Object
- 当你的键是字符串或符号,并且你更习惯对象的字面量语法。
- 当你具有简单的键值对并且没有特殊的性能或顺序需求。
7. computed和watch区别
// computed概念 :是一种基于响应式依赖缓存的属性,只有当依赖的数据发生变化时才会重新计算其值。
// computed适用场景: 用于根据现有的数据计算出其他数据,且这些计算过程需要缓存以优化性能。
// computed缓存:计算属性的值会被缓存,直到它的依赖数据发生变化。也就是说,如果计算属性依赖的数据没有变化,多次访问计算属性的值会直接返回缓存中的值,不会重新计算。
// computed使用场景:适用于较重计算且需要缓存结果的场景。
// watch概念:是一种在数据变化时执行特定操作的方法。
// watch使用场景: 用于在数据变化时执行异步操作或执行带有副作用的操作(如 Ajax 请求、修改 DOM)。
// watch无缓存:侦听器的回调函数不会缓存,每次监听的数据变化都会执行回调函数。
// watch使用场景:适用于需要在数据变化时执行复杂逻辑或异步操作的场景。
8. computed和methods区别
9. localStorage,sessionStorage,cookie区别
10. vuex存储数据,强制刷新页面,数据会丢失,怎么处理?
- 保存在localstorage中
// 创建 Vuex Store:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
data: null,
},
mutations: {
setData(state, payload) {
state.data = payload;
// 保存状态到localStorage
localStorage.setItem('vuexState', JSON.stringify(state));
},
},
actions: {
loadData({ commit }) {
const savedState = localStorage.getItem('vuexState');
if (savedState) {
commit('setData', JSON.parse(savedState).data);
}
},
},
});
export default store;
// 加载数据:
// main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';
Vue.config.productionTip = false;
new Vue({
store,
created() {
this.$store.dispatch('loadData');
},
render: h => h(App),
}).$mount('#app');
// 使用数据:
// ExampleComponent.vue
<template>
<div>
<p>Data: {{ data }}</p>
<button @click="setData">Set Data</button>
</div>
</template>
<script>
export default {
computed: {
data() {
return this.$store.state.data;
},
},
methods: {
setData() {
this.$store.commit('setData', 'new data');
},
},
};
</script>
- 使用 vuex-persistedstate 插件
// 插件,可以更方便地实现 Vuex 状态的持久化。它会自动将 Vuex 状态存储到 `localStorage` 或 `sessionStorage`,并在页面刷新时恢复状态。
// 安装: npm install vuex-persistedstate
// 引入
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
data: null,
},
mutations: {
setData(state, payload) {
state.data = payload;
},
},
plugins: [createPersistedState()],
});
export default store;
// 使用数据
// ExampleComponent.vue
<template>
<div>
<p>Data: {{ data }}</p>
<button @click="setData">Set Data</button>
</div>
</template>
<script>
export default {
computed: {
data() {
return this.$store.state.data;
},
},
methods: {
setData() {
this.$store.commit('setData', 'new data');
},
},
};
</script>
- vuex-persist
// 安装 npm install vuex-persist
//引入
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
import VuexPersist from 'vuex-persist';
Vue.use(Vuex);
const vuexLocalStorage = new VuexPersist({
key: 'my-app',
storage: window.localStorage,
});
const store = new Vuex.Store({
state: {
data: null,
},
mutations: {
setData(state, payload) {
state.data = payload;
},
},
plugins: [vuexLocalStorage.plugin],
});
export default store;
// 使用
// ExampleComponent.vue
<template>
<div>
<p>Data: {{ data }}</p>
<button @click="setData">Set Data</button>
</div>
</template>
<script>
export default {
computed: {
data() {
return this.$store.state.data;
},
},
methods: {
setData() {
this.$store.commit('setData', 'new data');
},
},
};
</script>
11. 1-100生成,不使用for和while
12. 事件循环机制
- 执行顺序
- 宏任务除了setTimeout,setInterval还有什么
- promise是什么任务?
-
Promise 构造函数中的代码:这是同步执行的。
- promise.then是什么任务?
-
Promise的回调(then,catch,finally) :这是通过微任务(Microtasks)机制异步执行的
13. vue2和vue3区别
- vue2和vue3的区别
-
Vue在初始化数据时,会使用
Object.defineProperty重新定义data中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。 -
Vue3.x改用
Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
关键差异
-
数据劫持机制:
- Vue 2 使用
Object.defineProperty,这只能劫持对象的已有属性,无法拦截其他操作(例如对数组的操作)。 - Vue 3 使用
Proxy,可以动态拦截和管理几乎所有的操作,具有更高的灵活性和性能。
- Vue 2 使用
-
依赖追踪和更新机制:
- Vue 2 采用传统的发布-订阅模式,将依赖手动添加到订阅者列表中,然后在变化时通知订阅者。
- Vue 3 通过
effect和响应式依赖追踪系统自动管理依赖关系,使得系统更加高效和智能。
-
性能:
- Vue 3 基于
Proxy的实现减少了很多 Vue 2 中的性能瓶颈,提供了更高的性能和更好的内存管理。
- Vue 3 基于
-
总结
-
Vue 2:主要基于发布-订阅模式,使用
Object.defineProperty进行数据劫持。 -
Vue 3:引入了基于
Proxy的响应式系统,并采用更为智能和高效的依赖追踪机制。
14. vue2的生命周期
15. 前端如何处理小数进行计算(精度问题)
- 在前端开发中,处理小数进行计算时常常会遇到精度问题。这通常是由于 JavaScript 使用的数值类型是基于 IEEE 754 标准的双精度浮点数,这种表示法有时会导致精度丢失。
// javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果
// 1
function test(arg1, arg2) {
let m = 0;
const s1 = arg1.toFixed(2).toString();
const s2 = arg2.toFixed(2).toString();
try {
m += s1.split('.')[1].length;
} catch (e) {
console.log(e);
}
try {
m += s2.split('.')[1].length;
} catch (e) {
console.log(e);
}
return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m);
}
//2
function accurateMultiplication(arg1, arg2) {
// 将浮点数转换为字符串
const str1 = arg1.toString();
const str2 = arg2.toString();
// 计算两个数的小数位数
const decimalPlaces1 = (str1.split('.')[1] || '').length;
const decimalPlaces2 = (str2.split('.')[1] || '').length;
// 总小数位数
const totalDecimalPlaces = decimalPlaces1 + decimalPlaces2;
// 移除小数点并转换为整数
const int1 = Number(str1.replace('.', ''));
const int2 = Number(str2.replace('.', ''));
// 计算结果并还原小数位置
const result = (int1 * int2) / Math.pow(10, totalDecimalPlaces);
return result;
}
const result = test(0.1, 0.2);
console.log(result); // 输出 0.02
// toFixed可以将数字格式化为指定的小数位数,但要注意它们返回的是字符串,需要在需要进行进一步计算时转换回数值
let num = 0.1 + 0.2;
console.log(num.toFixed(2)); // 输出 "0.30"
// 乘法和除法技巧: 通过将小数转换为整数进行计算,然后再恢复结果,可以避免小数计算的精度问题
let num1 = 0.1;
let num2 = 0.2;
// 将 0.1 和 0.2 转换为整数计算,然后除以 100
let result = (num1 * 10 + num2 * 10) / 10;
console.log(result); // 输出 0.3
- 库:
decimal.js是一个用于高精度浮点数计算的库,能够进行各种准确的数学运算。 - 库:
big.js是一个轻量级的高精度库,适用于需要高精度计算的小数问题。 - 库:
bignumber.js是另一个高精度数值计算库,提供了丰富的 API 以应对各种数学计算场景。
16. vue2视图不更新,怎么处理?
vm.$forceUpdate()是 Vue.js 提供的一个实例方法,可以强制组件重新渲染。一般情况下,不推荐频繁使用这个方法,因为它违反了 Vue 的数据驱动视图原则。不过,在一些特殊情况下或调试过程中,这个方法可以派上用场。
// 例子1
export default {
data() {
return {
user: {
name: 'John Doe',
age: 30
}
};
},
methods: {
updateName(newName) {
this.user.name = newName;
this.$forceUpdate(); // 强制更新视图
}
}
};
// 通过 `updateName` 方法更新 `user.name` 属性后调用 `this.$forceUpdate()` 来强制组件重新渲染。
// 例子2
export default {
data() {
return {
nestedObject: {
level1: {
level2: {
value: 'initial'
}
}
}
};
},
methods: {
updateNestedValue(newValue) {
this.nestedObject.level1.level2.value = newValue;
this.$forceUpdate(); // 强制更新视图
}
}
};
// 深度嵌套的修改
// 当 Vue 未能正确追踪深度嵌套对象的变更时,可以使用 `$forceUpdate()` 进行强制更新
// 例子3
export default {
data() {
return {
user: {
name: 'John Doe'
// age 未初始化
}
};
},
methods: {
addUserAge() {
this.user.age = 30; // 新添加未初始化的属性
this.$forceUpdate(); // 强制更新视图
}
}
};
// 数据未在 data 属性中初始化
// 如果数据未在 `data` 属性中初始化,Vue 不会将其标记为响应式。虽然可以使用 `Vue.set` 或 `this.$set` 添加新属性,但在必要时可以调用 `$forceUpdate()`
// 例子4
methods: {
debugDataUpdate() {
// 调试某数据变化
this.someNonReactiveData = 'some value';
this.$forceUpdate(); // 强制更新以查看视图变化情况
}
}
// Debugging 场景
// 在调试 Vue 应用时,可能会发现某些数据变化并没有引起视图的更新。这时使用 `$forceUpdate()` 可以帮助我们快速验证是否是 Vue 的响应性系统的问题
- 注意事项
- 尽量避免频繁使用:
$forceUpdate违背了 Vue 的设计初衷,即通过数据的响应式机制自动更新视图。频繁使用可能会掩盖潜在的数据管理问题。 - 检查 Vue 响应式机制:在决定使用
$forceUpdate之前,先考虑使用Vue.set或this.$set来确保数据的响应式更新。 - 性能考虑:使用
$forceUpdate强制触发重新渲染可能会影响性能,因为它会迫使 Vue 跳过高效的更新路径,直接更新整个组件树。
- 总结
vm.$forceUpdate()方法应作为解决最后一线希望来处理视图未自动更新的问题。通常我们应当依赖 Vue 的响应性系统,通过正确修改数据和使用 Vue 提供的工具(如Vue.set和this.$set)来确保视图及时更新。但是在某些复杂或调试场景下,$forceUpdate仍然是一个有用的工具。希望通过这些详细的示例和使用场景,你可以更好地理解和应用这个方法
17、打印
function addCount() {
var count = 0;
return function () {
count = count + 1;
console.log(count);
};
}
var fun1 = addCount();
var fun2 = addCount();
fun1(); // 1
fun2(); // 1
fun2(); // 2
fun1(); // 2