Angularã¯ãæçãã匷åãªJavaScriptãã¬ãŒã ã¯ãŒã¯ã§ãã éåžžã«å€§ããã广çã«æ©èœããããã«ç¿åŸããå¿
èŠãããå€ãã®æ°ããæŠå¿µã«åºã¥ããŠããŸãã Angularã«æ
£ããŠããã»ãšãã©ã®éçºè
ã¯ãåãå°é£ã«çŽé¢ããŠããŸãã ãã€ãžã§ã¹ãæ©èœã¯æ£ç¢ºã«äœãããŸããïŒ ãã£ã¬ã¯ãã£ããäœæããæ¹æ³ã¯äœã§ããïŒ ãµãŒãã¹ãšãããã€ããŒã®éãã¯äœã§ããïŒ
Angularã«ã¯éåžžã«åªãã
ããã¥ã¡ã³ããããã
ãµãŒãããŒãã£ã®ãªãœãŒã¹ããããããããšããäºå®ã«ãããããããæè¡ã现ããåå²ããŠãã®éæ³ãæããã«ããããããæè¡ãåŠã¶è¯ãæ¹æ³ã¯ãããŸããã
ãã®ã·ãªãŒãºã®èšäºã§ã¯ã
AngularJSããŒãããåäœæããŸãã ãããæ®µéçã«äžç·ã«è¡ããŸãããã®éã«ãAngularã®å
éšæ§é ãããæ·±ãçè§£ã§ããŸãã
ãã®ã·ãªãŒãºã®æåã®éšåã§ã¯ãã¹ã³ãŒãããã€ã¹ãããã³
$ eval ã
$ digestãããã³
$ applyãå®éã«ã©ã®ããã«æ©èœããããèŠãŠãããŸãã Angularã§ããŒã¿ã®å€æŽïŒããŒãã£ãã§ãã¯ïŒããã§ãã¯ããã®ã¯éæ³ã®ããã«æããŸãããããã§ã¯ãããŸãã-èªåã§ç¢ºèªã§ããŸãã
æºåãã
ãããžã§ã¯ããœãŒã¹ã¯githubã§å
¥æã§ã
ãŸãããèªåã§ã³ããŒããããšã¯ãå§ãããŸããã 代ããã«ãç§ã¯ããªãããã¹ãŠãèªåã§ãäžæ©ãã€å®è¡ããã³ãŒãããããããããæãäžããããšã䞻匵ããŸãã ããã¹ãã§ã¯
JS Binã䜿çšããŠãããããããŒãžãé¢ããããšãªãã³ãŒããæäœã§ããŸãïŒ
çŽ -倿ã§ã¯ãJS Binã³ãŒããžã®ãªã³ã¯ã®ã¿ãååšããŸãïŒã
é
åãšãªããžã§ã¯ãã䜿çšããäœã¬ãã«ã®æäœã«ã¯
Lo-Dashã䜿çšããŸãã Angularèªäœã¯Lo-Dashã䜿çšããŸãããããã®ç®çã®ããã«ããã³ãã¬ãŒãã®äœã¬ãã«ã³ãŒããå¯èœãªéãåé€ããããšã¯çã«ããªã£ãŠããŸãã ã³ãŒãïŒã¢ã³ããŒã¹ã³ã¢ïŒã®ã©ãã§ãïŒ
_ ïŒãLo-Dash颿°ãåŒã³åºãããŸãã
ãŸããåçŽãªãã§ãã¯ã®ããã«
console.assert颿°ã䜿çšããŸãã ææ°ã®ãã¹ãŠã®JavaScriptç°å¢ã§äœ¿çšã§ããå¿
èŠããããŸãã
Lo-Dashã®äŸã¯æ¬¡ã®ãšããã§ãã
JS Binã³ãŒãã³ãŒãã衚瀺var a = [1, 2, 3]; var b = [1, 2, 3]; var c = _.map(a, function(i) { return i * 2; }); console.assert(a !== b); console.assert(_.isEqual(a, b)); console.assert(_.isEqual([2, 4, 6], c));
ã³ã³ãœãŒã«ïŒ
æ¬åœ
æ¬åœ
æ¬åœ
ãªããžã§ã¯ã-ã¹ã³ãŒãïŒã¹ã³ãŒãïŒ
è§åºŠã¹ã³ãŒããªããžã§ã¯ãã¯ãæšæºçãªæ¹æ³ã§ããããã£ã远å ã§ããéåžžã®JavaScriptãªããžã§ã¯ãã§ãã ãããã¯ã
Scopeã³ã³ã¹ãã©ã¯ã¿ãŒã䜿çšããŠäœæãããŸãã æãç°¡åãªå®è£
ãæžããŸãããã
function Scope() { }
ããã§ã
newæŒç®åã䜿çšããŠãã¹ã³ãŒããªããžã§ã¯ããäœæããããããã£ã远å ã§ããŸãã
var aScope = new Scope(); aScope.firstName = 'Jane'; aScope.lastName = 'Smith';
ãããã®ããããã£ã«ã¯ç¹å¥ãªãã®ã¯ãããŸããã ç¹å¥ãªã»ãã¿ãŒãå²ãåœãŠãå¿
èŠã¯ãããŸãã;å€ã®åã«å¶éã¯ãããŸããã 代ããã«ããã¹ãŠã®éæ³ã¯2ã€ã®é¢æ°ã«ãããŸãïŒ
$ watchãš
$ digestã§ãã
ãªããžã§ã¯ãããããã£ã®ç£èŠïŒ$ watchããã³$ digest
$ watchãš
$ digestã¯ãåãã³ã€ã³ã®è¡šè£ã§ãã ãããã¯äžç·ã«ãªã£ãŠãAngularã®ã¹ã³ãŒããªããžã§ã¯ãã®äžå¿ã§ããããŒã¿å€æŽãžã®åå¿ã圢æããŸãã
$ watchã䜿çšããŠãã¹ã³ãŒãã«ããªãã¶ãŒããŒãã远å ã§ããŸãã ãªãã¶ãŒããŒã¯ãé©åãªã¹ã³ãŒãã§å€æŽãçºçãããšãã«éç¥ããããã®ã§ãã
ãªãã¶ãŒããŒã¯ã2ã€ã®é¢æ°ã
$ watchã«æž¡ãããšã§äœæãããŸãã
- 倿Žã«é¢å¿ã®ããããŒã¿ãè¿ããŠã©ãã颿°
- ãã®ããŒã¿ã倿Žããããšãã«åŒã³åºããããªã¹ããŒé¢æ°
Angularã§ã¯ãç£èŠé¢æ°ã®ä»£ããã«ç£èŠåŒã䜿çšããŸããã ããã¯ããã€ã³ãæã«htmlã§ããã£ã¬ã¯ãã£ãã®å±æ§ãšããŠããŸãã¯JavaScriptããçŽæ¥æå®ããæååïŒãuser.firstNameãã®ãããªãã®ïŒã§ãã ãã®è¡ã¯ãAngularã«ãã£ãŠè§£æãããŠã³ã³ãã€ã«ãããç§ãã¡ã®ãã®ãšåæ§ã®ç£èŠé¢æ°ã«ãªããŸããã ãããã©ã®ããã«è¡ããããã«ã€ããŠã¯ã次ã®èšäºã§èª¬æããŸãã åãèšäºã§ã¯ãç£èŠæ©èœã䜿çšããäœã¬ãã«ã®ã¢ãããŒãã«åŸããŸãã
$ watchãå®è£
ããã«ã¯ ãç»é²ããããã¹ãŠã®ãªãã¶ãŒããŒãã©ããã«ä¿åããå¿
èŠããããŸãã ãããã®é
åã
Scopeã³ã³ã¹ãã©ã¯ã¿ãŒã«è¿œå ããŸãããã
function Scope() { this.$$watchers = []; }
$$ãã¬ãã£ãã¯ã¹ã¯ã倿°ãAngularãã¬ãŒã ã¯ãŒã¯ã§ãã©ã€ããŒãã§ãããã¢ããªã±ãŒã·ã§ã³ã³ãŒãããåŒã³åºãããªãããšãæå³ããŸãã
ããã§ã颿°
$ watchãå®çŸ©ã§ããŸãã 2ã€ã®é¢æ°ãåŒæ°ãšããŠåãåãã
$$ãŠã©ããã£ãŒé
åã«æ ŒçŽããŸãã ãã¹ãŠã®ã¹ã³ãŒããªããžã§ã¯ãããã®é¢æ°ãå¿
èŠãšããããšãæ³å®ãããŠããã®ã§ããããã¿ã€ãã«å
¥ããŸãããã
Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); };
ã³ã€ã³ã®è£åŽã¯
$ãã€ãžã§ã¹ãæ©èœã§ãã ç¹å®ã®ã¹ã³ãŒãã«ç»é²ãããŠãããã¹ãŠã®ãªãã¶ãŒããŒãèµ·åããŸãã ãã¹ãŠã®ãªãã¶ãŒããŒãåçŽã«ãœãŒããããããããããªã¹ããŒé¢æ°ãåŒã³åºãæãåçŽãªå®è£
ã説æããŸãããã
Scope.prototype.$digest = function() { _.forEach(this.$$watchers, function(watch) { watch.listenerFn(); }); };
ããã§ããªãã¶ãŒããŒãç»é²ãã
$ digestãå®è¡ããŠããã®çµæãšããŠãªã¹ããŒé¢æ°ãæ©èœããããã«ãªããŸãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { _.forEach(this.$$watchers, function(watch) { watch.listenerFn(); }); }; var scope = new Scope(); scope.$watch( function() {console.log('watchFn'); }, function() {console.log('listener'); } ); scope.$digest(); scope.$digest(); scope.$digest();
ã³ã³ãœãŒã«ïŒ
ããªã¹ããŒã
ããªã¹ããŒã
ããªã¹ããŒã
ããèªäœã¯ç¹ã«æçšã§ã¯ãããŸããã ãŠã©ãã颿°ã§ç€ºãããããŒã¿ãå®éã«å€æŽãããå Žåã«ã®ã¿ããã³ãã©ãŒãèµ·åããããšãæ¬åœã«æãŸããŸãã
ããŒã¿å€æŽæ€åº
åè¿°ã®ããã«ããªãã¶ãŒããŒã®ç£èŠæ©èœã¯ã倿Žãé¢å¿ã®ããããŒã¿ãè¿ãå¿
èŠããããŸãã éåžžããã®ããŒã¿ã¯ã¹ã³ãŒãå
ã«ããããã䟿å®äžãã¹ã³ãŒãã¯åŒæ°ãšããŠæž¡ãããŸãã ã¹ã³ãŒããã
firstNameãç£èŠããç£èŠé¢æ°ã¯ã次ã®ããã«ãªããŸãã
function(scope) { return scope.firstName; }
ã»ãšãã©ã®å Žåãç£èŠé¢æ°ã¯æ¬¡ã®ããã«ãªããŸãã察象ã®ããŒã¿ãã¹ã³ãŒãããæœåºããŠè¿ããŸãã
$ãã€ãžã§ã¹ãã®æ©èœã¯ããã®ç£èŠé¢æ°ãåŒã³åºããããããåãåã£ãå€ãšååè¿ãããå€ãæ¯èŒããããšã§ãã å€ãç°ãªãå ŽåãããŒã¿ã¯ãããŒãã£ãã§ããã察å¿ãããªã¹ããŒé¢æ°ãåŒã³åºãå¿
èŠããããŸãã
ãããè¡ãã«ã¯ã
$ãã€ãžã§ã¹ãã¯åç£èŠé¢æ°ã®æåŸã«è¿ãããå€ãèšæ¶ããå¿
èŠããããŸãããŸããåãªãã¶ãŒããŒã«ç¬èªã®ãªããžã§ã¯ããæ¢ã«ããããããã®ããŒã¿ãæ ŒçŽããã®ãæã䟿å©ã§ãã 以äžã«ã
$ãã€ãžã§ã¹ã颿°ã®æ°ããå®è£
ã瀺ããŸããããã¯ãåãªãã¶ãŒããŒã®å€æŽã«ã€ããŠããŒã¿ããã§ãã¯ããŸãã
Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); };
ç£èŠé¢æ°ã¯åãªãã¶ãŒããŒã«å¯ŸããŠåŒã³åºãããçŸåšã®ã¹ã³ãŒããåŒæ°ãšããŠæž¡ããŸãã ããã«ãååŸããå€ã¯ã
æåŸã®å±æ§ã«æ ŒçŽãããŠãã以åã®å€ãšæ¯èŒãããŸãã å€ãç°ãªãå Žåããªã¹ããŒãåŒã³åºãããŸãã 䟿å®äžãå€ãšã¹ã³ãŒãã®äž¡æ¹ãåŒæ°ãšããŠãªã¹ããŒã«æž¡ãããŸãã
æåŸã«ããªãã¶ãŒãã®
æåŸã®å±æ§ã«æ°ããå€ãæžã蟌ãŸãããããæ¬¡åã®æ¯èŒãå¯èœã«ãªããŸãã
$ãã€ãžã§ã¹ããåŒã³åºããããšãã«ããã®å®è£
ã§ãªã¹ããŒãã©ã®ããã«ããªã¬ãŒãããããèŠãŠã¿ãŸãããã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } );
ã³ã³ãœãŒã«ïŒ
æ¬åœ
æ¬åœ
æ¬åœ
æ¬åœ
ããã§ãAngularã¹ã³ãŒãã®ã³ã¢ãæ¢ã«å®è£
ããŸããããªãã¶ãŒããŒãç»é²ãã
$ãã€ãžã§ã¹ã颿°ã§ããããèµ·åããŸãã
ããã§ãAngularã®ã¹ã³ãŒãã®ããã©ãŒãã³ã¹ã«é¢ããããã€ãã®çµè«ãå°ãåºãããšãã§ããŸãã
- ããŒã¿ãã¹ã³ãŒãã«è¿œå ããŠããããèªäœãããã©ãŒãã³ã¹ã«åœ±é¿ããããšã¯ãããŸããã ããŒã¿ãç£èŠãããªãã¶ãŒããŒãããªãå ŽåãããŒã¿ãã¹ã³ãŒãã«è¿œå ãããŠãããã©ããã«éãã¯ãããŸããã Angularã¯ã¹ã³ãŒãããããã£ãå埩åŠçããããªãã¶ãŒããŒãå埩åŠçããŸãã
- $ãã€ãžã§ã¹ãã®å®è¡äžã«ãåç£èŠé¢æ°ãåŒã³åºãå¿
èŠããããŸãã ãã®ãããããªããäœäººã®ãªãã¶ãŒããŒãæã£ãŠãããã ãã§ãªããåæèšæ©èœèªäœã®ããã©ãŒãã³ã¹ã«ã泚æãæãããšã¯çã«ããªã£ãŠããŸãã
ãã€ãžã§ã¹ãã¢ã©ãŒã
$ãã€ãžã§ã¹ããå®è¡ãããŠãããšããã¢ã©ãŒããåä¿¡ããå¿
èŠãããå Žåã
ãã€ãžã§ã¹ããåŠçããéçšã§åç£èŠæ©èœã確å®ã«èµ·åãããšããäºå®ãå©çšã§ããŸãã ãªã¹ããŒãªãã§ç£èŠæ©èœãç»é²ããã ãã§ãã
ãããèæ
®ããã«ã¯ããªã¹ããŒãã¹ããããããŠããªããã©ããã
$ watch颿°ã§ãã§ãã¯ã€ã³ããå¿
èŠããããŸãããã§ãã¯ã€ã³ãããŠããå Žåã¯ã代ããã«ã¹ã¿ã颿°ã眮ãæããŸãã
Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); };
ãã®ãã³ãã¬ãŒãã䜿çšããå ŽåãAngularã¯ããªã¹ããŒé¢æ°ã宣èšãããŠããªãå Žåã§ããç£èŠé¢æ°ããè¿ãããå€ãèæ
®ããããšã«æ³šæããŠãã ããã å€ãè¿ããšã倿Žã®ãã§ãã¯ã«åå ããŸãã äžèŠãªäœæ¥ãåŒãèµ·ãããªãããã«-颿°ããäœãè¿ããªãã§ãã ãã
ãundefinedã¯åžžã«ããã©ã«ãã§è¿ãããŸãïŒ
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.$watch(function() { console.log('digest listener fired'); }); scope.$digest(); scope.$digest(); scope.$digest();
ã³ã³ãœãŒã«ïŒ
ããã€ãžã§ã¹ããªã¹ããŒè§£éã
ããã€ãžã§ã¹ããªã¹ããŒè§£éã
ããã€ãžã§ã¹ããªã¹ããŒè§£éã
ã«ãŒãã«ã®æºåã¯ã§ããŠããŸããããŸã å®å
šã§ã¯ãããŸããã ããšãã°ãããªãå
žåçãªã·ããªãªã¯èæ
®ãããŠããŸããããªã¹ããŒé¢æ°èªäœãã¹ã³ãŒãããããããã£ã倿Žã§ããŸãã ãããçºçããå¥ã®ãªãã¶ãŒããŒããã®ããããã£ãç£èŠããå Žåãå°ãªããšããã®ããã»ãŒãž
$ digestã§ããã®ãªãã¶ãŒããŒã倿Žã®éç¥ãåãåããªãããšã倿ããå ŽåããããŸãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {} }; this.$$watchers.push(watcher); }; Scope.prototype.$digest = function() { var self = this; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); } watch.last = newValue; }); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.counter; }, function(newValue, oldValue, scope) { scope.counterIsTwo = (newValue === 2); } ); scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } );
ã³ã³ãœãŒã«ïŒ
æ¬åœ
æ¬åœ
åœ
æ¬åœ
ãããä¿®æ£ããŸãããã
ããŒãã£ããŒã¿ãããéãã$ãã€ãžã§ã¹ããå®è¡ããŸã
ç£èŠãããå€ãå€åããªããªããŸã§ãã§ãã¯ãç¶ããããã«ã
$ãã€ãžã§ã¹ããä¿®æ£ããå¿
èŠããããŸãã
æåã«ãçŸåšã®$ãã€ãžã§ã¹ã颿°ã®ååã
$$ digestOnceã«å€æŽãããã¹ãŠã®ç£èŠé¢æ°ã1åå®è¡ããŠãç£èŠãã£ãŒã«ãã®å€ã«å°ãªããšã1ã€ã®å€æŽããã£ããã©ããã瀺ãããŒã«å€æ°ãè¿ãããã«å€æŽããŸãã
Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { var newValue = watch.watchFn(self); var oldValue = watch.last; if (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; };
ãã®åŸã
$ãã€ãžã§ã¹ã颿°ãå宣èšããŠã倿Žãããéãã«ãŒãå
ã§
$$ digestOnceãéå§ãããããã«ããŸãã
Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); };
$ãã€ãžã§ã¹ãã¯ãç»é²ãããç£èŠæ©èœãå°ãªããšã1åå®è¡ããããã«ãªããŸããã æåã®ãã¹ã§èŠ³æž¬å€ã®ããããã倿Žãããå Žåããã¹ã¯ãããŒãã£ããšããŠããŒã¯ããã2çªç®ã®ãã¹ãéå§ãããŸãã ããã¯ãåäžã®å€æŽãããå€ãããã»ãŒãžå
šäœã§æ€åºããããŸã§èµ·ãããŸã-ç¶æ³ã¯å®å®ããŸãã
Angularã®ã¹ã³ãŒãã«ã¯ãå®éã«ã¯$$ digestOnce颿°ã¯ãããŸããã 代ããã«ããã®æ©èœã¯$ãã€ãžã§ã¹ãã§çŽæ¥ã«ãŒãã«çµã¿èŸŒãŸããŸãã ç§ãã¡ã®ç®çã§ã¯ãããã©ãŒãã³ã¹ãããæå¿«ããšèªã¿ããããéèŠã§ãããã®ããããªãã¡ã¯ã¿ãªã³ã°ãå°ãè¡ããŸããã
åäœäžã®æ°ããå®è£
ã¯æ¬¡ã®ãšããã§ãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn ||function() { } }; 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 (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); }; var scope = new Scope(); scope.firstName = 'Joe'; scope.counter = 0; scope.$watch( function(scope) { return scope.counter; }, function(newValue, oldValue, scope) { scope.counterIsTwo = (newValue === 2); } ); scope.$watch( function(scope) { return scope.firstName; }, function(newValue, oldValue, scope) { scope.counter++; } );
ã³ã³ãœãŒã«ïŒ
æ¬åœ
æ¬åœ
æ¬åœ
ãŠã©ããæ©èœã«é¢ããŠãã1ã€ã®éèŠãªçµè«ãåºãããšãã§ããŸãããããã¯ã$ãã€ãžã§ã¹ãæäœäžã«æ°ååäœããããšããããŸãã ãã®ãããæèšæ©èœã¯ã¹ãçã§ãããšããèšãããŸããæ©èœã«å¯äœçšããªãããäœåãæ£åžžã«æ©èœãããããªå¯äœçšãããã¯ãã§ãã ããšãã°ãç£èŠæ©èœã«AJAXãªã¯ãšã¹ããããå Žåããã®ãªã¯ãšã¹ããäœåå®è¡ããããã«ã€ããŠã®ä¿èšŒã¯ãããŸããã
çŸåšã®å®è£
ã«ã¯1ã€ã®å€§ããªæ¬ é¥ããããŸãã2人ã®ãªãã¶ãŒããŒãäºãã®å€æŽãç£èŠãããšã©ããªããŸããïŒ ãã®å Žåãç¶æ³ã¯æ±ºããŠå®å®ããŸãããïŒ åæ§ã®ç¶æ³ã以äžã®ã³ãŒãã«å®è£
ãããŠããŸãã ãã®äŸã§ã¯ã
$ãã€ãžã§ã¹ãã®åŒã³åºã
ã¯ã³ã¡ã³ãåãããŠããŸãã
ã³ã¡ã³ããå€ããŠãäœãèµ·ãããã調ã¹ãŸãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; 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 (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function() { var dirty; do { dirty = this.$$digestOnce(); } while (dirty); }; var scope = new Scope(); scope.counter1 = 0; scope.counter2 = 0; scope.$watch( function(scope) { return scope.counter1; }, function(newValue, oldValue, scope) { scope.counter2++; } ); scope.$watch( function(scope) { return scope.counter2; }, function(newValue, oldValue, scope) { scope.counter1++; } );
ã³ã³ãœãŒã«ïŒ
0
JSBinã¯ãã°ãããããšæ©èœã忢ããŸãïŒç§ã®ãã·ã³ã§ã¯çŽ100,000åã®å埩ãè¡ãããŸãïŒã ããšãã°ãnode.jsã®äžã§ãã®ã³ãŒããå®è¡ãããšãæ°žä¹
ã«å®è¡ãããŸãã
$ãã€ãžã§ã¹ãã®äžå®å®æ§ãåãé€ã
å¿
èŠãªã®ã¯ã
$ãã€ãžã§ã¹ããç¹å®ã®ååŸ©åæ°ã«å¶é
ããããšã ãã§ãã ç¹°ãè¿ããçµäºããŠãã¹ã³ãŒããå€åãç¶ããå Žåã¯ãæãæããŠãããããŸã-ç¶æ
ã¯æ±ºããŠå®å®ããŸããã ãã®ç¶æ³ã§ã¯ãã¹ã³ãŒãã®ç¶æ
ã¯æããã«ãŠãŒã¶ãŒãæåŸ
ãããã®ã§ã¯ãªããããäŸå€ãã¹ããŒãããå¯èœæ§ããããŸãã
ååŸ©ã®æå€§æ°ã¯TTLïŒåç¶æé-ã©ã€ãã¿ã€ã ã®ç¥ïŒãšåŒã°ããŸãã ããã©ã«ãã§ã¯10ã«èšå®ããŸãããã®æ°åã¯å°ããããã«èŠããŸãïŒçŽ100,000åãã€ãžã§ã¹ããå®è¡ããŸããïŒããããã¯ãã§ã«ããã©ãŒãã³ã¹ã®åé¡ã§ããããšã«æ³šæããŠãã ããã ããã«ããŠãŒã¶ãŒã10åãè¶
ãããŠã©ããæ©èœããã§ãŒã³ã«é
眮ããããšã¯ã»ãšãã©ãããŸããã
è§åºŠTTLã¯ã«ã¹ã¿ãã€ãºå¯èœã§ãã ããã«ã€ããŠã¯ããããã€ããŒãšäŸåæ§æ³šå
¥ã«ã€ããŠèª¬æããä»åŸã®èšäºã§èª¬æããŸãã
ç¶ããŸããã-ãã€ãžã§ã¹ãã«ãŒãã«ã«ãŠã³ã¿ãŒã远å ããŸãããã TTLã«éããå Žå-äŸå€ãã¹ããŒããŸãïŒ
Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); };
åã®äŸã®æŽæ°ããŒãžã§ã³ã¯ãäŸå€ãã¹ããŒããŸãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { } }; 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 (newValue !== oldValue) { watch.listenerFn(newValue, oldValue, self); dirty = true; } watch.last = newValue; }); return dirty; }; Scope.prototype.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.counter1 = 0; scope.counter2 = 0; scope.$watch( function(scope) { return scope.counter1; }, function(newValue, oldValue, scope) { scope.counter2++; } ); scope.$watch( function(scope) { return scope.counter2; }, function(newValue, oldValue, scope) { scope.counter1++; } ); scope.$digest();
ã³ã³ãœãŒã«ïŒ
ã10åã®ãã€ãžã§ã¹ãã®ç¹°ãè¿ãã«å°éããªãã£ãïŒ36è¡ç®ïŒã
ãã€ãžã§ã¹ãã¯åºå®ãåãé€ããŸããã
ããã§ãäœãã倿Žãããããšãæ£ç¢ºã«å€æãã
æ¹æ³ãèŠãŠã¿ãŸãããã
å€ã«ãã倿Žã確èªãã
çŸæç¹ã§ã¯ãå³å¯ãªç䟡æŒç®å
===ã䜿çšããŠæ°ããå€ãšå€ãå€ãæ¯èŒããŸãã ã»ãšãã©ã®å Žåãããã¯æ©èœããŸããããªããã£ãåïŒæ°å€ãæååãªã©ïŒã®å€æŽã¯éåžžæ€åºããããªããžã§ã¯ããŸãã¯é
åãå¥ã®ãã®ã«çœ®ãæãããããã©ãããæ±ºå®ãããŸãã ãã ããAngularã«ã¯å€æŽãæ€åºããå¥ã®æ¹æ³ããããé
åãŸãã¯ãªããžã§ã¯ãå
ã§äœãã倿Žããããã©ããã確èªã§ããŸãã ãããè¡ãã«ã¯
ãåç
§ã§ã¯ãªã
å€ã§æ¯èŒããå¿
èŠããã
ãŸã ã
ãã®ã¿ã€ãã®æ€èšŒã¯ãããŒã«åã®ãªãã·ã§ã³ã®3çªç®ã®ãã©ã¡ãŒã¿ãŒã
$ watch颿°ã«æž¡ãããšã§ã¢ã¯ãã£ãã«ã§ããŸãã ãã®ãã©ã°ãtrueã®å Žåãå€ãã§ãã¯ã䜿çšãããŸãã
$ watchã倿ŽããŸããã-ãã©ã°ãååŸããŠããªãã¶ãŒããŒïŒ
watcher倿°ïŒã«ä¿åããŸãã
Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn, valueEq: !!valueEq }; this.$$watchers.push(watcher); };
ãªãã¶ãŒããŒã«ãã©ã°ã远å ããäºéåŠå®ã䜿çšããŠåŒ·å¶çã«ããŒã«åã«ãã£ã¹ãããŸããã ãŠãŒã¶ãŒã3çªç®ã®ãã©ã¡ãŒã¿ãŒãªãã§
$ watchãåŒã³åºããšã
valueEqã¯
undefinedã«ãªãããŠã©ããã£ãŒãªããžã§ã¯ãã§
falseã«å€æãããŸãã
å€ã«ãããã§ãã¯ã¯ãå€ããªããžã§ã¯ããŸãã¯é
åã§ããå Žåãå€ãã³ã³ãã³ããšæ°ããã³ã³ãã³ãã®äž¡æ¹ã調ã¹ãå¿
èŠãããããšãæå³ããŸãã éããããå Žåããªãã¶ãŒããŒã¯ãããŒãã£ããšããŠããŒã¯ãããŸãã ãã¹ãããããªããžã§ã¯ããŸãã¯é
åãã³ã³ãã³ãå
ã§çºçããå¯èœæ§ããããŸãããã®å Žåãå€ã«ãã£ãŠååž°çã«ãã§ãã¯ããå¿
èŠããããŸãã
Angularã«ã¯
ç¬èªã®å€æ¯èŒé¢æ°ããããŸãã
ãLo-Dashã® é¢æ°ã䜿çšããŸãã å€ã®ãã¢ãšãã©ã°ãåãæ¯èŒé¢æ°ãæžããŸãããïŒ
Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue; } };
ãå€ã«ãããå€åã倿ããããã«ãã¬ãŒã«ãã¬ã³ãããå€ãå€ããä¿åããããšãå¿
èŠã§ãã çŸåšã®å€ãžã®ãªã³ã¯ãä¿åããã ãã§ã¯ååã§ã¯ãããŸããã倿Žãå ãããããšãåç
§ã«ãã£ãŠä¿åããããªããžã§ã¯ãã«ãåæ ãããŸãã åãããŒã¿ãžã®2ã€ã®ãªã³ã¯ãåžžã«
$$ areEqual颿°ã«è©²åœããå Žåãäœãã倿Žããããã©ããã倿ã§ããŸããã ãããã£ãŠãã³ã³ãã³ãã®è©³çްã³ããŒãäœæãããã®ã³ããŒãä¿åããå¿
èŠããããŸãã
æ¯èŒ
颿°ã®å Žåãšåæ§ã«ãAngularã«ã¯
ããŒã¿ããã£ãŒãã³ããŒããããã®ç¬èªã®é¢æ°ããããŸããã
Lo-Dashã®åã 颿°ã䜿çšããŸãã æ¯èŒã®ããã«
$$ areEqualã䜿çšããå¿
èŠã«å¿ããŠ
æåŸã«ã³ããŒãäœæ
ããããã«
$$ digestOnceãæ¹è¯ããŸãããã
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; };
ããã§ãå€ãæ¯èŒãã2ã€ã®æ¹æ³ã®éãã確èªã§ããŸãã
JS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { }, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue; } }; 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.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.counterByRef = 0; scope.counterByValue = 0; scope.value = [1, 2, {three: [4, 5]}];
ã³ã³ãœãŒã«ïŒæ¬åœ
æ¬åœ
æ¬åœ
æ¬åœ
æ¬åœ
æ¬åœ
å€ã«ããæ€èšŒã¯ãåç
§ã«ããæ€èšŒãããæããã«ãªãœãŒã¹ã«å³ããã§ãããã¹ããããæ§é ã®ç¹°ãè¿ãã«ã¯æéããããããªããžã§ã¯ãã®ã³ããŒãä¿åãããšã¡ã¢ãªã®æ¶è²»éãå¢ããŸããããããAngularãããã©ã«ãã§ããã©ã«ãã®æ€èšŒã䜿çšããªãçç±ã§ãããã©ã°ãæç€ºçã«èšå®ããå¿
èŠããããŸãããŸããAngularã«ã¯ãå€ã®å€æŽããã§ãã¯ããããã®3çªç®ã®ã¡ã«ããºã ãã³ã¬ã¯ã·ã§ã³ã®ç£èŠãããããŸããå€ã«ãããã§ãã¯ã®ã¡ã«ããºã ãšåæ§ã«ããªããžã§ã¯ããšé
åã®å€æŽãæããã«ããŸããããããšã¯ç°ãªãããã§ãã¯ã¯ãã¹ããããã¬ãã«ã«æ·±ãå
¥ãããšãªãç°¡åã§ããèªç¶ã«é«éã§ããã³ã¬ã¯ã·ã§ã³ã®ç£èŠã¯ã$ watchCollection颿°ã䜿çšããŠå©çšã§ããŸãããã®å®è£
ã«ã€ããŠã¯ãã·ãªãŒãºã®æ¬¡ã®èšäºã§èª¬æããŸãã
å€ã®æ¯èŒãå®äºããåã«ãJavaScriptã®1ã€ã®æ©èœãæ€èšããå¿
èŠããããŸããNaNå€
JavaScriptã§ã¯ãå€NaNïŒæ°å€ã§ã¯ãªãïŒã¯ããèªäœãšçãããããŸãããããããããã§ãããããããã¯å¥åŠã«èããããããããŸããã倿Žã®å€ããã§ãã¯ãã颿°ã§NaNãæåã§åŠçããªãã£ããããNaNãç£èŠããç£èŠé¢æ°ã¯åžžã«ãªãã¶ãŒããŒããããŒãã£ããšããŠããŒã¯ããŸãããå€ã«ããããã¹ãã§ã¯ããã®ã±ãŒã¹ã¯Lo-Dashã®isEqual颿°ã§æ¢ã«èæ
®ãããŠããŸãããåç
§ã«ããããã§ãã¯ã§ã¯ãèªåã§è¡ãå¿
èŠããããŸããããã§ã¯ã$$ areEqual颿°ãæ¹è¯ããŸãããã 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)); } };
NaNãæã€ãªãã¶ãŒããŒã¯æåŸ
ã©ããã«åäœããããã«ãªããŸããïŒJS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; var scope = new Scope(); scope.number = 0; scope.counter = 0; scope.$watch( function(scope) { return scope.number; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1); scope.number = parseInt('wat', 10);
ã³ã³ãœãŒã«ïŒæ¬åœ
æ¬åœ
ããã§ãå€ããã§ãã¯ããããšãããã¢ããªã±ãŒã·ã§ã³ã³ãŒãããã¹ã³ãŒããæäœããæ¹æ³ã«çŠç¹ãç§»ããŸãããã$ eval-ã¹ã³ãŒãã³ã³ããã¹ãã§ã®ã³ãŒãå®è¡
Angularã«ã¯ãã¹ã³ãŒãã³ã³ããã¹ãã§ã³ãŒããå®è¡ããããã®ãªãã·ã§ã³ãããã€ããããŸãããããã®äžã§æãåçŽãªã®ã¯ã$ eval颿°ã§ããããã¯é¢æ°ãåŒæ°ãšããŠåãããããè¡ãããšã¯ãã ã¡ã«ãããåŒã³åºãããšã§ãããçŸåšã®ã¹ã³ãŒãããã©ã¡ãŒã¿ãŒãšããŠæž¡ããŸããããã§ã¯ãå®è¡çµæãè¿ããŸãã$ evalã¯2çªç®ã®ãã©ã¡ãŒã¿ãŒãåãåããŸãããã®ãã©ã¡ãŒã¿ãŒã¯ãåŒã³åºããã颿°ã«ãã®ãŸãŸæž¡ãããŸãã$ evalã®å®è£
ã¯éåžžã«ç°¡åã§ãïŒ Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); };
$ evalã®äœ¿çšãéåžžã«ç°¡åã§ãïŒJS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; var scope = new Scope(); scope.number = 1; scope.$eval(function(theScope) { console.log('Number during $eval:', theScope.number); });
ã³ã³ãœãŒã«ïŒã$è©äŸ¡äžã®æ°ïŒã
1
? ,
$eval , scope.
$eval $apply , .
,
$eval ââ .
$watch ,
$eval . scope. , .
$apply â $digest
ãããã$ applyã¯ããã¹ãŠã®Scope颿°ã®äžã§æãæåã§ããããã¯ããµãŒãããŒãã£ã®ã©ã€ãã©ãªãAngularãšçµ±åããæšæºçãªæ¹æ³ãšããŠäœçœ®ä»ããããŠããŸãããããŠãããã«ã¯çç±ããããŸãã$ applyã¯é¢æ°ãåŒæ°ãšããŠåãå
¥ãã$ evalã䜿çšããŠãã®é¢æ°ãåŒã³åºããŸãããæçµçã«ã¯$ digestãéå§ããŸããæãç°¡åãªå®è£
ãæ¬¡ã«ç€ºããŸãã Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } };
颿°ã§äŸå€ãçºçããå Žåã§ããäŸåé¢ä¿ãæŽæ°ããããã«ãfinallyãããã¯ã§$ãã€ãžã§ã¹ããåŒã³åºãããŸãã$ applyã䜿çšãããšãAngularã«éŠŽæã¿ã®ãªãã³ãŒããå®è¡ã§ããŸãããã®ã³ãŒãã¯ã¹ã³ãŒãå
ã®ããŒã¿ã倿Žã§ãã$ applyã¯ãªãã¶ãŒããŒããããã®å€æŽã確å®ã«ãã£ããããããã«ããŸãããããã¯ããAngularã©ã€ããµã€ã¯ã«ãžã®ã³ãŒãã®çµ±åãã«ã€ããŠè©±ããšãã®æå³ã§ããããã¯ãã以äžã®ãã®ã§ã¯ãããŸããã$å®è¡äžã«é©çšïŒJS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$digest = function(){ var ttl = 10; var dirty; do { dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } }; var scope = new Scope(); scope.counter = 0; scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$apply(function(scope) { scope.aValue = 'Hello from "outside"'; }); console.assert(scope.counter === 1);
ã³ã³ãœãŒã«ïŒæ¬åœ
é
å»¶å®è¡-$ evalAsync
JavaScriptã§ã¯ãå€ãã®å ŽåããåŸã§ãã³ãŒããå®è¡ããå¿
èŠããããŸããã€ãŸããçŸåšã®å®è¡ã³ã³ããã¹ãå
ã®ãã¹ãŠã®ã³ãŒããå®è¡ããããŸã§å®è¡ãé
ãããŸããããã¯éåžžããŒãïŒãŸãã¯ãŒãã«è¿ãïŒé
å»¶ã§SetTimeoutïŒïŒã䜿çšããŠè¡ãããŸãããã®ããªãã¯ã¯Angularã¢ããªã±ãŒã·ã§ã³ã§ãæ©èœããŸãããããã«ã¯$ã¿ã€ã ã¢ãŠããµãŒãã¹ ã䜿çšããããšããå§ãããŸããããã¯ãç¹ã«$ applyã䜿çšããŠãé
延颿°ã®åŒã³åºãããã€ãžã§ã¹ãã«ãŒãã«çµ±åããŸããããããAngularã§ã³ãŒãã®å®è¡ãå»¶æããå¥ã®æ¹æ³ããããŸã- $ evalAsync颿°ã颿°ããã©ã¡ãŒã¿ãŒãšããŠåãåããåŸã§ç¢ºå®ã«å®è¡ãããŸãããçŸåšã®ãã€ãžã§ã¹ããµã€ã¯ã«å
ã§çŽæ¥å®è¡ãããå ŽåïŒçŸåšå®è¡äžã®å ŽåïŒããŸãã¯æ¬¡ã®ãã€ãžã§ã¹ããµã€ã¯ã«ã®çŽåã«å®è¡ãããŸããããšãã°ããªãã¶ãŒããŒã®ãªã¹ããŒé¢æ°ããã³ãŒãã®å®è¡ãçŽæ¥å»¶æããããšãã§ããŸããã³ãŒããé
å»¶ããŠããã«ããããããããã€ãžã§ã¹ãã«ãŒãã®æ¬¡ã®å埩ã§å®è¡ãããããšãç¥ã£ãŠããŸãããŸãã$$ evalAsyncãä»ããŠå»¶æãããã¿ã¹ã¯ãä¿åããå Žæã決å®ããå¿
èŠããããŸããScopeã³ã³ã¹ãã©ã¯ã¿ãŒã§åæåããããšã§ãããã«é
åã䜿çšã§ããŸãã function Scope() { this.$$watchers = []; this.$$asyncQueue = []; }
次ã«ããã¥ãŒã«é¢æ°ã远å ãã$ evalAsyncèªäœãèšè¿°ããŸãã Scope.prototype.$evalAsync = function(expr) { this.$$asyncQueue.push({scope: this, expression: expr}); };
ãã¥ãŒãªããžã§ã¯ãã«ã¹ã³ãŒããæç€ºçã«è¿œå ããçç±ã¯ãã¹ã³ãŒãã®ç¶æ¿ã«ãããã®ã§ããããã«ã€ããŠã¯ããã®ã·ãªãŒãºã®æ¬¡åã®èšäºã§èª¬æããŸãã
ããã§ã$ãã€ãžã§ã¹ãã§æåã«è¡ãããšã¯ãé
å»¶éå§ãã¥ãŒã«ãããã¹ãŠã®é¢æ°ãæœåºãã$ evalã䜿çšããŠããããå®è¡ããããšã§ãã Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); };
ãã®å®è£
ã«ããã颿°ã®å®è¡ãé
å»¶ãããã¹ã³ãŒããããŒãã£ãšããŠããŒã¯ãããå Žåããã®é¢æ°ã¯åããã€ãžã§ã¹ãã«ãŒãã§é
å»¶ãšåŒã°ããŸãã$ evalAsyncã®äœ¿ç𿹿³ã®äŸã次ã«ç€ºããŸããJS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; } Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$digest = function() { var ttl = 10; var dirty; do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { this.$$asyncQueue.push({scope: this, expression: expr}); }; var scope = new Scope(); scope.asyncEvaled = false; scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; scope.$evalAsync(function(scope) { scope.asyncEvaled = true; }); console.log("Evaled inside listener: "+scope.asyncEvaled); } ); scope.aValue = "test"; scope.$digest(); console.log("Evaled after digest: "+scope.asyncEvaled);
ã³ã³ãœãŒã«ïŒããªã¹ããŒå
ã§è©äŸ¡ïŒfalseã
ããã€ãžã§ã¹ãåŸã«è©äŸ¡ïŒtrueã
ã¹ã³ãŒãå
ã®ãã§ãŒãº
$ evalAsync颿°ã¯å¥ã®ããšãè¡ããŸãããã€ãžã§ã¹ããå®è¡ãããŠããªãå Žåã¯ããã€ãžã§ã¹ããå®è¡ããããã«ã¹ã±ãžã¥ãŒã«ããå¿
èŠããããŸãããã®ç¹ã¯ã$ evalAsyncãåŒã³åºããšãã¯ãã€ã§ããä»ã®äœãããã€ãžã§ã¹ããéå§ãããšãã§ã¯ãªããé
延颿°ããããªãããã«ãå®è¡ãããããšã確èªããå¿
èŠããããšããããšã§ãã$ evalAsyncã¯ããã€ãžã§ã¹ããçŸåšå®è¡äžãã©ãããäœããã®åœ¢ã§çè§£ããå¿
èŠããããŸãããã®ç®çã®ããã«ãAngularã¹ã³ãŒãã¯ããã§ãŒãºããšåŒã°ããã¡ã«ããºã ãå®è£
ããŸããããã¯ãã¹ã³ãŒãå
ã®éåžžã®æååã§ãããçŸåšäœãèµ·ãã£ãŠãããã«é¢ããæ
å ±ãä¿åããŸãã$$ãã§ãŒãºãã£ãŒã«ãã$ã¹ã³ãŒãã³ã³ã¹ãã©ã¯ã¿ãŒã«è¿œå ããnullã«èšå®ããŸãã function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$phase = null; }
次ã«ããã§ãŒãºãå¶åŸ¡ããããã€ãã®é¢æ°ãäœæããŠã¿ãŸãããã1ã€ã¯ã€ã³ã¹ããŒã«çšããã1ã€ã¯ã¯ãªãŒãã³ã°çšã§ãããŸããåã®ãã§ãŒãºãå®äºããã«ãã§ãŒãºã確ç«ããããšããŠããªãããšã確èªãã远å ã®ãã§ãã¯ã远å ããŸãã 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(); };
ããã«ããéã«ã$ applyãåæã«ç¢ºå®ããŠããã§ãŒãºãããã«èšè¿°ããŸããããã¯ããããã°ããã»ã¹ã§åœ¹ç«ã¡ãŸãã Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } };
ããã§ãæçµçã«$ evalAsync颿°ã§$ digestã®åŒã³åºããã¹ã±ãžã¥ãŒã«ã§ããŸããããã§ã¯ããã§ãŒãºã空ã®å ŽåïŒããã³éåæã¿ã¹ã¯ããŸã èšç»ãããŠããªãå ŽåïŒãã§ãŒãºããã§ãã¯ããå¿
èŠããããŸã- $ digestãå®è¡ããäºå®ã§ãïŒ Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); };
ãã®å®è£
ã§ã¯ã$ evalAsyncãåŒã³åºãããšã«ãããåŒã³åºãå
ã«é¢ä¿ãªããè¿ãå°æ¥ãã€ãžã§ã¹ããçºçããããšã確èªã§ããŸããJSBinã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$phase = null; } 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.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$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(); }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; var scope = new Scope(); scope.asyncEvaled = false; scope.$evalAsync(function(scope) { scope.asyncEvaled = true; }); setTimeout(function() { console.log("Evaled after a while: "+scope.asyncEvaled); }, 100);
ã³ã³ãœãŒã«ïŒããã°ããããŠè©äŸ¡ïŒtrueã
ãã€ãžã§ã¹ãåŸã®ã³ãŒãã®å®è¡-$$ postDigest
digest- â
$$postDigest .
, Angular-, Angular-. , .
$evalAsync ,
$$postDigest - ââ. , , digest .
$$postDigest $digestããããã£ãŠãé
延颿°ã®èµ·åã¯ããµãŒãããŒãã£ã®ã³ãŒãããã€ãžã§ã¹ããéå§ãããŸã§é
å»¶ããå ŽåããããŸããååã瀺ãããã«ã$$ postDigestã¯ãã€ãžã§ã¹ãã®çŽåŸã«é
延颿°ã®ã¿ãéå§ããããã$$ postDigestã«æž¡ãããã³ãŒãã®ã¹ã³ãŒãã倿Žããå Žåã¯ã$ digestãŸãã¯$ applyãæç€ºçã«äœ¿çšããŠå€æŽããã£ããã¢ããããå¿
èŠããããŸããéå§ããã«ã¯ããã1ã€ã®ãã¥ãŒãScopeã³ã³ã¹ãã©ã¯ã¿ãŒã«è¿œå ããŸããããä»åã¯$$ postDigestã«äœ¿çšããŸãã function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; }
次ã«ã$$ postDigestèªäœãå®è£
ããŸãã圌女ãããããšã¯ãåãå
¥ãããã颿°ããã¥ãŒã«è¿œå ããããšã ãã§ãã Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); };
æåŸã«ã$ digestã®æåŸã§ãäžåºŠã«ãã¹ãŠã®é¢æ°ãåŒã³åºããŠãã¥ãŒãã¯ãªã¢ããå¿
èŠããããŸãã 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()(); } };
ããã§ã¯ã䜿çšããæ¹æ³ã®äžäŸã§ãã$$ postDigestã¯ïŒJSãã³ã³ãŒãã«ã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } 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.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$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.$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.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); var postDigestInvoked = false; scope.$$postDigest(function() { postDigestInvoked = true; }); console.assert(!postDigestInvoked); scope.$digest(); console.assert(postDigestInvoked);
ã³ã³ãœãŒã«ïŒæ¬åœ
æ¬åœ
äŸå€åŠç
çŸåšã®$ã¹ã³ãŒãã®å®è£
ã¯ãAngularããŒãžã§ã³ã«ãŸããŸãè¿ã¥ããŠããŸããããããããã§ããŸã éåžžã«å£ããããã§ããããã¯ãäŸå€åŠçã«ååãªæ³šæãæã£ãŠããªãããã§ããAngularã®ã¹ã³ãŒããªããžã§ã¯ãã¯ããªããšã©ãŒèæ§ããããŸããç£èŠé¢æ°$ evalAsyncãŸãã¯$$ postDigestã§äŸå€ãçºçããŠãããã€ãžã§ã¹ããµã€ã¯ã«ã¯äžæãããŸãããçŸåšã®å®è£
ã§ã¯ããããã®ãšã©ãŒã¯ãã€ãžã§ã¹ãããå€ããŸããtry ... catchã§ããããã¹ãŠã®é¢æ°ã®åŒã³åºããããã¯ã®å
åŽãã©ããããããšã§ããããç°¡åã«ä¿®æ£ã§ããŸããAngularã§ã¯ããããã®ãšã©ãŒã¯ç¹å¥ãªãµãŒãã¹$ exceptionHandlerã«æž¡ãããŸãããŸã ãããŸããã®ã§ãä»ã®ãšããã¯ã³ã³ãœãŒã«ã«ãšã©ãŒãåºåããŸãã
$ evalAsyncããã³$$ postDigestã®äŸå€åŠçã¯ã$ digest颿°ã§è¡ãããŸããã©ã¡ãã®å ŽåããäŸå€ã¯ãã°ã«èšé²ããããã€ãžã§ã¹ãã¯éåžžââã©ããç¶è¡ãããŸãã Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } };
ç£èŠæ©èœã®äŸå€åŠçã¯ã$ digestOnceã§è¡ãããŸãã Scope.prototype.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { 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); } catch (e) { (console.error || console.log)(e); } }); return dirty; };
ãã€ãžã§ã¹ããµã€ã¯ã«ã¯ãäŸå€ãé€ããŠã¯ããã«ä¿¡é Œæ§ãé«ããªããŸããïŒJS Binã³ãŒãã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } 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.$watch = function(watchFn, listenerFn, valueEq) { var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq }; this.$$watchers.push(watcher); }; 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.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { 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); } catch (e) { (console.error || console.log)(e); } }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); scope.aValue = "abc"; scope.counter = 0; scope.$watch(function() { throw "Watch fail"; }); scope.$watch( function(scope) { scope.$evalAsync(function(scope) { throw "async fail"; }); return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1);
ã³ã³ãœãŒã«ïŒããŠã©ãã倱æã
ãéåæå€±æã
ããŠã©ãã倱æã
æ¬åœ
ãªãã¶ãŒããŒã®ç¡å¹å
ãªãã¶ãŒããŒãç»é²ããå Žåãã»ãšãã©ã®å Žåãã¹ã³ãŒããªããžã§ã¯ãã®åç¶æéäžã¢ã¯ãã£ãã«ä¿ã€å¿
èŠããããæç€ºçã«åé€ããå¿
èŠã¯ãããŸããããã ããå Žåã«ãã£ãŠã¯ãã¹ã³ãŒãã¯åŒãç¶ãæ©èœããã¯ãã§ãããäœããã®ãªãã¶ãŒããŒãåé€ããå¿
èŠãããå ŽåããããŸããAngularã®$ watch颿°ã¯ãå®éã«å€ãè¿ããŸã-ãã®åŒã³åºããç»é²æžã¿ãªãã¶ãŒããŒãåé€ãã颿°ããããå®è£
ããããã«å¿
èŠãªã®ã¯ã$ watchã$$ watchersé
åããæ°ããäœæããããªãã¶ãŒããŒãåé€ãã颿°ãè¿ãããšã ãã§ãïŒ Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var self = this; var watcher = { watchFn: watchFn, listenerFn: listenerFn, valueEq: !!valueEq }; self.$$watchers.push(watcher); return function() { var index = self.$$watchers.indexOf(watcher); if (index >= 0) { self.$$watchers.splice(index, 1); } }; };
ããã§ã$ watchããè¿ããã颿°ãèŠããŠãåŸã§ãªãã¶ãŒããŒãç Žæ£ããå¿
èŠããããšãã«åŒã³åºãããšãã§ããŸãïŒJS Bin codeã³ãŒãã衚瀺 function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$phase = null; } 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.$watch = function(watchFn, listenerFn, valueEq) { var self = this; var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() { }, valueEq: !!valueEq }; self.$$watchers.push(watcher); return function() { var index = self.$$watchers.indexOf(watcher); if (index >= 0) { self.$$watchers.splice(index, 1); } }; }; 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.$$digestOnce = function() { var self = this; var dirty; _.forEach(this.$$watchers, function(watch) { try { 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); } catch (e) { (console.error || console.log)(e); } }); return dirty; }; Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } catch (e) { (console.error || console.log)(e); } } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); while (this.$$postDigestQueue.length) { try { this.$$postDigestQueue.shift()(); } catch (e) { (console.error || console.log)(e); } } }; Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); }; Scope.prototype.$apply = function(expr) { try { this.$beginPhase("$apply"); return this.$eval(expr); } finally { this.$clearPhase(); this.$digest(); } }; Scope.prototype.$evalAsync = function(expr) { var self = this; if (!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if (self.$$asyncQueue.length) { self.$digest(); } }, 0); } self.$$asyncQueue.push({scope: self, expression: expr}); }; Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); }; var scope = new Scope(); scope.aValue = "abc"; scope.counter = 0; var removeWatch = scope.$watch( function(scope) { return scope.aValue; }, function(newValue, oldValue, scope) { scope.counter++; } ); scope.$digest(); console.assert(scope.counter === 1); scope.aValue = 'def'; scope.$digest(); console.assert(scope.counter === 2); removeWatch(); scope.aValue = 'ghi'; scope.$digest(); console.assert(scope.counter === 2);
ã³ã³ãœãŒã«ïŒæ¬åœ
æ¬åœ
æ¬åœ
次ã¯äœã§ãã
Angularã®æé«ã®äŒçµ±ã§ãç§ãã¡ã¯é·ãéã®ããæ©ã¿ãã¹ã³ãŒããªããžã§ã¯ãã®åªããå®è£
ãäœæããŸãããããããAngularã®ã¹ã³ãŒããªããžã§ã¯ãã¯ãç§ãã¡ãæã£ãŠãããã®ãããã¯ããã«å€ãã®ãã®ã§ããããããæãéèŠãªããšã¯ãAngularã®ã¹ã³ãŒããå€ç«ããç¬ç«ãããªããžã§ã¯ãã§ã¯ãªãããšã§ããããã©ããããã¹ã³ãŒããªããžã§ã¯ãã¯ä»ã®ã¹ã³ãŒãããç¶æ¿ãããªãã¶ãŒããŒã¯ãæ¥ç¶ãããŠããã¹ã³ãŒãã®ããããã£ã ãã§ãªãã芪ã¹ã³ãŒãã®ããããã£ãç£èŠã§ããŸããå®éãéåžžã«åçŽãªãã®ã¢ãããŒãã¯ãåå¿è
ã«ãšã£ãŠå€ãã®åé¡ã®åå ã§ãããã®ããããã®ã·ãªãŒãºã®æ¬¡åã®èšäºã§ã¯ãã¹ã³ãŒãã®ç¶æ¿ãç ç©¶ã®äž»é¡ã«ãªããŸããå°æ¥çã«ã¯ãScopeã«ãå®è£
ãããŠããã€ãã³ããµãã·ã¹ãã ã«ã€ããŠã説æããŸãã翻蚳è
ããïŒããã¹ãã¯éåžžã«å€§ããããšã©ãŒãã¿ã€ããã¹ããããšæããŸããå人çã«éã£ãŠãã ãã-ç§ã¯ãã¹ãŠãä¿®æ£ããŸãã誰ããããã®ã³ãŒãã®è¡ãéžæããæ¹æ³ãç¥ã£ãŠããå Žåãããšãã°ãããã«ããã³ãŒãã®å¯èªæ§ãåäžããŸãã