魔方最出名的产品是票星球,平时爱看一些小众宝藏演唱会,所以感觉这产品还是挺活跃的也很想去。
记录一下这次面试里的一些问题,只是部分,并非全部
webAssembly
webAssembly是近年来比较新的前端技术。早在2015年W3C便成立了Wasm社区小组,从2017年开始Wasm相继获得几大浏览器厂商的支持、主流编程语言的支持。
webAssembly的原理
Mozilla开发Emscripten/asm.js 将C++代码编译成js代码时,Google的Chrome团队在试图使用NaCl(Google Native Client)和 PNaCl(Portable NaCl)解决 JavaScript性能问题。Chrome 的解决方案是,Chrome 浏览器可以在沙箱环境中直接执行本地代码。asm.js 和NaCl/PNaC1技术各有优缺点,二者可以取长补短。 Mozilla和Google也看到了这一点,所以从2013年开 始,两个团队就经常交流和合作。直到后来Mozilla和Google决定结合两个项目的长处,合作开发一种基于字节码的技术,WebAssembly就此诞生。
wasm是一种底层汇编语言,能够在PC浏览器和移动浏览器中已接近本地的速度运行。开发者可以使用C/C++/Rust/Go等各种语言进行功能开发,然后通过专用工具emscripten编译到wasm。这个过程完成后就会生成对应的.wasm文件,它能够在浏览器端运行。
webAssembly的使用
WebAssembly可以在Web浏览器中运行。而浏览器主流语言JS也提供了**WebAssembly
**对象来支持相关的操作。
加载
由于.wasm是一个文件,所以理所当然地,我们应该先加载这个文件。
ini
let sourceWasm = fetch('your-wasm-url.wasm')
fetch
是一个异步API,按照常理应该是要等待它的兑现结果再进行下一步操作的。但我们只写到这里,因为WebAssembly
对象提供了两种初始化的方式。
初始化
即在浏览器端编译wasm
1.WebAssembly.instantiateStreaming(source)
source:Response
对象或兑现为 Response 对象的 promise。
这允许我们:
javascript
const {instance, module} = WebAssembly.instantiateStreaming(sourceWasm)
2.WebAssembly.instantiate(sourceBuffer)
sourceBuffer: 包含你想编译的 Wasm 模块的二进制代码的 类型化数组 或 ArrayBuffer
这就需要:
javascript
let sourceBufferWasm = await sourceWasm.arrayBuffer()
const {instance, module} = WebAssembly.instantiate(sourceBufferWasm)
使用
无论通过上述哪种方式来完成初始化都ok,在完成上述工作之后我们只需要通过instance
对象就可以使用wasm模块中的功能了:
java
instance.exports.add(1, 2); // 假设 wasm 导出了 add 函数
instance.exports.version; // 假设 wasm 导出了 version 变量
胶水文件
通常来说wasm汇编时会生成.wasm文件,.js胶水文件。
csharp
import init, { greet } from './pkg/wasm.js'
init().then(wasm => {
greet('world')
})
这是js中调用wasm的最常见形式,init初始化是个异步操作,初始化完成之后就可以使用相关的函数了。不管是初始化函数、功能函数都来自于胶水代码wasm.js
(也可以是别的名字),而胶水代码的核心就是以上的加载、初始化、使用。
小程序与h5的跳转
从小程序跳h5
使用<webview src="{{ src }}">
即可
从h5跳小程序
h5只能调用宿主环境(相应小程序平台)的SDK来进行操作
1个h5页面要嵌在不同平台的小程序中使用的问题
javascript
function clickFn() {
if (navigator.userAgent.match(/MicroMessenger/i) && window.wx) {
// 微信小程序
window.wx.miniProgram.navigateTo({
url: `/page/index`,
});
} else if (navigator.userAgent.includes('ToutiaoMicroApp')) {
// 抖音小程序
} else {
// 浏览器
}
}
像这样的代码并不方便管理,使用设计模式优化它:
工厂模式
javascript
class clientSDK {
navigateTo() {
throw new Error('should implement the method navigateTo')
}
}
class douYinSDK {
navigateTo(url) {
wx.miniProgram.navigateTo({url: 'pages/' + url})
}
}
class weiXinSDK {
navigateTo(url) {
tt.miniProgram.navigateTo({url: 'pages/' + url})
}
}
class broswerSDK {
navigateTO(url) {
// 取决于不同的框架的具体实现
}
}
class SDKFactory {
getSDKInstance(client) {
switch (client) {
case: 'wx':
return new weiXinSDK();
case: 'tt':
return new douYinSDK();
case: 'broswer':
return new broswerSDK();
default:
throw new Error('Invalid type')
}
}
}
javascript
// 页面初始化阶段
client = getCurrentClient()
// 方法
function getCurrentClient() {
let current = 'broswer'
if (navigator.userAgent.match(/MicroMessenger/i) && window.wx) {
current = 'wx'
} else if (navigator.userAgent.includes('ToutiaoMicroApp')) {
// 抖音小程序
current = 'tt'
}
const factory = new SDKFactory()
return factory.getSDKInstance(current)
}
function clickFn() {
client.navigateTo('index')
}
策略模式
javascript
class routeStrategy {
constructor(strategy) {
this.strategy = strategy
}
setStrategy(strategy) {
this.strategy = strategy
}
executeStrategy(...args) {
return this.strategy(...args)
}
}
javascript
// 页面初始化阶段
const currentRouter = new routeStrategy()
getCurrentClient(currentRouter)
// 方法
function getCurrentClient(strategy) {
let current = 'broswer'
if (navigator.userAgent.match(/MicroMessenger/i) && window.wx) {
strategy.setStrategy(wx.miniProgram.navigateTo)
} else if (navigator.userAgent.includes('ToutiaoMicroApp')) {
strategy.setStrategy(tt.miniProgram.navigateTo)
} else {
// 浏览器端要看框架
}
}
function clickFn() {
currentRouter.executeStrategy('pages/index')
}
还是太紧张了,当时太支支吾吾了
算法题
依赖收集
文件入口:A,
依赖项:[['A', 'B'], ['B', 'C'], ['B', 'D'], ['E', 'F'], ['C', 'E'], ['F', 'D'], ['G', 'H']]
根据依赖项,收集所有相关依赖
ini
function collectDeps (index, deps) {
let map = new Map()
deps.forEach(arr => {
const key = arr[0]
if (map.has(key)) {
map.get(key).push(arr[1])
} else {
map.set(key, [arr[1]])
}
})
let res = map.get(index)
res.push(index)
for (let i = 0; i < res.length; i++) {
const item = res[i];
if (map.has(item)) {
res = res.concat(map.get(item))
map.delete(item)
}
}
return new Set(res)
}
console.log(collectDeps(index, deps));
要点:
- Map的使用
- Set去重
- 使用
for()
循环而不是forEach
,因为forEach
只会根据初始的数组状态来遍历,for()
可以处理更新的数组
当时脑一抽忘了concat
返回新数组了......
总结
- 有些场景没有遇到过,但是总是会有类似的,不用慌,思考一下总会有头绪
- 对webassembly的内容梳理了一下,毕竟这算是过往工作中的小亮点
- 设计模式
- 太容易紧张了,一紧张就犯错
没发挥好啊啊啊啊,还挺喜欢他们家产品的其实。