基础知识

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>
<input type="text" ng-bind="name" />
<button type="button" ng-click="increment">increment</button>
<div ng-bind="name"></div>
</div>
<script>
class Watcher {
constructor(name, last, exp, listener) {
this.name = name; // 数据变量名
this.last = last; // 数据变量旧值
this.newVal = exp; // 返回数据变量新值的函数
this.listener = listener || function () {}; // 监听回调函数,变量“脏”时触发
this.listener(this.last, this.newVal());
}
} class Scope {
constructor() {
// 观察者数组
this.watchers = [];
} // 添加数据观察者
watch(name, exp, listener) {
this.watchers.push(new Watcher(name, "", exp, listener));
} // 对监视器的新旧值进行对比
// 当新旧值不同时,调用listener函数进行相应操作
// 并将旧值更新为新值。它将不断重复这一过程,直到所有数据变量的新旧值相等:
digest() {
let dirty = true;
while (dirty) {
dirty = false;
this.watchers.forEach(watcher => {
let newVal = watcher.newVal();
var oldVal = watcher.last;
if (newVal !== oldVal) {
dirty = true;
watcher.listener(oldVal, newVal);
watcher.last = newVal;
}
});
}
}
} class App extends Scope {
name = "Ajanuw"; constructor() {
super();
} increment() {
this.name += "+";
}
} const app = new App();
run(app);
function run(app) {
document // 绑定依赖观察者
.querySelectorAll("[ng-bind]")
.forEach(it => {
const nodeName = it.nodeName.toLowerCase();
const bindKey = it.getAttribute("ng-bind");
if (bindKey in app) {
app.watch(
bindKey,
() => app[bindKey],
(oldVal, newVal) => {
if (nodeName === "input") {
it.value = newVal;
} else {
it.textContent = newVal;
}
}
);
}
}); // 绑定事件
document.querySelectorAll("[ng-click]").forEach(it => {
const bindKey = it.getAttribute("ng-click");
it.addEventListener("click", e => {
if (app[bindKey] && typeof app[bindKey] === "function") {
app[bindKey]();
app.digest();
}
});
}); // 双向绑定
document.querySelectorAll("input[ng-bind]").forEach(it => {
const bindKey = it.getAttribute("ng-bind");
it.addEventListener("input", e => {
app[bindKey] = it.value;
app.digest();
});
});
}
</script>
</body>
</html>

