backbond Model方法(set)
backbond的Model,其中存在一些操作属性的方法,而在这些方法中,最重要的就是set方法,其余的方法大部分都基于这个方法实现的,在backbond开发版中,也说了该方法是model中的核心方法。
在分析之前,先看一下官方文档的描述:
也就是说,可以传入{key-value}{obj}两个对象作为参数,也可以传入key,value,{obj}三个作为参数,其中obj是用来实现当调用set方法时,是否进行其他操作。
接下来,可以先看代码:
set: function(key, val, options) {
var attr, attrs, unset, changes, silent, changing, prev, current;
if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments.
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
} options || (options = {});
// Run validation.
// 根据用户传入参数调用validate函数
if (!this._validate(attrs, options)) return false;
// Extract attributes and options.
unset = options.unset;
silent = options.silent;
changes = [];
changing = this._changing;
this._changing = true;
//存储变化之前的attributes
if (!changing) {
this._previousAttributes = _.clone(this.attributes);
this.changed = {};
}
current = this.attributes, prev = this._previousAttributes; // Check for changes of `id`.
// 修改id的值
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; // For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
if (!_.isEqual(current[attr], val)) changes.push(attr);
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
unset ? delete current[attr] : current[attr] = val;
} // Trigger all relevant attribute changes.触发事件
if (!silent) {
if (changes.length) this._pending = options;
for (var i = , length = changes.length; i < length; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
} // You might be wondering why there's a `while` loop here. Changes can
// be recursively nested within `"change"` events.
if (changing) return this;
if (!silent) {
while (this._pending) {
options = this._pending;
this._pending = false;
this.trigger('change', this, options);
}
}
this._pending = false;
this._changing = false;
return this;
}
在分析代码的时候,有一句代码 if (!this._validate(attrs, options)) return false;
这是调用我们定义model时的validate方法,判断设置的数据是否正确,在这之前,可以先看一下validate的用法:
var Chapter = Backbone.Model.extend({
validate: function(attrs, options) {
if (attrs.end < attrs.start) {
return "can't end before it starts";
}
}
}); var one = new Chapter({
title : "Chapter One: The Beginning"
}); one.on("invalid", function(model, error) {
console.log(model.get("title") + " " + error); //Chapter One: The Beginning can't end before it starts
}); one.set({ start: 15, end: 10 },{validate:true});
当我们调用set方法时,并在第二个参数中传入{validate:true},则会调用validate方法,进行判断是否错误。
validate的内部实现方法如下,其中也存在注释,在这里不再赘述。
_validate: function(attrs, options) {
//options和this必须同时包含validate
//即需要在属性里定义validate方法 也需要在传入的options里定义validate属性为真值
//否则 直接返回true
if (!options.validate || !this.validate) return true;
//获得实例的所有属性和新传入的属性
attrs = _.extend({}, this.attributes, attrs);
//设置error信息为用户定义的validate返回值
var error = this.validationError = this.validate(attrs, options) || null;
//如果返回值不存在 即没有错误信息 那么返回true
if (!error) return true;
//否则 执行invlid事件 并返回false
this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
return false;
}
set接下来的代码大多数都比较容易理解,只是其中有几句代码使我困惑了很久,如this.changing、this._pengding等变量,如下:
set: function(key, val, options) {
...
...
options || (options = {});
...
...
changes = [];
changing = this._changing;
this._changing = true;
.... ... ...
// Trigger all relevant attribute changes.触发事件
if (!silent) {
if (changes.length) this._pending = options;
...
} // You might be wondering why there's a `while` loop here. Changes can
// be recursively nested within `"change"` events.
if (changing) return this;
if (!silent) {
while (this._pending) {
options = this._pending;
this._pending = false;
this.trigger('change', this, options);
}
}
this._pending = false;
this._changing = false;
return this;
}
在一开始调用的时候,this._changing就是false,那么无论如何,changing都是false,为什么还要设置一个changing变量呢?
分析源码的最大一个好处就是,他的代码肯定不是没有意义的! 顾名思义,changing就是正在改变的意思。
假如我们调用如下代码时,
var z = 0;
var model = new Backbone.Model();
model.on('change', function() {
console.log(++z)
});
model.on('change:a', function() {
model.set({b: true});
});
model.set({a: true});
控制台只输出了1,也就是调用了一次console.log(++z);因为设置了changing变量,在model.set({b:true})时,因为之前调用了model.set({a:true}),changing变量处于true状态,所以model.set({b:true})调用时不会调用 this.trigger('change', this, options);
紧接着就是下面的语句:
if (!silent) {
if (changes.length) this._pending = options;
...
}
if (!silent) {
while (this._pending) {
options = this._pending;
this._pending = false;
this.trigger('change', this, options);
}
}
当changes的长度不为0时,this._pengding = options,所以this._pengding是一个对象,而while({})是可以正常执行的。
该语句的作用就是当调用set方法时,属性没有改变,即this._pending为false时,不执行this.trigger('change', this, options);语句。
所以这里的changing变量和while语句就是为了避免事件嵌套。
至于这里为什么用while而不是if,可以先看一下下面的代码:
var z = 0; var model = new Backbone.Model();
model.on('change', function() {
console.log(++z);
model.set({b: true});
});
model.on('change:a', function() { });
model.set({a: true});
当我们执行上面的代码时,实际输出的是1,2.而不是像上面的一样,只调用了一次console。
其原因就在while。
当我们调用model.set({a:true})时,正常执行了while里面的语句,在调用change事件之前将this._pending改为false,紧接着调用了change事件,该事件首先打印出++z,也就是控制台输出了1.
接着它调用了model.set({b:true}),此时changing为true,并且changes.length不为0,那么this._pending被置为options的值,函数在这里返回,重新回到调用model.set({a:true})的while循环,此时根据while循环,它在一次执行了while里面的语句,同样,在调用change事件之前将this._pending改为false,紧接着调用了change事件,该事件首先打印出++z,也就是控制台输出了2.
接着它又调用了model.set({b:true}),此时changing为true,并且changes.length为0,那么this._pengding依然为原来false值,函数在这里返回,再次回到调用model.set({a:true})的while循环,此时退出while循环,接着进行下一次操作。
也就是说,当我们在change事件的回调函数里再次或多次调用了set方法,那么change事件都会被触发两次,多次调用也是两次,具体原因根据以上思路可以得到结果。
backbond Model方法(set)的更多相关文章
- backbond Model实现
backbond中的M,指的是模型,即存放数据以及数据相关逻辑的单位.在分析其结构之前,先看一下其调用过程. <script> (function ($) { World = Backbo ...
- Sails 自定义 model 方法
Sails 自定义 model 方法 在 Sails 中 model 提供了一些原生的静态方法,如 .create(), .update(), .destroy(), .find(), 等. 在实际业 ...
- 大D实例化model-->调用自定义类方法,大M调用原声model方法
class ContactsModel extends Model{ public function addxxx(){ } } $conmodel = D('contacts','Model'); ...
- Cesium 中两种添加 model 方法的区别
概述 Cesium 中包含两种添加 model 的方法,分别为: 通过 viewer.entities.add() 函数添加 通过 viewer.scene.primitives.add() 函数添加 ...
- 在ASP.NET MVC中使用Knockout实践02,组合View Model成员、Select绑定、通过构造器创建View Model,扩展View Model方法
本篇体验使用ko.computed(fn)计算.组合View Model成员.Select元素的绑定.使用构造器创建View Model.通过View Model的原型(Prototype)为View ...
- Laravel 视图调用model方法
首先控制器 model 视图
- tp5.1 model 方法下的like语句查询
$where_like = ['title','like','%' . $_GET['title'] . '%']; $result_list = $this->model->where( ...
- [转]微擎人人商城m()函数调用model方法
本文转自:http://yangjunwei.com/a/3177.html 微擎人人商城p()函数可内部调用任意插件的函数 http://yangjunwei.com/a/3216.html
- model方法取值总结
转自:https://www.cnblogs.com/ajianbeyourself/p/3604332.html
随机推荐
- shell 命令 创建/删除 软连接 ln -s
软链接的作用是, 1. 节省复制造成的空间浪费 2. 保证两个文件的内容同时修改 所以,可以把软连接理解为给文件/文件夹创建了别名,当访问别名时,实际访问的是链接的文件/文件夹 软链文件 ln -s ...
- Mapreduce操作HBase
这个操作和普通的Mapreduce还不太一样,比如普通的Mapreduce输入可以是txt文件等,Mapreduce可以直接读取Hive中的表的数据(能够看见是以类似txt文件形式),但Mapredu ...
- 一、Java和JavaScript
JavaScript诞生于1995年,所以他得叫我一声姐姐,(*^__^*) .当时它的主要任务就是表单验证,在还没JavaScript的时候,进行表单验证的时候必须要把数据提交到服务器,才能进行表单 ...
- 常用 ADB 命令[ZZ]
https://blog.csdn.net/yang_zhang_1992/article/details/71404186 1. 显示当前运行的全部模拟器: adb devices 2. 对某一模拟 ...
- C#常用工具类——Excel操作类(ZT)
本文转载于: http://www.cnblogs.com/zfanlong1314/p/3916047.html /// 常用工具类——Excel操作类 /// <para> ----- ...
- 4.TableLayout、回调接口
会话详情页 listview条目布局 列,当第0列的内容过多时,不至于将其他列的内容,挤出屏幕 --> <TableLayout android:id="@+id/tl_rece ...
- IDEA一定要懂的32条快捷键
阅读本文大概需要 2 分钟. 作者:帝都羊 这些IntelliJ IDEA键盘快捷键可以让你专注于编写代码,让你的双手在键盘上起舞. 1.搜索文件名: ↑ Shift 快速连续按两下 2.显示 ...
- 吴恩达机器学习笔记6-梯度下降II(Gradient descent intuition)--梯度下降的直观理解
在之前的学习中,我们给出了一个数学上关于梯度下降的定义,本次视频我们更深入研究一下,更直观地感受一下这个算法是做什么的,以及梯度下降算法的更新过程有什么意义.梯度下降算法如下: 描述:对
- Spring详解(八)------事务管理
PS:本篇博客源码下载链接:http://pan.baidu.com/s/1mi3NhX2 密码:3io2 1.事务介绍 事务(Transaction),一般是指要做的或所做的事情.在计算机术语中是指 ...
- Python档案袋( 命令行操作 及 Os与Shutil文件操作补充 )
调用系统命令 import os #调用系统命令,输出只能输出到屏幕上,不能用变量接收 os.system("ipconfig") #调用系统命令,并把执行结果存到变量中 res= ...