来源
 avalon的重头戏。终于要到我最期待的vmodel了。 
ps:这篇博文想做的全一点,错误少一点,所以会有后续的更新在这篇文章中。 状态:一稿 目录[-]
avalon dom小结
数据结构
观察者模式
依赖收集与触发
avalon Observable
avalon modelFactory
loopModel
函数介绍
isEqual
小记
avalon dom小结 看过前面三篇文章后,应该会对avalon关于dom的处理有个大体的理念。 这里再理一遍:avalon通过手动触发scan函数来遍历dom。
然后根据ms-import ms-container ms-include ms-skip确定VMODELS的作用域,
接下来便是处理用户代码并生成相应的函数, 通过registerSubscriber函数,
将dom生成的函数以及相应的关联数据进行注册。
剩下的,就是其他模块的事情了。
数据结构 如常 //VMODULE 严格意义上,这个不算数据结构,而且,这是object一层的结构,如果object是嵌套式的
// {
// a:{
// b:"嵌套"
// }
// }
// 那么这个比较复杂了
{
$id:$string,//define函数的第一个参数,如果是嵌套层的,则随机
$model:$obj,//define定义中的第二个参数中的vm。如果是嵌套层的,则$model为当前嵌套层及以下的值
$watch:$fn,
$unwatch:$fn,
$fire:$fn,//以上三个是Observable对象下的三个方法,不过他们的上下文被修改了。
$skipArray:true|$array,//要忽略的参数
hasOwnProperty:$fn,//重写的方法,将其作用域定义到$model
$accessors:$obj,下文将详细讲解。
$event:$obj,//记录用户定义的$watch
get ...:$fn,
set ...:$fn,
...:...//经过defineProperty处理的model值
}
//$accessors,
{
$modelName:$fn //$fn[subscribers] =["记录订阅者"]
$vmodel:$obj// 嵌套层时,出现
}
观察者模式 在讲解 avalon vmodel之前,一定要对观察者模式有个清晰的了解,
观察者模式又叫订阅发布模式(Subscribe/Publish),
具体讲解参见观察者模型。
在avalon中,作者用了两遍观察者模型,
分别解决了model和view的交互和扩展用户监控model值的问题。
具体实现模块为依赖收集与触发和Observable。 和java代码中实现的观察者不同的是,被观察者会创建一个数组
,数组内存放着所有的观察者,而观察者则都是函数。
当被观察者产生改变时,观察者函数则会被附加上下文环境后,依次执行。
依赖收集与触发 这里先上下源码,用来做辅助讲解。
 function registerSubscriber(data) {
Registry[expose] = data //暴光此函数,方便collectSubscribers收集
avalon.openComputedCollect = true
var fn = data.evaluator
if (fn) { //如果是求值函数
if (data.type === "duplex") {
data.handler()
} else {
data.handler(fn.apply(0, data.args), data.element, data)
}
} else { //如果是计算属性的accessor
data()
}
avalon.openComputedCollect = false
delete Registry[expose]
} function collectSubscribers(accessor) { //收集依赖于这个访问器的订阅者
if (Registry[expose]) {
var list = accessor[subscribers]
list && avalon.Array.ensure(list, Registry[expose])//只有数组不存在此元素才push进去
}
}


上面代码中,accessor函数的[subscribes]属性,做了被观察者存储观察者的事情。
为什么registerSubscriber函数就敢肯定在 Registry[expose] = data
和delete Registry[expose]之间,collectSubscribers会被执行?
秘密就在于descriptorFactory函数对Object.defineProperty函数的应用。
我们先写一点Object.defineProperty的简单例子,
具体理解可参考数据属性和访问器属性。
var a={};
Object.defineProperty(a,"a",{ get:function(){
console.log("get a")
return "默认值"
},
set:function(val){
console.log("set a")
value=val;
}
});
console.log(a.a);
console.log(a.a="new value")
console.log(a.a);
// get a
// 默认值
// set a
// new value
// get a
// 默认值 var memeryValue="默认值";//we need to save the value in some where
Object.defineProperty(a,"b",{
get:function(){
console.log("get b")
return memeryValue
},
set:function(val){
console.log("set b")
memeryValue=val;
}
});
console.log(a.b);
console.log(a.b="new value")
console.log(a.b);
// get b
// 默认值
// set b
// new value
// get b
// new value


通过上面的例子,结合观察者模型我们可知道,通过对get/set的设定,
我们可以观察用户什么时候对vmodel里绑定的值进行赋值或读取,
并做相应的处理(对avalon来讲,就是调用accessor函数)。
至于notifySubscribers函数,
则是调出绑定在accessor函数上的观察者集合,并执行。
avalon Observable Observable是一个对象。它下面定义了三个方法,
分别为$watch、$unwacth、$fire。
这三个方法会和$events会被注入到每一个用户定义的vm中 (如果用户定义的vm是嵌套的,方法会在每个嵌套层都注入一下)。
用户只需要了解$watch和$unwacth用法即可,fire交给avalon自行处理就好了。
$events用来记录观察者和被观察者关系。 注意:"$events"下面的属性会多出类似这样的值{"modelValue":undefined},
而这个值的来源计算属性
accessor的真么一段代码。
//name='abc'
var backup = vmodel.$events[name]//当vmodel.$events[name]不存在时,backup会被赋值为undefined
vmodel.$events[name] = []
setter.call(vmodel, newValue)
vmodel.$events[name] = backup//这时,{abc:undefined}