监听object和array的修改

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>
<input type="text" ng-bind="name" />
<button type="button" ng-click="increment">increment</button>
<div ng-bind="name"></div>
<hr />
<div ng-bind="obj"></div>
<button ng-click="changeValue">改变object的值</button>
</div>
<script>
function equal(obj, other) {
const objectTag = "[object Object]";
const arrayTag = "[object Array]";
const _tostring = value => Object.prototype.toString.call(value);
const emptyp = value => JSON.stringify(value).length === 2;
function Equal(obj, other) {
let objTag = _tostring(obj);
let otherTag = _tostring(other); // 非集合,使用===判断
if (
objTag !== objectTag &&
objTag !== arrayTag &&
otherTag !== objectTag &&
otherTag !== arrayTag
) {
return obj === other;
} // 集合类型不一样
if (objTag !== otherTag) return false; // 集合元素数量不一样
if (
Object.getOwnPropertyNames(obj).length !==
Object.getOwnPropertyNames(other).length
)
return false; // 类型一样的空集合,永远相等。
if (emptyp(obj) && emptyp(other)) return true; let rsult = false;
for (const k in obj) {
if (k in other) {
const obj_value = obj[k];
const other_value = other[k];
rsult = Equal(obj_value, other_value);
} else {
return false;
}
}
return rsult;
} return Equal(obj, other);
}
function copytree(tree, all = true) {
const objectTag = "[object Object]";
const arrayTag = "[object Array]"; const _tostring = value => Object.prototype.toString.call(value);
// 记录所有的对象
const map = new WeakMap();
function copyTree(tree, all = true) {
const treeTag = _tostring(tree);
const res =
treeTag === objectTag ? {} : treeTag === arrayTag ? [] : tree; if (treeTag !== objectTag && treeTag !== arrayTag) return res; // 判断是否有此对象
if (map.has(tree)) {
// 直接返回
return tree;
} else {
map.set(tree, true);
} const t = all ? Object.getOwnPropertyNames(tree) : tree; if (all) {
for (const i in t) {
const k = t[i];
res[k] = copyTree(tree[k], all);
}
} else {
for (const k in t) {
res[k] = copyTree(tree[k], all);
}
} return res;
} return copyTree(tree, all);
}
function evalFun(bindKey, data) {
try {
const r = Function(`with(this){ return ${bindKey} }`).apply(
data,
arguments
);
return r === "" ? undefined : r;
} catch (error) {}
}
function setData(key, newValue, context) {
return Function(`return function(d) {
with(this){
${key} = d;
}
}`)().call(context, newValue);
} class Watcher {
constructor(last, exp, listener, valueEq) {
this.last = last; // 数据变量旧值
this.newVal = exp; // 返回数据变量新值的函数
this.listener = listener || function () {}; // 监听回调函数,变量“脏”时触发
this.valueEq = valueEq;
this.listener(this.last, this.newVal());
}
} class Scope {
constructor() {
// 观察者数组
this.watchers = [];
} // 添加数据观察者
// valueEq检查值,而不是引用
watch(v, exp, listener, valueEq = false) {
this.watchers.push(
new Watcher(valueEq ? copytree(v) : v, exp, listener, valueEq)
);
} // 对监视器的新旧值进行对比
// 当新旧值不同时,调用listener函数进行相应操作
// 并将旧值更新为新值。它将不断重复这一过程,直到所有数据变量的新旧值相等:
digest() {
let dirty = true;
// while (dirty) {
// dirty = false;
this.watchers.forEach(watcher => {
const newVal = watcher.newVal();
const oldVal = watcher.last;
if (!this.valueEqual(newVal, oldVal, watcher.valueEq)) {
dirty = true;
watcher.listener(oldVal, newVal);
watcher.last = watcher.valueEq ? copytree(newVal) : newVal;
}
});
// }
} valueEqual(newValue, oldValue, valueEq) {
if (this.valueEq) {
return equal(newValue, oldValue);
} else {
return newValue === oldValue;
}
}
} class App extends Scope {
name = "Ajanuw";
obj = {
value: "hello world"
}; constructor() {
super();
} increment() {
this.name += "+";
} changeValue() {
this.obj.value = "hello ajanuw";
}
} const app = new App();
run(app);
function run(app) {
document // 绑定依赖观察者
.querySelectorAll("[ng-bind]")
.forEach(it => {
const nodeName = it.nodeName.toLowerCase();
const bindKey = it.getAttribute("ng-bind");
const v = evalFun(bindKey, app);
if (v) {
app.watch(
v,
() => evalFun(bindKey, app),
(oldVal, newVal) => {
if (nodeName === "input") {
it.value = newVal;
} else {
if (typeof newVal === "object") {
it.textContent = JSON.stringify(newVal);
} else {
it.textContent = newVal;
}
}
},
typeof v === "object" && v !== null
);
}
}); // 绑定事件
document.querySelectorAll("[ng-click]").forEach(it => {
const bindKey = it.getAttribute("ng-click");
const fn = evalFun(bindKey, app);
if (fn && typeof fn === "function") {
it.addEventListener("click", e => {
fn.call(app);
app.digest();
});
}
}); // 双向绑定
document.querySelectorAll("input[ng-bind]").forEach(it => {
const bindKey = it.getAttribute("ng-bind");
it.addEventListener("input", e => {
setData(bindKey, it.value, app);
app.digest();
});
});
}
</script>
</body>
</html>

