即时设计 - 可实时协作的专业 UI 设计工具
即时设计是一款可在线编辑的专业UI设计工具,支持原型、交互、智能动画、切图等功能,满足产品创造和交付的全流程体验。
面试题目源码:前端面经
初始化仓库:git init
远程仓库地址:git clone / git remote add origin
查看关联的远程仓库列表 git remote -v
显示当前工作目录和暂存区的状态 git status
添加到暂存区 git add .
提交本地仓库 git commit -m "提交日志"
提交日志信息 git log
推送远程仓库 git push origin <本地分支名>
创建分支 git branch 分支名称
切换分支 git checkout 分支名称
合并分支 git merge 分支名称
https://juejin.cn/post/7306040473542213644
vue2 使用 Object.defineProperty实现,是 es6 引入的方法,可以通过配置精确添加或修改对象的属性
不能利用 get/set 对额外添加的属性进行监听
不能监听自身数组下标修改元素的变化,直接定义数组可以,但是不常用
vue3 使用 Proxy 实现,能够支持对象添加或修改属性的变化
通过reactive() 函数给每一个对象都包一层 proxy,从而实现对数据的监控
支持改变数组的方法,push、pop、shift、unshift、splice、sort、reverse
Reflect 是 es6 引入的新特性,提供一种新的方式操作对象
使用 WeakMap 作为缓存区防止对象被重复代理,主要是避免内存泄漏,以及隐藏内部实现细节
class PubSub {
constructor() {
this.events = {}
}
subscribe(event, callback) {
if (!this.events[event]) this.events[event] = []
this.events[event].push(callback)
}
unSunscribe(event, callback) {
if (!this.events[event]) return
this.events[event] = this.events[event].filter((cb) => cb !== callback)
}
publish(event, data) {
if (!this.events[event]) return
for (let item of this.events[event]) item(data)
}
}
const pubsub = new PubSub()
function callback1(data) {
console.log('触发订阅事件 1', data)
}
function callback2(data) {
console.log('触发订阅事件 2', data)
}
pubsub.subscribe('myEvent', callback1)
pubsub.subscribe('myEvent', callback2)
pubsub.publish('myEvent', '11111')
/**
* 实现翻转二叉树
* 例如
* a
* / \
* b c
* 转为
* a
* / |
* c b
*/
const invertTree = function (root) {
if (root === null) return root
let temp = root.left
root.left = invertTree(root.right)
root.right = invertTree(temp)
return root
}
const ensure = (output, expect, message) => {
if (JSON.stringify(output) === JSON.stringify(expect)) {
console.log(`${message} ok`)
} else {
console.log(`${message} fail`)
}
}
const test = function () {
const input = {
val: 1,
left: {
val: 2,
left: {
val: 3,
left: null,
right: null,
},
right: {
val: 4,
left: null,
right: null,
},
},
right: {
val: 5,
left: {
val: 6,
left: null,
right: null,
},
right: {
val: 7,
left: null,
right: null,
},
},
}
const expect = {
val: 1,
left: {
val: 5,
left: {
val: 7,
left: null,
right: null,
},
right: {
val: 6,
left: null,
right: null,
},
},
right: {
val: 2,
left: {
val: 4,
left: null,
right: null,
},
right: {
val: 3,
left: null,
right: null,
},
},
}
const output = invertTree(input)
ensure(output, expect, 'test')
}
// 输出 test ok表示测试成功
test()
Promise.myAll = function (promises) {
let result = []
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
Promise.resolve(item)
.then((res) => {
result[index] = res
if (Object.keys(result).length === promises.length) resolve(result)
})
.catch((err) => {
reject(err)
})
})
})
}
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 3000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject(4)
Promise.myAll([p4, p2, p3])
.then((res) => console.log(res))
.catch((err) => console.log(err)) // 2秒后打印 [
const promise = new Promise((resolve, reject) => {})
console.log(promise)
// https://juejin.cn/post/7273693216372916283
console.log('\n --- 一:原生方法 --- \n')
let arr = [1, [2, 3], [4, [5, 6, [7, 8]]]]
console.log(arr.flat()) // 默认解析一层,可以指定参数
console.log(arr.flat(Infinity)) // Infinity完全解析
console.log('\n--- 二、递归解析 ---\n')
function flatten(arr, depth) {
let result = []
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) && depth > 0) {
result = result.concat(flatten(arr[i], depth - 1))
} else {
result.push(arr[i])
}
}
return result
}
console.log(flatten(arr, 1))
console.log(flatten(arr, 2))
console.log(flatten(arr, 3))
console.log('\n--- 三、reduce解析 ---\n')
function reduceFlatten(arr) {
return arr.reduce((acc, cur) => {
if (Array.isArray(cur)) {
return acc.concat(reduceFlatten(cur))
} else {
return acc.concat(cur)
}
}, [])
}
console.log(reduceFlatten(arr))
一面忘记录音了,有一些问题面试后没有回想起来
事件循环(Event Loop)是 JavaScript 中实现异步编程的一种机制。
同步:任务按照次序依次执行,每个任务必须完成后才能执行下一个任务。
异步:异步任务是非阻塞的,可以同时执行多个任务,不需要等待任务的完成。
https://juejin.cn/post/7071861718573383716
总结一下: 函数式编程有两个核心概念。
纯函数带来的意义。
常见请求头:Accept 接受的内容、字符集、编码方式,Content 请求体的类型、长度,缓存机制(强制缓存、协商缓存标识)
代码层面
构建方面
其他
用户体验
概述:几何体由一个个顶点粒子构成,每个粒子包含了位置、uv贴图(3d=>2d坐标对应)等,三个顶点形成一个面,在three.js中。物体由一个个三角形面构成。
BufferGeometry:是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销。目前,three.js的物体都用上了Buffer,可以直接调用,一些顶点可以使用这个。
当WebGL绘制粒子时,WebGL会测试正在绘制的粒子哪个更靠前,在其后面的粒子不会被绘制,在其前面的粒子会被绘制,这被称为深度测试,可以通过alphaTest停用depth testing。
setTimeout(function () {
console.log(1)
}, 0)
new Promise(function (resolve, reject) {
console.log(2)
for (var i = 0; i < 10000; i++) {
if (i === 10) console.log(10)
i == 9999 && resolve()
}
console.log(3)
}).then(function () {
console.log(4)
})
console.log(5)
//2 10 3 5 4 1
function Foo() {
getName = function () {
console.log(1)
}
return this
}
Foo.getName = function () {
console.log(2)
}
Foo.prototype.getName = function () {
console.log(3)
}
var getName = function () {
console.log(4)
}
function getName() {
console.log(5)
}
Foo.getName() //2
getName() //4
Foo().getName() //1
new Foo().getName() //3
var getName
function getName() {
console.log(5)
}
getName = function () {
console.log(4)
}
//node 环境运行报错、let 声明不可用
// 输入:1->2->3->4->5->NULL
// 输出:5->4->3->2->1->NULL
function traverseStr(str) {
const array = str.split('->').slice(0, -1)
return array.reverse().join('->') + '->NULL'
}
const target = '1->2->3->4->5->NULL'
const result = traverseStr(target)
console.log(result)
const target = 'aaabbbcceeff'
function removeStr(target) {
let map = new Map(),
result = []
for (let ch of target) map.set(ch, (map.get(ch) || 0) + 1)
let minCount = Math.min(...map.values())
for (let ch of target) {
if (map.get(ch) !== minCount) result.push(ch)
}
return result.join('')
}
const result = removeStr(target)
console.log(result)