Created
March 23, 2015 03:59
-
-
Save yichaowang/2c0cd2895555ae384597 to your computer and use it in GitHub Desktop.
JS Bin Yichao Test // source http://jsbin.com/modasa
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name="description" content="Yichao Test" /> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script> | |
<meta charset=utf-8 /> | |
<title>JS Bin</title> | |
</head> | |
<body> | |
<script id="jsbin-javascript"> | |
var i = 0; | |
function Scope() { | |
this.$$watchers = []; | |
this.$$asyncQueue = []; | |
this.$$postDigestQueue = []; | |
this.$$phase = null; | |
} | |
Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { | |
var watcher = { | |
watchFn: watchFn, | |
listenerFn: listenerFn, | |
valueEq: !!valueEq | |
}; | |
this.$$watchers.push(watcher); | |
}; | |
Scope.prototype.$$digestOnce = function() { | |
var self = this; | |
var dirty; | |
_.forEach(this.$$watchers, function(watch) { | |
var newValue = watch.watchFn(self); | |
var oldValue = watch.last; | |
if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { | |
watch.listenerFn(newValue, oldValue, self); | |
dirty = true; | |
watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); | |
} | |
}); | |
return dirty; | |
}; | |
Scope.prototype.$beginPhase = function(phase) { | |
if (this.$$phase) { | |
throw this.$$phase + 'already in progress.'; | |
} | |
this.$$phase = phase; | |
}; | |
Scope.prototype.$clearPhase = function() { | |
this.$$phase = null; | |
}; | |
Scope.prototype.$digest = function() { | |
var ttl = 10; | |
var dirty; | |
this.$beginPhase('$digest'); | |
do { | |
while (this.$$asyncQueue.length) { | |
var asyncTask = this.$$asyncQueue.shift(); | |
this.$eval(asyncTask.expression); | |
} | |
dirty = this.$$digestOnce(); | |
if (dirty && !(ttl--)) { | |
this.$clearPhase(); | |
throw "10 digest iterations reached"; | |
} | |
} while (dirty); | |
this.$clearPhase(); | |
while (this.$$postDigestQueue.length) { | |
this.$$postDigestQueue.shift()(); | |
} | |
}; | |
Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { | |
if (valueEq) { | |
return _.isEqual(newValue, oldValue); | |
} else { | |
return newValue === oldValue || | |
(typeof newValue === 'number' && typeof oldValue === 'number' && | |
isNaN(newValue) && isNaN(oldValue)); | |
} | |
}; | |
Scope.prototype.$eval = function(expr, locals) { | |
return expr(this, locals); | |
}; | |
Scope.prototype.$evalAsync = function(expr) { | |
var self = this; | |
if (!self.$$phase && !self.$$asyncQueue.legnth) { | |
setTimeout(function() { | |
if (self.$$asyncQueue.length) { | |
self.$digest(); | |
} | |
}, 0); | |
} | |
this.$$asyncQueue.push({scope: this, expression: expr}); | |
}; | |
Scope.prototype.$apply = function(expr) { | |
try { | |
this.$beginPhase("$apply"); | |
return this.$eval(expr); | |
} finally { | |
this.$clearPhase(); | |
this.$digest(); | |
} | |
}; | |
Scope.prototype.$$postDigest = function(fn) { | |
this.$$postDigestQueue.push(fn); | |
}; | |
var scope = new Scope(); | |
scope.asyncEvaled = false; | |
scope.evaled = false; | |
scope.evalCounter = 0; | |
scope.asyncCounter = 0; | |
scope.$watch( | |
function(scope) { | |
return scope.evaled; | |
}, | |
function(newValue, oldValue, scope) { | |
scope.evalCounter++; | |
} | |
); | |
scope.$eval(function(scope) { | |
scope.evaled = true; | |
}); | |
console.log("Evaled: "+scope.evaled); | |
console.log("EvalCounte: "+scope.evalCounter); | |
scope.$evalAsync(function(scope) { | |
scope.asyncEvaled = true; | |
console.log("Evaled in asyncTask: "+scope.evalCounter); | |
}); | |
setTimeout(function() { | |
console.log("Evaled after a while: "+scope.asyncEvaled); | |
console.log("EvalCounter after a while: "+scope.evalCounter); | |
}); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">var i = 0; | |
function Scope() { | |
this.$$watchers = []; | |
this.$$asyncQueue = []; | |
this.$$postDigestQueue = []; | |
this.$$phase = null; | |
} | |
Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { | |
var watcher = { | |
watchFn: watchFn, | |
listenerFn: listenerFn, | |
valueEq: !!valueEq | |
}; | |
this.$$watchers.push(watcher); | |
}; | |
Scope.prototype.$$digestOnce = function() { | |
var self = this; | |
var dirty; | |
_.forEach(this.$$watchers, function(watch) { | |
var newValue = watch.watchFn(self); | |
var oldValue = watch.last; | |
if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { | |
watch.listenerFn(newValue, oldValue, self); | |
dirty = true; | |
watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); | |
} | |
}); | |
return dirty; | |
}; | |
Scope.prototype.$beginPhase = function(phase) { | |
if (this.$$phase) { | |
throw this.$$phase + 'already in progress.'; | |
} | |
this.$$phase = phase; | |
}; | |
Scope.prototype.$clearPhase = function() { | |
this.$$phase = null; | |
}; | |
Scope.prototype.$digest = function() { | |
var ttl = 10; | |
var dirty; | |
this.$beginPhase('$digest'); | |
do { | |
while (this.$$asyncQueue.length) { | |
var asyncTask = this.$$asyncQueue.shift(); | |
this.$eval(asyncTask.expression); | |
} | |
dirty = this.$$digestOnce(); | |
if (dirty && !(ttl--)) { | |
this.$clearPhase(); | |
throw "10 digest iterations reached"; | |
} | |
} while (dirty); | |
this.$clearPhase(); | |
while (this.$$postDigestQueue.length) { | |
this.$$postDigestQueue.shift()(); | |
} | |
}; | |
Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { | |
if (valueEq) { | |
return _.isEqual(newValue, oldValue); | |
} else { | |
return newValue === oldValue || | |
(typeof newValue === 'number' && typeof oldValue === 'number' && | |
isNaN(newValue) && isNaN(oldValue)); | |
} | |
}; | |
Scope.prototype.$eval = function(expr, locals) { | |
return expr(this, locals); | |
}; | |
Scope.prototype.$evalAsync = function(expr) { | |
var self = this; | |
if (!self.$$phase && !self.$$asyncQueue.legnth) { | |
setTimeout(function() { | |
if (self.$$asyncQueue.length) { | |
self.$digest(); | |
} | |
}, 0); | |
} | |
this.$$asyncQueue.push({scope: this, expression: expr}); | |
}; | |
Scope.prototype.$apply = function(expr) { | |
try { | |
this.$beginPhase("$apply"); | |
return this.$eval(expr); | |
} finally { | |
this.$clearPhase(); | |
this.$digest(); | |
} | |
}; | |
Scope.prototype.$$postDigest = function(fn) { | |
this.$$postDigestQueue.push(fn); | |
}; | |
var scope = new Scope(); | |
scope.asyncEvaled = false; | |
scope.evaled = false; | |
scope.evalCounter = 0; | |
scope.asyncCounter = 0; | |
scope.$watch( | |
function(scope) { | |
return scope.evaled; | |
}, | |
function(newValue, oldValue, scope) { | |
scope.evalCounter++; | |
} | |
); | |
scope.$eval(function(scope) { | |
scope.evaled = true; | |
}); | |
console.log("Evaled: "+scope.evaled); | |
console.log("EvalCounte: "+scope.evalCounter); | |
scope.$evalAsync(function(scope) { | |
scope.asyncEvaled = true; | |
console.log("Evaled in asyncTask: "+scope.evalCounter); | |
}); | |
setTimeout(function() { | |
console.log("Evaled after a while: "+scope.asyncEvaled); | |
console.log("EvalCounter after a while: "+scope.evalCounter); | |
}); | |
</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var i = 0; | |
function Scope() { | |
this.$$watchers = []; | |
this.$$asyncQueue = []; | |
this.$$postDigestQueue = []; | |
this.$$phase = null; | |
} | |
Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { | |
var watcher = { | |
watchFn: watchFn, | |
listenerFn: listenerFn, | |
valueEq: !!valueEq | |
}; | |
this.$$watchers.push(watcher); | |
}; | |
Scope.prototype.$$digestOnce = function() { | |
var self = this; | |
var dirty; | |
_.forEach(this.$$watchers, function(watch) { | |
var newValue = watch.watchFn(self); | |
var oldValue = watch.last; | |
if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) { | |
watch.listenerFn(newValue, oldValue, self); | |
dirty = true; | |
watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue); | |
} | |
}); | |
return dirty; | |
}; | |
Scope.prototype.$beginPhase = function(phase) { | |
if (this.$$phase) { | |
throw this.$$phase + 'already in progress.'; | |
} | |
this.$$phase = phase; | |
}; | |
Scope.prototype.$clearPhase = function() { | |
this.$$phase = null; | |
}; | |
Scope.prototype.$digest = function() { | |
var ttl = 10; | |
var dirty; | |
this.$beginPhase('$digest'); | |
do { | |
while (this.$$asyncQueue.length) { | |
var asyncTask = this.$$asyncQueue.shift(); | |
this.$eval(asyncTask.expression); | |
} | |
dirty = this.$$digestOnce(); | |
if (dirty && !(ttl--)) { | |
this.$clearPhase(); | |
throw "10 digest iterations reached"; | |
} | |
} while (dirty); | |
this.$clearPhase(); | |
while (this.$$postDigestQueue.length) { | |
this.$$postDigestQueue.shift()(); | |
} | |
}; | |
Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { | |
if (valueEq) { | |
return _.isEqual(newValue, oldValue); | |
} else { | |
return newValue === oldValue || | |
(typeof newValue === 'number' && typeof oldValue === 'number' && | |
isNaN(newValue) && isNaN(oldValue)); | |
} | |
}; | |
Scope.prototype.$eval = function(expr, locals) { | |
return expr(this, locals); | |
}; | |
Scope.prototype.$evalAsync = function(expr) { | |
var self = this; | |
if (!self.$$phase && !self.$$asyncQueue.legnth) { | |
setTimeout(function() { | |
if (self.$$asyncQueue.length) { | |
self.$digest(); | |
} | |
}, 0); | |
} | |
this.$$asyncQueue.push({scope: this, expression: expr}); | |
}; | |
Scope.prototype.$apply = function(expr) { | |
try { | |
this.$beginPhase("$apply"); | |
return this.$eval(expr); | |
} finally { | |
this.$clearPhase(); | |
this.$digest(); | |
} | |
}; | |
Scope.prototype.$$postDigest = function(fn) { | |
this.$$postDigestQueue.push(fn); | |
}; | |
var scope = new Scope(); | |
scope.asyncEvaled = false; | |
scope.evaled = false; | |
scope.evalCounter = 0; | |
scope.asyncCounter = 0; | |
scope.$watch( | |
function(scope) { | |
return scope.evaled; | |
}, | |
function(newValue, oldValue, scope) { | |
scope.evalCounter++; | |
} | |
); | |
scope.$eval(function(scope) { | |
scope.evaled = true; | |
}); | |
console.log("Evaled: "+scope.evaled); | |
console.log("EvalCounte: "+scope.evalCounter); | |
scope.$evalAsync(function(scope) { | |
scope.asyncEvaled = true; | |
console.log("Evaled in asyncTask: "+scope.evalCounter); | |
}); | |
setTimeout(function() { | |
console.log("Evaled after a while: "+scope.asyncEvaled); | |
console.log("EvalCounter after a while: "+scope.evalCounter); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment