avalon.define = function(id, factory) { var $id = id.$id || id if (!$id) { log("warning: vm必须指定$id") } if (VMODELS[$id]) { log("warning: " + $id + " 已经存在于avalon.vmodels中") } if (typeof id === "object") { var model = modelFactory(id) } else { var scope = { $watch: noop } factory(scope) //得到所有定义 model = modelFactory(scope) //偷天换日,将scope换为model stopRepeatAssign = true factory(model) stopRepeatAssign = false } model.$id = $id return VMODELS[$id] = model }
运行这段代码的过程中,会把id在modelFactory中进行加工
function modelFactory(source, $special, $model) { if (Array.isArray(source)) { var arr = source.concat() source.length = 0 var collection = Collection(source) collection.pushArray(arr) return collection } if (typeof source.nodeType === "number") { return source } if (source.$id && source.$events) { //fix IE6-8 createWithProxy $val: val引发的BUG return source } if (!Array.isArray(source.$skipArray)) { source.$skipArray = [] } source.$skipArray.$special = $special || {} //强制要监听的属性 var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤 $model = $model || {} //vmodels.$model属性 var $events = {} //vmodel.$events属性 var watchedProperties = {} //监控属性 var initCallbacks = [] //初始化才执行的函数 for (var i in source) { (function(name, val) { $model[name] = val if (!isObservable(name, val, source.$skipArray)) { return //过滤所有非监控属性 } //总共产生三种accessor $events[name] = [] var valueType = avalon.type(val) var accessor = function(newValue) { var name = accessor._name var $vmodel = this var $model = $vmodel.$model var oldValue = $model[name] var $events = $vmodel.$events if (arguments.length) { if (stopRepeatAssign) { return } //计算属性与对象属性需要重新计算newValue if (accessor.type !== 1) { newValue = getNewValue(accessor, name, newValue, $vmodel) } if (!isEqual(oldValue, newValue)) { $model[name] = newValue if ($events.$digest) { if (accessor.pedding) return accessor.pedding = true setTimeout(function() { notifySubscribers($events[name]) //同步视图 safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调 accessor.pedding = false }) } else { notifySubscribers($events[name]) //同步视图 safeFire($vmodel, name, newValue, oldValue) //触发$watch回调 } } } else { if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性 //计算属性不需要收集视图刷新函数,都是由其他监控属性代劳 return $model[name] = accessor.get.call($vmodel) } else { collectSubscribers($events[name]) //收集视图函数 return accessor.svmodel || oldValue } } } //总共产生三种accessor if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) { //第1种为计算属性, 因变量,通过其他监控属性触发其改变 accessor.set = val.set accessor.get = val.get accessor.type = 0 initCallbacks.push(function() { var data = { evaluator: function() { data.element = null data.type = new Date - 0 $model[name] = accessor.get.call($vmodel) }, element: head, type: new Date - 0, handler: noop, args: [] } Registry[expose] = data accessor.call($vmodel) delete Registry[expose] }) } else if (rcomplexType.test(valueType)) { //第2种为对象属性,产生子VM与监控数组 accessor.type = 2 accessor.valueType = valueType initCallbacks.push(function() { var svmodel = modelFactory(val, 0, $model[name]) accessor.svmodel = svmodel svmodel.$events[subscribers] = $events[name] }) } else { accessor.type = 1 //第3种为监控属性,对应简单的数据类型,自变量 } accessor._name = name /*给需要监控的属性添加set和get方法*/ watchedProperties[name] = accessor })(i, source[i]) } $$skipArray.forEach(function(name) { delete source[name] delete $model[name] //这些特殊属性不应该在$model中出现 }) /* 下面方法是给属性生成如下格式: * var descriptorFactory = W3C ? function(obj) { var descriptors = {} for (var i in obj) { descriptors[i] = { get: obj[i], set: obj[i], enumerable: true, configurable: true } } return descriptors } : function(a) { return a } */ $vmodel = defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel for (var name in source) { if (!watchedProperties[name]) { $vmodel[name] = source[name] } } //添加$id, $model, $events, $watch, $unwatch, $fire $vmodel.$id = generateID() $vmodel.$model = $model $vmodel.$events = $events for (var i in EventBus) { var fn = EventBus[i] if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下 fn = fn.bind($vmodel) } $vmodel[i] = fn } if (canHideOwn) { Object.defineProperty($vmodel, "hasOwnProperty", { value: function(name) { return name in this.$model }, writable: false, enumerable: false, configurable: true }) } else { $vmodel.hasOwnProperty = function(name) { return name in $vmodel.$model } } initCallbacks.forEach(function(cb) { //收集依赖 cb() }) return $vmodel }