基础知识

<!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. Map转换为格式化的YAML字符串

    yaml与java对象的互转 yaml与java对象的互转有snakeyaml <dependency> <groupId>org.yaml</groupId> & ...

  2. 小w、小j和小z

    n个月没更了,现在学的东西很难,掌握不好,不敢更! 这个题目既不超范围又足够难想,反正我没想出来,很好的题目! 我发现noi.ac上的题目很不错!!! ------------------------ ...

  3. centos安装Qt

    转:http://blog.csdn.net/wavelee/article/details/7855727 在编译Qt4.8.6版本的库时,在配置时 ./configure 出现了如下的错误: Ba ...

  4. 封装验证blue事件插件

    (function ($) { $.fn.checkForm = function (options) { var root = this; //将当前应用对象存入root var isok = fa ...

  5. 19.损坏磁盘阵列及修复&磁盘阵列+备份盘

    1.在确认有一块物理硬盘设备出现损坏而不能继续正常使用后,应该使用mdadm 命令将其移除,然后查看RAID 磁盘阵列的状态,可以发现状态已经改变. [root@Centos ~]# mdadm /d ...

  6. CF-1354 E. Graph Coloring(二分图,背包,背包方案输出)

    E. Graph Coloring 链接 n个点m条边的无向图,不保证联通,给每个点标号1,2,3.1号点个数n1,2号点个数n2,3号点个数n3.且每条边的两点,标号之差绝对值为1.如果有合法方案, ...

  7. 【洛谷 p3371】模板-单源最短路径(图论)

    题目:给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 解法:spfa算法. 1 #include<cstdio> 2 #include<cstdlib> 3 #in ...

  8. java swing JDialog 和 java.util.concurrent的使用

    参考链接: Java-Swing的JFrame的一些插件使用详解 java swing JDialog 使用 ScheduledExecutorService定时周期执行指定的任务 swing JDi ...

  9. zoj3777 Problem Arrangement(状压dp,思路赞)

    The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem setter, Edward i ...

  10. hdu 3549Flow Problem

    Problem Description Network flow is a well-known difficult problem for ACMers. Given a graph, your t ...