算法
二分法搜索
Array.prototype.binarySearch = function (item) {
this.sort((a, b) => {
return a - b;
});
var low = 0,
high = this.length - 1,
mid = Math.ceil((low + high) / 2);
while (low <= high) {
if (this[mid] < item) {
low = mid + 1;
} else if (this[mid] > item) {
high = mid - 1;
} else {
return mid;
}
mid = Math.ceil((low + high) / 2);
}
return -1;
}
var arr = [3, 4, 51, 2, 4, 2, 1, 23, 4, 5, 62, 123, 1, 231, 4, 123];
console.log(arr.binarySearch(1));
斐波那契
const fibonacci = (num: number) => {
if (num === 1 || num === 2) {
return 1
}
return fibonacci(num - 1) + fibonacci(num - 2);
}
console.log(fibonacci(6))
实现 Vue 双绑不含虚拟 DOM
Vue 实现双绑大概如下:
- 对数据进行递归的 Observe
- 实例化 dep,get 方法内放入 dep.addSubs,如果对应有 watcher 将 watcher 放入依赖绑定中
- set 方法中绑定 dep.notify,notify 调用与当前数据相关的所有 watcher
- 对模板进行递归解析,将 textnode 中的对应模板与数据进行隐射,初始化替换 node 节点内容之后,初始化一个 watcher,将改数据的 key 和 相关该 key 的回调传入 watcher
- watcher 实例化直接调用 watcher 的 get 方法,此时 get 方法中已经有一个方法,将该 watcher addSub 到依赖收集器中
- 初始化完毕
- 数据发生改变时,触发 set 方法,set 方法中触发 dep.notify,notify 触发与这个数据所有相关的 watcher.update 方法
- watcher.update 触发 run 方法,run 触发实例化 watcher 时与当前数据相关的模板回调。执行回调,发生页面改变
// Vue 简单的对象 const info = { el: document.querySelector('#karyn'), template: `<p></p><p></p><p></p>`, data() { return { demo1: 'demo1', demo2: 'demo2', data: { demo: 'demo' } } } } // 依赖收集器 class Dep { private subs: any[] = []; static target = null; addSub(sub) { // 将 watcher 添加进来 this.subs.push(sub); } notify() { this.subs.map(item => { // 执行 watcher 的 update 方法 item.update(); }) } } // 指令监听器 class Watcher { private cb; // 指令回调 private info; // 主函数信息 private exp; // 匹配的数据项 private value; constructor(info, exp, cb: Function) { this.cb = cb; this.info = info; this.exp = exp; // 将 watcher 的 get 方法进行执行 this.value = this.get(); } update() { // 这里可以做个延迟,最后一次性 append dom this.run(); } run() { // 深层递归 var exps = this.exp.split('.'); var value = this.info.data; while (exps.length) { value = value[exps.shift()]; } var oldVal = this.value; // 替换新旧的值执行 watcher 的回执 if (value !== oldVal) { this.value = value; this.cb.call(this.info, value, oldVal); } } get() { Dep.target = this; // 深层递归 var exps = this.exp.split('.'); var value = this.info.data; while (exps.length) { value = value[exps.shift()]; } Dep.target = null; return value } } // 编译模板 class Compile { private info = {}; constructor(info) { this.info = info; // 编译模板 this.compileElement(info.el); } compileElement(node) { var childNodes = node.childNodes; // 将每一个子节点拿出来查询 [].slice.call(childNodes).map(item => { var reg = /\{\{(.*)\}\}/; var text = item.textContent; // 如果是文本节点且有参数 if (this.isTextNode(item) && reg.test(text)) { // 将模板中的数据进行替换 this.compileText(item, reg.exec(text)[1]); } // 如果还有子节点进行递归 if (item.childNodes && item.childNodes.length) { this.compileElement(item); } }) } compileText(node, exp) { exp = exp.replace(/^\s*|\s*$/g, ''); var exps = exp.split('.'); var initText = this.info.data; // 将对应的值拿出来,如果是多层级可能会有问题 while (exps.length) { initText = initText[exps.shift()]; } // 更新模板 this.updateText(node, initText); // 实例化一个 watcher, 当 watcher 触发时执行回调 new Watcher(this.info, exp, (value) => { this.updateText(node, value); }); } updateText(node, text) { node.nodeValue = text; } isTextNode(node) { return node.nodeType === 3; } } // 核心组件 class Karyn { private $data: object = {}; private data: object = {}; private template: string = ''; private el: any; constructor(info) { // Vue 中,这里是一个拷贝数据 this.$data = info.data.call(this); // 将双板的数据放入 this.data 中 this.data = info.data.call(this); // 缓存模板 this.template = info.template; // 缓存跟节点 this.el = info.el; // 先将节点拼接进去 this.el.innerHTML = this.template; // 初始化双绑数据 this.observe(this.data); // 对模板进行编译 new Compile(this); } observe(data) { // 递归绑定 if (!data || typeof data !== 'object') { return; } // 由于 object 是引用类型,所以递归绑定也是同一个值 Object.keys(data).map(item => { // 真正绑定的方法 this.defineReactive(data, item, data[item]); }); } defineReactive(data, key, val) { // 递归调用 this.observe(val); // 初始化一个以来收集器 var dep = new Dep(); // 对一个数据进行绑定 Object.defineProperty(data, key, { get() { // 第一次会默认调起 // 如果需要添加订阅者 if (Dep.target) { // 添加订阅者 dep.addSub(Dep.target); } return val }, set(newVal) { if (val === newVal) { return; } val = newVal; // 赋值之后分发事件 dep.notify(); } }) } } var karyn = new Karyn(info) setTimeout(() => { karyn.data.data.demo = '000'; karyn.data.demo1 = '111'; karyn.data.demo2 = '222'; }, 2000)