这个地方好生纠结,自己稍微改了一下:
var backup = vmodel.$events[name]
vmodel.$events[name] = []
setter.call(vmodel, newValue)
if(backup===undefined)
delete vmodel.$events[name]
else
vmodel.$events[name] = backup avalon modelFactory modelFactory由两个重要的函数构成 loopModel和descriptorFactory。
loopModel


在说loopModel之前,我们要先了解下avalon的5种属性。

    model属性,存放着未被avalon处理用户定义的属性集合。你可以把它当成java的entity,和后台进行数据交互时,直接和它进行交互,avalon会自动触发订阅。

    normalProperties 普通属性,不需要双向绑定的,例如放在$skipArray里的属性,用户自定义以$开头的,函数以及avalon自定义的一些函数($event, $watch 等)。

    accessingProperties 监控属性,要进行双向绑定的属性。

    watchProperties 强制要监听的属性,以$开头的,但又想强制监听它,例如avalon内部定义的$event等。这个属性是normalProperties的补充,属于内部属性,用户不会使用到它。

    computedProperties 计算属性,用户自己自定义的set和get方法。是accessingProperties的补充。

loopModel函数的主要作用是将用户定义的vm object进行属性归类,并生成被观察者函数accessor。
作为被观察者accessor,他需要绑定来自dom的观察者到自身上以及将值大的改变通知到来自dom的观察(notifySubscribers)和来自用户自定义的观察(safeFire)。依据

accessingProperties、computedProperties的嵌套的的不同特点构造了三种类型的accessor。
我们捡一个对object嵌套的accessor实现来看看。他除了实现上面的功能外,还需要调用modelFactory对嵌套的每一层生成VMODULE结构,在这个实现上,为了尽可能的复用现有代码,牺牲 了数据结构,avalon.vmodels关于$model的记录有些重复。至于updateWithProxy、updateVModel函数以及关于的详细讲解,会在以后补充上。
函数介绍
isEqual
var isEqual = Object.is || function(v1, v2) {
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2
} else if (v1 !== v1) {
return v2 !== v2
} else {
return v1 === v2
}
}//虽然看不懂,但不妨碍使用

小记

avalon的双向绑定的基本内容就这么多了。代码读到现在终于有一种可解脱的感觉了。
感谢司徒正美给我们带来如此优秀的代码,另推荐他的一本书《javascript框架设计》,
虽然校验的不怎么样,小bug不断,但内容绝对丰满,适合低中级的Jser反复阅读 (至于高级适不适合,我就不知道了)。

avalonJS-源码阅读(3) VMODEL的更多相关文章

  1. avalon源码阅读(1)

    来源 写angularJS源码阅读系列的时候,写的太垃圾了. 一个月后看,真心不忍直视,以后有机会的话得重写. 这次写avalonJS,希望能在代码架构层面多些一点,少上源码.多写思路. avalon ...

  2. Element源码阅读(1)

    一.目的 阅读element源码旨在了解其代码的组织架构模式, 代码编写的方式, 以及组件化的一些思路, 对照自己, 从而进步. 二. 源码阅读所得 1.在element源码中的mixins目录之下, ...

  3. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  4. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  5. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  6. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  7. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  8. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  9. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  10. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

随机推荐

  1. js媒体查询设置根字号

    !function(n){var e=n.document,t=e.documentElement,i=750,d=i/50,o="orientationchange"in n?& ...

  2. c#传统SqlTransaction事务和TransactionScope事务

    事务有很多种,看了一些关于事务的问题,这里做下笔记····· 事务时单个的工作单位.如果某一事务成功,则在该事务中进行的所有数据更改均会提交,成为数据库中永久的组成部分.若果事务遇到错误,则必须取消或 ...

  3. 51nod 1412 AVL树的种类(经典dp)

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1412 题意: 思路: 经典dp!!!可惜我想不到!! $dp[i][k] ...

  4. VisualStudio使用技巧及快捷键

    1. 怎样调整代码排版的格式? 选择:编辑—>高级—>设置文档的格式或编辑—>高级—>设置选中代码的格式. 格式化cs代码:Ctrl+k+f 格式化aspx代码:Ctrl+k+ ...

  5. servlet生命周期深入理解

    什么是Servlet Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中 ...

  6. MVC扩展Url.Action方法解决复杂对象参数问题

    1:问题描述 @Url.Action("Index", "Home", new { Key = "Key", Val = new { Nam ...

  7. Python day7_set集合的常用方法以及常用格式化的总结

    1.集合的定义:集合是无序的,没有重合元素的集合 集合外使用{}符号,各元素用,连接 2.集合的常用方法 1.add增加元素 2.clear清除元素 3.copy浅拷贝 4.difference差集( ...

  8. Java 常用对象-Scanner类

    2017-11-02 16:33:11 Scanner类:一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器. Scanner 使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白 ...

  9. Python 爬虫-获得大学排名

    2017-07-29 23:20:24 主要技术路线:requests+bs4+格式化输出 import requests from bs4 import BeautifulSoup url = 'h ...

  10. 新概念 Lesson 5 How are you today

    How is Emma? 艾玛身体还好吗? 短语:very well How's Emma? She's very well, too. Emma is very well today   adv. ...