import{_ as s,o as n,c as a,S as l}from"./chunks/framework.04e6e156.js";const A=JSON.parse('{"title":"【快手】深入理解前端面试题","description":"","frontmatter":{},"headers":[],"relativePath":"article/【快手】深入理解前端面试题.md","filePath":"article/【快手】深入理解前端面试题.md","lastUpdated":1695573785000}'),p={name:"article/【快手】深入理解前端面试题.md"},o=l(`
beforeCreate created beforeMount mounted
beforeUpdate updated
beforeDestory destoryed
Vue3移除了beforeCreate
created
两个声明周期钩子,这是因为setup发生在开始创建组件之前,在beforeCreate
和created
之前执行
可以在setup中使用的生命周期函数:onMounted
onUpdated
onUnmounted
onBeforeUpdate
这几个,
越早越好,一般是放在created
或onMounted
或者setup
中
created
(vue2) 此时组件内的基本数据已经创建好,组件的模板结构尚未生成mounted
(vue2) onMounted
(vue3) 组件挂载到DOM树上,可以获取到DOMsetup
(vue3) 时机要早于beforeCreated
和created
所以在setup中发起网络请求也可以setup
的执行要早于beforeCreated
和created
,可以认为相当于这两个生命周期
全部使用Object.defineProperty()
中的set与get函数
ref
使用的是Object.defineProperty()
,而reactive
使用的是Proxy
Proxy
可以直接深度代理一个对象,通过设置handler中的捕获器可以对对象创建一个代理,将各种行为监听并且同步到对象本身上
const obj = {
name: 'Ziu',
age: 18
}
const proxy = new Proxy(obj, {
set: function (target, key, newVal) {
console.log(\`监听: \${key} 设置 \${newVal}\`)
target[key] = newVal
},
get: function (target, key) {
console.log(\`监听: \${key} 获取\`)
return target[key]
}
})
监听数据的角度
defineproperty
只能监听某个属性而不能监听整个对象。proxy
不用设置具体属性,直接监听整个对象。defineproperty
监听需要知道是哪个对象的哪个属性,而proxy
只需要知道哪个对象就可以了。也就是会省去for in
循环提高了效率。监听对原对象的影响
defineproperty
是通过在原对象身上新增或修改属性增加描述符的方式实现的监听效果,一定会修改原数据。proxy
只是原对象的代理,proxy
会返回一个代理对象不会在原对象上进行改动,对原数据无污染。实现对数组的监听
length
的特殊性 (length 的描述符configurable 和 enumerable 为 false,并且妄图修改 configurable 为 True 的话 js 会直接报错:VM305:1 Uncaught TypeError: Cannot redefine property: length)
defineproperty
无法监听数组长度变化, Vue
只能通过重写数组方法的方式变现达成监听的效果,光重写数组方法还是不能解决修改数组下标时监听的问题,只能再使用自定义的$set
的方式proxy
因为自身特性,是创建新的代理对象而不是在原数据身上监听属性,对代理对象进行操作时,所有的操作都会被捕捉,包括数组的方法和length
操作,再不需要重写数组方法和自定义set
函数了。(代码示例在下方)4. 监听的范围
defineproperty
只能监听到value
的 get set
变化。proxy
可以监听除 [[getOwnPropertyNames]]
以外所有JS
的对象操作。监听的范围更大更全面。ownKeys、deleteProperty、has
等是 Object.defineProperty
不具备的。Object.defineProperty
只能遍历对象属性直接修改;兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平。
Object.defineProperty
只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Object.defineProperty
不能监听数组。是通过重写数据的那7个可以改变数据的方法来对数组进行监听的。Object.defineProperty
也不能对 es6
新产生的 Map
,Set
这些数据结构做出监听。Object.defineProperty
也不能监听新增和删除操作,通过 Vue.set()
和 Vue.delete
来实现响应式的。Object.defineProperty()
的get()和set().value
获取const obj1 = ref({ name: 'Ziu', age: 18 })
console.log(obj1) // RefImpl
console.log(obj1.value) // Proxy
const obj2 = reactive({
name: 'Kobe',
age: 19
})
console.log(obj2) // Proxy
ref() reactive()
Vue3 对比 Vue2.x 差异性、注意点、整体梳理,与React hook比又如何?
不存在变量提升 暂时性死区
ES6 明确规定,如果区块中存在
let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
var tmp = 123;
if (true) {
tmp = 'abc';
let tmp;
}
console.log('tmp =', tmp);
pending fulfilled rejected
.resolve .reject .all .race .any
.then .catch .finally .allSettled
.then
方法两个入参 一个是上一个Promise对象resolved的回调 另一个是rejected的回调,返回一个新的Promise对象.then
传入的回调函数会被加入微任务队列.catch
本质上是只有错误处理函数的.then
传入的都是一个可迭代对象(iterable),迭代器,类似Array
function testPromise() {
return new Promise((resolve, reject) => {
console.log('1');
resolve(2);
reject(3);
console.log('4');
}).then(res => {
console.log('res = ', res);
}, err => {
console.log('err =', err);
});
}
testPromise();
输出结果:1 4 res = 2
执行resolve
之后,当前Promise的状态变为fulfilled
不再改变,即使后面继续调用了reject
,后续代码继续执行输出'4'
,随后执行微任务中的.then
,输出res = 2
function selfPromiseAll(iterable) {
return new Promise((resolve, reject) => {
const promises = Array.from(iterable) // 将可迭代对象转为数组
const result = [] // 保存结果
let count = 0 // 记录是否所有promise都执行完毕
// 并发执行每一个promise
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i])
.then((res) => {
result[i] = res // 将结果按顺序存入result
count++
// 保证每一个Promise都执行完毕后再resolve
if (count === promises.length) {
resolve(result)
}
})
.catch((err) => reject(err)) // 只要有reject 外部Promise直接reject
}
})
}
cookie | localStorage | sessionStorage | |
---|---|---|---|
大小 | 4Kb | 5MB | 5MB |
兼容 | H4/H5 | H5 | H5 |
访问 | 任何窗口 | 任何窗口 | 同一窗口 |
有效期 | 手动设置 | 无 | 窗口关闭 |
存储位置 | 浏览器和服务器 | 浏览器 | 浏览器 |
与请求一起发送 | 是 | 否 | 否 |
语法 | 复杂 | 简单 | 简单 |
?key1=value1;key2=value2
组成的,可以通过encodeURIComponent()
来保证它不包含任何逗号、分号或空格(cookie值中禁止使用这些值).path domain max-age expires secure
localStorage.setItem('key', value)
localStorage.getItem('key')
localStorage.removeItem('key')
localStorage.clear()
根据同源策略的限制,在端口,域名,协议这三者一个或者多个不同的情况下,就会出现跨域限制,请求发送到了服务器并且服务器也响应了数据,但是浏览器不会为你展示出来。
但是,<script>
标签访问时,可以跨越这些同源策略限制,但只能使用 GET
方法:
<script>
function jsonCallback(data) {
console.log(data)
}
</script>
<script src="https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg"></script>
<script>
标签返回的是一段由函数jsonCallback
包裹的数据,这样在页面中就可以拿到跨域的数据了
主线程 宏任务 微任务 EventTable EventQueue
队列检查 函数执行栈 优先级 常见的宏任务/微任务
setTimeout(() => {
console.log('time1')
Promise.resolve()
.then(() => {
console.log('promise1')
process.nextTick(() => {
console.log('next tick1')
})
})
.catch((err) => {
console.log(err)
})
})
setTimeout(() => {
console.log('time2')
new Promise((res) => {
res()
console.log('promise2')
process.nextTick(() => {
console.log('next tick2')
})
}).then(() => {
console.log('promise3')
})
})
输出顺序: time1 promise1 next tick1 time2 promise2 next tick2 promise3
flex: 1
含义:root
选择器定义变量class
切换颜色<style>
:root {
--text-color: #1f1f1f;
--bg-color: #f5f5f5;
--primary-color: #1890ff;
}
:root.dark {
--text-color: #f5f5f5;
--bg-color: #1f1f1f;
--primary-color: #1890ff;
}
.card {
color: var(--text-color);
background-color: var(--bg-color);
padding: 10px;
max-width: 300px;
transition: all 0.2s;
}
.card h2 {
color: var(--primary-color);
}
</style>
<div>
<button id="dark-mode">Toggle Dark Mode</button>
<div class="card">
<h2>跨站脚本攻击(XSS)</h2>
<p>
Cross-Site
Scripting(跨站脚本攻击)简称XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如
Cookie、SessionID 等,进而危害数据安全。 为了和 CSS 区分,这里把攻击的第一个字母改成了
X,于是叫做 XSS。
</p>
</div>
</div>
<script>
const root = document.querySelector('html')
const btn = document.querySelector('#dark-mode')
btn.addEventListener('click', () => {
root.classList.toggle('dark')
})
</script>
从数组中找到两数之和为目标值的数字对数 不允许重复使用数字
[1, 2, 3, 4, 4], 5
结果为 2
[1, 1, 2, 3, 4, 4], 5
结果为 3
/**
* 从数组中找到两数之和为目标值的数字对数 不允许重复使用数字
* @param {number[]} nums 数组
* @param {number} target 目标值
* @returns {number} 次数
*/
function twoSum(nums, target) {
// 边界情况
if (nums.length < 2) return 0
let count = 0
const map = new Map()
for (let i = 0; i < nums.length; i++) {
const num = nums[i]
const diff = target - num
const value = map.get(diff) // undefined | number
// 如果找到了与diff相等的num
if (value !== undefined && value > 0) {
count++
map.set(diff, value - 1) // 剩余数字个数-1
} else {
// 未找到 则将num设为键 值为出现次数
const count = map.get(num) === undefined ? 1 : map.get(num) + 1
map.set(num, count)
}
}
return count
}
console.log(twoSum([1, 2, 3, 4, 4], 5)) // 2
console.log(twoSum([1, 1, 2, 3, 4, 4], 5)) // 3