js 脏检测的更多相关文章

  1. 我的angularjs源码学习之旅3——脏检测与数据双向绑定

    前言 为了后面描述方便,我们将保存模块的对象modules叫做模块缓存.我们跟踪的例子如下 <div ng-app="myApp" ng-controller='myCtrl ...

  2. Device.js – 快速检测平台、操作系统和方向信息

    在 Web 项目中,有时候我们需要根据程序运行的环境采取特定操作.Device.js 是一个很小的 JavaScript 库,它简化了编写和平台,操作系统或浏览器相关的条件 CSS 或 JavaScr ...

  3. Sublime text3 JS语法检测工具安装及使用

    Sublime text3 JS语法检测工具安装及使用 工具/原料 sublime text3 nodejs sublimeLinter sublimeLinter-jshint 方法/步骤 首先ct ...

  4. angular1.x 脏检测

    写在前面 双向绑定是angular的大亮点,然后支撑它的就是脏检测.一直对脏检测都有一些理解,却没有比较系统的概念. 以下是我阅读网上博文以及angular高级程序设计的理解与总结. 接收指导与批评. ...

  5. 手写面试编程题- 数组去重 深拷贝 获取文本节点 设置奇数偶数背景色 JS中检测变量为string类型的方法 第6题闭包 将两个数组合并为一个数组 怎样添加、移除、移动、复制、创建和查找节点? 继承 对一个数组实现随机排序 让元素水平 垂直居中的三种方式 通过jQuery的extend方法实现深拷贝

    第1题==>实现数组去重 通过 new Set(数组名) // var arr = [12, 12, 3, 4, 5, 4, 5, 6, 6]; // var newarr1 = new Set ...

  6. js代码检测设备问题:为什么在移动端检测设备的时候会出现pc的页面

    为了在手机上也能正常显示页面,所以为之前写的页面又重写了一遍,专门用来在移动端显示,用js代码检测设备,如果是pc就显示pc的页面,如果是移动就显示移动的页面,但遇到一个问题就是在移动端打开会有一个延 ...

  7. JS 中检测数组的四种方法

    今天和大家分享一下 JS 中检测是不是数组的四种方法,虽然篇幅不长,不过方法应该算是比较全面了. 1. instanceof 方法 instanceof 用于检测一个对象是不是某个类的实例,数组也是一 ...

  8. JS正则检测密码强度

    今天遇到个需求,使用JS检测密码强度:密码长度最短为8,必须同时包含字母.数字.特殊符号. 代码如下: /*         * 检测密码复杂度         */         function ...

  9. js如何检测打开窗口是否存在的三个方法?

    js打开窗口一般也就是使用window.open方法: win = window.open(CHILD_WINDOW_URL, CHILD_WINDOW_NAME, CHILD_WINDOW_ATTR ...

随机推荐

  1. Redis击穿、穿透、雪崩产生原因以及解决思路

    击穿 大家都知道,计算机的瓶颈之一就是IO,为了解决内存与磁盘速度不匹配的问题,产生了缓存,将一些热点数据放在内存中,随用随取,降低连接到数据库的请求链接,避免数据库挂掉.需要注意的是,无论是击穿还是 ...

  2. 济南学习D3T1__线性筛和阶乘质因数分解

    [问题描述] 从1− N中找一些数乘起来使得答案是一个完全平方数,求这个完全平方数最大可能是多少. [输入格式] 第一行一个数字N. [输出格式] 一行,一个整数代表答案对100000007取模之后的 ...

  3. (十六)配置多数据源,整合MybatisPlus增强插件

    配置多数据源,整合MybatisPlus增强插件 多数据简介 MybatisPlus简介 1.案例实现 1.1 项目结构 1.2 多数据源配置 1.3 参数扫描类 1.4 配置Druid连接池 1.5 ...

  4. JavaScript——DOM操作

    DOM-(Document Object Model)即文档对象模型. JavaScript可以动态地修改DOM,从而来修改HTML的内容. 查找HTML元素 通过 id 找到 HTML 元素 通过标 ...

  5. 客户端负载均衡Ribbon

    客户端负载均衡Ribbon 一.Ribbon是什么 二.Ribbon实现客户端负载均衡 三.Ribbon负载均衡策略 四.Rest请求模板类解读 4.1 RestTemplate的GET请求 第一种: ...

  6. mysqld_exporter的源码分析和定制化(单个mysqld_exporter监控多个数据库实例)

    mysqld_exporter是prometheus官方提供的用于监控mysql运行状态的exporter.其相关信息可以参考:https://github.com/prometheus/mysqld ...

  7. js将金额转成大写金额

    function Chinese(){ /* var num= $(dialogStruct.iframe.contentDocument.getElementById("contractA ...

  8. idea中将普通工程设置为maven项目

    只需要在工程上右键,"Add Frameworks support...",然后选择Maven即可

  9. 服务器CPU又爆了?Linux快速排查Java程序占用CPU很高的方法

    这个问题可以说是 Java 面试的高频面试题了,有很多面试官都喜欢问这个问题,问题可能是下面这样的. 线上一台服务器 CPU 使用率100% 了,如果你碰到这样的情况,如何排查并找到问题原因? 1.场 ...

  10. springboot源码解析-管中窥豹系列之web服务器(七)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...