咱们在这里不讨论Angular2和Angular4,由于其彻底重写,其实已经不叫AngularJS了。javascript
AngularJS的缺陷:html
angularJS定义是一个当即执行的匿名函数,那么其执行时机为引入angularJS的位置决定。前端
(function(window) { ... })(window);
基于1.6.9java
源码的上部分基本在定义方法,其主要执行开始在源码的最后,主要函数以下: git
bindJQuery(); publishExternalAPI(angular); jqLite(function() { angularInit(window.document, bootstrap); });
尝试绑定jQuery对象,若是没有则采用内置的jqLiteangularjs
function publishExternalAPI(angular) { extend(angular, { 'bootstrap': bootstrap, 'copy': copy, 'extend': extend, 'bind': bind, ... }); angularModule = setupModuleLoader(window); angularModule('ng', ['ngLocale'], ['$provide', function ngModule($provide) { ... } ... }
angular.module
方法。找到带angular项目标识的元素,而后调用bootstrap方法。 github
function angularInit(element, bootstrap) { var appElement, module, config = {}; forEach(ngAttrPrefixes, function(prefix) { var name = prefix + 'app'; if (!appElement && element.hasAttribute && element.hasAttribute(name)) { appElement = element; module = element.getAttribute(name); } }); ... if (appElement) { ... bootstrap(appElement, module ? [module] : [], config); } }
bootstrap函数主要功能:建立注入器和用注入器进行调用web
function bootstrap(element, modules, config) { ... var injector = createInjector(modules, config.strictDi); injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', function bootstrapApply(scope, element, compile, injector) { scope.$apply(function() { element.data('$injector', injector); compile(element)(scope); }); }] ); ... }
createInjector:其内部代码复杂,DI及provider在此不作展开。在这里模块加载完成后,运行模块,生成实例。编程
injector.invoke:生成编译实例,经过编译实例去编译项目起始页,编译的核心是生成指令对应的link函数,有点相似后端的编译,先词法分析,用lex,而后语法分析,用parse,最后连接,生成link函数。具体分析以下bootstrap
在publishExternalAPI函数中咱们定义了Provider,这个provider实现了AngularJS的核心方法。
$rootScope: $RootScopeProvider
接下来看看该provider的源码,下一章咱们将分析其核心实现细节。
function $RootScopeProvider() { var TTL = 10; this.digestTtl = function(value) { ... }; this.$get = ['$exceptionHandler', '$parse', '$browser', function($exceptionHandler, $parse, $browser) { function Scope() { ... } Scope.prototype = { constructor: Scope, $new: function, $watch: function, $watchGroup: function, $watchCollection: function, $digest: function, $destroy: function, $eval: function, $evalAsync: function, $$postDigest: function, $apply: function, $applyAsync: function, $on: function, $emit: function, $broadcast: function, }; var $rootScope = new Scope(); } ]; }
$watch和$digest是相辅相成的。二者一块儿,构成了Angular做用域的核心:数据变化的响应。
做用为在Scope上添加一个监听器。当Scope上发生变动时,监听器会收到提示。监听器包括下面二个函数:
一般来讲这里的监听器是监控一个表达式。监控表达式是一个字符串,好比说“{{user.firstName}}”,一般在数据绑定,指令的属性,或者JavaScript代码中指定,它被Angular解析和编译成一个监控函数。
Angular框架中,双美圆符前缀$$表示这个变量被看成私有的来考虑,不该当在外部代码中调用。
$watch源码以下:
$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
array = scope.$$watchers, watcher = { fn: fn, last: initWatchVal, get: get, exp: prettyPrintExpression || watchExp, eq: !!objectEquality }; array.unshift(watcher); return function deregisterWatch() { var index = arrayRemove(array, watcher); if (index >= 0) { incrementWatchersCount(scope, -1); if (index < array.$$digestWatchIndex) { array.$$digestWatchIndex--; } } lastDirtyWatch = null; }; },
该函数定义在原型上,为全部scope实例共用。
该方法主要接受两个函数做参数(表达式和监听函数),把它们存储在$$watchers数组中 - array.unshift(watcher);
Wather数组中的监听器将会被下面的digest函数调用。
Digest函数是angularjs的核心方法之一,进行脏值检查,代码以下:
do { // "traverse the scopes" loop if ((watchers = current.$$watchers)) { watchers.$$digestWatchIndex = watchers.length; while (watchers.$$digestWatchIndex--) { try { watch = watchers[watchers.$$digestWatchIndex]; // Most common watches are on primitives, in which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { get = watch.get; if ((value = get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) : (isNumberNaN(value) && isNumberNaN(last)))) { dirty = true; lastDirtyWatch = watch; watch.last = watch.eq ? copy(value, null) : value; fn = watch.fn; fn(value, ((last === initWatchVal) ? value : last), current); if (ttl < 5) { logIdx = 4 - ttl; if (!watchLog[logIdx]) watchLog[logIdx] = []; watchLog[logIdx].push({ msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, newVal: value, oldVal: last }); } } else if (watch === lastDirtyWatch) { // If the most recently dirty watcher is now clean, short circuit since the remaining watchers // have already been tested. dirty = false; break traverseScopesLoop; } } } catch (e) { $exceptionHandler(e); } } } // Insanity Warning: scope depth-first traversal // yes, this code is a bit crazy, but it works and we have tests to prove it! // this piece should be kept in sync with the traversal in $broadcast if (!(next = ((current.$$watchersCount && current.$$childHead) || (current !== target && current.$$nextSibling)))) { while (current !== target && !(next = current.$$nextSibling)) { current = current.$parent; } } } while ((current = next));
Loop全部的watcher,经过get方法取得当前值,与 记录的上次值(watch.last)比较,若是不一样则值为脏值。
更新watch.last为当前新值,调用watcher上的监听函数,并把新值做为参数。
注意:在监听函数中有可能会再次更改scope中的值,在最后一部分有个疯狂的深度优先遍历。这里会有个问题,若是一直有脏值怎么办,注意代码中有个TTL最大try的次数。
以上实现了实现了Angular做用域的本质:添加监听器,在digest里运行它们。
特性:
在做用域上添加数据自己并不会有性能折扣。若是没有监听器在监控某个属性,它在不在做用域上都无所谓。Angular并不会遍历做用域的属性,它遍历的是监听器。
$digest里会调用每一个监控函数,所以,最好关注监听器的数量,还有每一个独立的监控函数或者表达式的性能。
做用:集成外部代码与digest循环
$apply使用函数做参数,它用$eval执行这个函数,而后经过$digest触发digest循环。
$apply: function(expr) { try { beginPhase('$apply'); try { return this.$eval(expr); } finally { clearPhase(); } } catch (e) { $exceptionHandler(e); } finally { try { $rootScope.$digest(); } catch (e) { $exceptionHandler(e); // eslint-disable-next-line no-unsafe-finally throw e; } } },
apply的源码很是简单,如上所示,经过eval调用指定的方法。而且保证执行后调用一次digest以进行脏值检查。
apply使用时机:DOM事件、setTimeout、XHR或其余第三方的库,特别是异步方法对scope中数据的改变不会被watcher监控到,须要显示调用apply告诉angularjs。
以上就是angularJS核心思想及其源码实现,有点懒没有展开的说,也有可能理解不够透彻,毕竟是过期的框架了,做为前端编程思想进行学习而已。
refers:
https://www.cnblogs.com/leo_wl/p/3446075.html
https://www.cnblogs.com/wuya16/p/3769032.html
https://blog.csdn.net/u013510614/article/details/50703811
https://blog.csdn.net/u013510614/article/details/50703813
https://blog.csdn.net/u013510614/article/details/50703815
https://blog.csdn.net/cteng/article/details/72823190
https://github.com/xufei/Make-Your-Own-AngularJS/blob/master/01.md