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)的更多相关文章

  1. backbond Model实现

    backbond中的M,指的是模型,即存放数据以及数据相关逻辑的单位.在分析其结构之前,先看一下其调用过程. <script> (function ($) { World = Backbo ...

  2. Sails 自定义 model 方法

    Sails 自定义 model 方法 在 Sails 中 model 提供了一些原生的静态方法,如 .create(), .update(), .destroy(), .find(), 等. 在实际业 ...

  3. 大D实例化model-->调用自定义类方法,大M调用原声model方法

    class ContactsModel extends Model{ public function addxxx(){ } } $conmodel = D('contacts','Model'); ...

  4. Cesium 中两种添加 model 方法的区别

    概述 Cesium 中包含两种添加 model 的方法,分别为: 通过 viewer.entities.add() 函数添加 通过 viewer.scene.primitives.add() 函数添加 ...

  5. 在ASP.NET MVC中使用Knockout实践02,组合View Model成员、Select绑定、通过构造器创建View Model,扩展View Model方法

    本篇体验使用ko.computed(fn)计算.组合View Model成员.Select元素的绑定.使用构造器创建View Model.通过View Model的原型(Prototype)为View ...

  6. Laravel 视图调用model方法

    首先控制器 model 视图

  7. tp5.1 model 方法下的like语句查询

    $where_like = ['title','like','%' . $_GET['title'] . '%']; $result_list = $this->model->where( ...

  8. [转]微擎人人商城m()函数调用model方法

    本文转自:http://yangjunwei.com/a/3177.html 微擎人人商城p()函数可内部调用任意插件的函数 http://yangjunwei.com/a/3216.html

  9. model方法取值总结

    转自:https://www.cnblogs.com/ajianbeyourself/p/3604332.html

随机推荐

  1. 记录做一个类似于探探的卡片式布局的Recycleview有数据一直不显示

    使用了别人的项目 https://github.com/JerryChan123/ReSwipeCard/blob/master/README_zh.md 之前找recycleview有数据不显示的原 ...

  2. JAVA:调用cmd指令(支持多次手工输入)

    JDK开发环境:1.8 package com.le.tool; import java.io.BufferedReader; import java.io.File; import java.io. ...

  3. noip第29课资料

  4. jquery1.6中的.prop()和.attr()异同

    jquery jQueryHTML5JavaScript浏览器ITeye  最近在iteye的新闻中看到jQuery已经更新到了1.6.1.和之前版本的最大变化是增加了.prop方法.但是.prop( ...

  5. PDF 报表 Java 组件 iText5 中的单位注意事项

    这里面涉及到这几个单位: 点(磅)(pt).像素(px).英寸(inch).毫米(mm) 分辨率单位有: dpi(点每英寸):出现于打印或印刷领域. lpi (线每英寸):描述光学分辨率的尺度. pp ...

  6. Java面试集合(七)

    前言: Java面试集合(六) 的回顾,对于final可以修饰常量,方法,和类,一旦常量定义好后就不可改变,而方法,用final来修饰方法,方法不可重载,继承,重写,final用来修饰类,该类不能被继 ...

  7. [Postman]调试和日志(10)

    Postman应用程序在我们发布之前会经过广泛的测试和beta版本.也就是说,可能存在应用程序崩溃或出现意外行为的情况.如果您无法   自行解决问题,可以在GitHub跟踪器中提出问题,或者 如果您希 ...

  8. netty入门(一)

    1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ...

  9. Informatica

    安装 相关专题 关于Bulk加载模式 性能调优 性能瓶颈 性能瓶颈概览 性能瓶颈之Target 性能瓶颈之Source 性能瓶颈之Mapping 性能瓶颈之Session 性能瓶颈之System 性能 ...

  10. 玩转Python图片处理 (OpenCV-Python )

    OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效——由一系列 C 函数和少量 C++ 类 ...