backbone 1.3.3源碼解析-------------------Model

来源:http://www.cnblogs.com/wangwei1314/archive/2016/06/18/5560125.html
-Advertisement-
Play Games

...


  1 // Backbone.Model
  2   // --------------
  3 
  4   // Backbone **Models** are the basic data object in the framework --
  5   // frequently representing a row in a table in a database on your server.
  6   // A discrete chunk of data and a bunch of useful, related methods for
  7   // performing computations and transformations on that data.
  8 
  9   // Create a new model with the specified attributes. A client id (`cid`)
 10   // is automatically generated and assigned for you.
 11   var Model = Backbone.Model = function(attributes, options) {
 12     var attrs = attributes || {};
 13     options || (options = {});
 14     this.preinitialize.apply(this, arguments);
 15     this.cid = _.uniqueId(this.cidPrefix);
 16     this.attributes = {};
 17     if (options.collection) this.collection = options.collection;
 18     if (options.parse) attrs = this.parse(attrs, options) || {};
 19     var defaults = _.result(this, 'defaults');
 20     attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
 21     this.set(attrs, options);
 22     this.changed = {};
 23     this.initialize.apply(this, arguments);
 24   };
 25 
 26   // Attach all inheritable methods to the Model prototype.
 27   _.extend(Model.prototype, Events, {
 28 
 29     // A hash of attributes whose current and previous value differ.
 30     changed: null,
 31 
 32     // The value returned during the last failed validation.
 33     validationError: null,
 34 
 35     // The default name for the JSON `id` attribute is `"id"`. MongoDB and
 36     // CouchDB users may want to set this to `"_id"`.
 37     idAttribute: 'id',
 38 
 39     // The prefix is used to create the client id which is used to identify models locally.
 40     // You may want to override this if you're experiencing name clashes with model ids.
 41     cidPrefix: 'c',
 42 
 43     // preinitialize is an empty function by default. You can override it with a function
 44     // or object.  preinitialize will run before any instantiation logic is run in the Model.
 45     preinitialize: function(){},
 46 
 47     // Initialize is an empty function by default. Override it with your own
 48     // initialization logic.
 49     initialize: function(){},
 50 
 51     // Return a copy of the model's `attributes` object.
 52     toJSON: function(options) {
 53       return _.clone(this.attributes);
 54     },
 55 
 56     // Proxy `Backbone.sync` by default -- but override this if you need
 57     // custom syncing semantics for *this* particular model.
 58     sync: function() {
 59       return Backbone.sync.apply(this, arguments);
 60     },
 61 
 62     // Get the value of an attribute.
 63     get: function(attr) {
 64       return this.attributes[attr];
 65     },
 66 
 67     // Get the HTML-escaped value of an attribute.
 68     escape: function(attr) {
 69       return _.escape(this.get(attr));
 70     },
 71 
 72     // Returns `true` if the attribute contains a value that is not null
 73     // or undefined.
 74     has: function(attr) {
 75       return this.get(attr) != null;
 76     },
 77 
 78     // Special-cased proxy to underscore's `_.matches` method.
 79     matches: function(attrs) {
 80       return !!_.iteratee(attrs, this)(this.attributes);
 81     },
 82 
 83     // Set a hash of model attributes on the object, firing `"change"`. This is
 84     // the core primitive operation of a model, updating the data and notifying
 85     // anyone who needs to know about the change in state. The heart of the beast.
 86     //這裡是最重要的方法。
 87     // 1.key-val轉換為attrs
 88     // 2.判斷是否需要驗證
 89     // 3.提取options中的屬性,changing應該是為了防止非同步操作造成了不可預知的錯誤。
 90     // 4.更新 _previousAttributes
 91     // 5.更新id
 92     // 6.將修改的屬性存入私有變數changes數組中,修改this.changed對象
 93     // 7.處理unset,silient(靜默更新,不處罰change事件)
 94     // 8.等待change事件執行完畢,因為有可能change事件中又觸發了change事件
 95     set: function(key, val, options) {
 96       if (key == null) return this;
 97 
 98       // Handle both `"key", value` and `{key: value}` -style arguments.
 99       var attrs;
100       if (typeof key === 'object') {
101         attrs = key;
102         options = val;
103       } else {
104         (attrs = {})[key] = val;
105       }
106 
107       options || (options = {});
108 
109       // Run validation.
110       if (!this._validate(attrs, options)) return false;
111 
112       // Extract attributes and options.
113       var unset      = options.unset;
114       var silent     = options.silent;
115       var changes    = [];
116       var changing   = this._changing;
117       this._changing = true;
118 
119       if (!changing) {
120         this._previousAttributes = _.clone(this.attributes);
121         this.changed = {};
122       }
123 
124       var current = this.attributes;
125       var changed = this.changed;
126       var prev    = this._previousAttributes;
127 
128       // For each `set` attribute, update or delete the current value.
129       for (var attr in attrs) {
130         val = attrs[attr];
131         if (!_.isEqual(current[attr], val)) changes.push(attr);
132         if (!_.isEqual(prev[attr], val)) {
133           changed[attr] = val;
134         } else {
135           delete changed[attr];
136         }
137         unset ? delete current[attr] : current[attr] = val;
138       }
139 
140       // Update the `id`.
141       if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
142 
143       // Trigger all relevant attribute changes.
144       if (!silent) {
145         if (changes.length) this._pending = options;
146         for (var i = 0; i < changes.length; i++) {
147           this.trigger('change:' + changes[i], this, current[changes[i]], options);
148         }
149       }
150 
151       // You might be wondering why there's a `while` loop here. Changes can
152       // be recursively nested within `"change"` events.
153       if (changing) return this;
154       if (!silent) {
155         while (this._pending) {
156           options = this._pending;
157           this._pending = false;
158           this.trigger('change', this, options);
159         }
160       }
161       this._pending = false;
162       this._changing = false;
163       return this;
164     },
165 
166     // Remove an attribute from the model, firing `"change"`. `unset` is a noop
167     // if the attribute doesn't exist.
168     unset: function(attr, options) {
169       return this.set(attr, void 0, _.extend({}, options, {unset: true}));
170     },
171 
172     // Clear all attributes on the model, firing `"change"`.
173     clear: function(options) {
174       var attrs = {};
175       for (var key in this.attributes) attrs[key] = void 0;
176       return this.set(attrs, _.extend({}, options, {unset: true}));
177     },
178 
179     // Determine if the model has changed since the last `"change"` event.
180     // If you specify an attribute name, determine if that attribute has changed.
181     hasChanged: function(attr) {
182       if (attr == null) return !_.isEmpty(this.changed);
183       return _.has(this.changed, attr);
184     },
185 
186     // Return an object containing all the attributes that have changed, or
187     // false if there are no changed attributes. Useful for determining what
188     // parts of a view need to be updated and/or what attributes need to be
189     // persisted to the server. Unset attributes will be set to undefined.
190     // You can also pass an attributes object to diff against the model,
191     // determining if there *would be* a change.
192     changedAttributes: function(diff) {
193       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
194       var old = this._changing ? this._previousAttributes : this.attributes;
195       var changed = {};
196       var hasChanged;
197       for (var attr in diff) {
198         var val = diff[attr];
199         if (_.isEqual(old[attr], val)) continue;
200         changed[attr] = val;
201         hasChanged = true;
202       }
203       return hasChanged ? changed : false;
204     },
205 
206     // Get the previous value of an attribute, recorded at the time the last
207     // `"change"` event was fired.
208     previous: function(attr) {
209       if (attr == null || !this._previousAttributes) return null;
210       return this._previousAttributes[attr];
211     },
212 
213     // Get all of the attributes of the model at the time of the previous
214     // `"change"` event.
215     previousAttributes: function() {
216       return _.clone(this._previousAttributes);
217     },
218 
219     // Fetch the model from the server, merging the response with the model's
220     // local attributes. Any changed attributes will trigger a "change" event.
221     fetch: function(options) {
222       options = _.extend({parse: true}, options);
223       var model = this;
224       var success = options.success;
225       options.success = function(resp) {
226         var serverAttrs = options.parse ? model.parse(resp, options) : resp;
227         if (!model.set(serverAttrs, options)) return false;
228         if (success) success.call(options.context, model, resp, options);
229         model.trigger('sync', model, resp, options);
230       };
231       wrapError(this, options);
232       return this.sync('read', this, options);
233     },
234 
235     // Set a hash of model attributes, and sync the model to the server.
236     // If the server returns an attributes hash that differs, the model's
237     // state will be `set` again.
238     save: function(key, val, options) {
239       // Handle both `"key", value` and `{key: value}` -style arguments.
240       var attrs;
241       if (key == null || typeof key === 'object') {
242         attrs = key;
243         options = val;
244       } else {
245         (attrs = {})[key] = val;
246       }
247 
248       options = _.extend({validate: true, parse: true}, options);
249       var wait = options.wait;
250 
251       // If we're not waiting and attributes exist, save acts as
252       // `set(attr).save(null, opts)` with validation. Otherwise, check if
253       // the model will be valid when the attributes, if any, are set.
254       if (attrs && !wait) {
255         if (!this.set(attrs, options)) return false;
256       } else if (!this._validate(attrs, options)) {
257         return false;
258       }
259 
260       // After a successful server-side save, the client is (optionally)
261       // updated with the server-side state.
262       var model = this;
263       var success = options.success;
264       var attributes = this.attributes;
265       options.success = function(resp) {
266         // Ensure attributes are restored during synchronous saves.
267         model.attributes = attributes;
268         var serverAttrs = options.parse ? model.parse(resp, options) : resp;
269         if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
270         if (serverAttrs && !model.set(serverAttrs, options)) return false;
271         if (success) success.call(options.context, model, resp, options);
272         model.trigger('sync', model, resp, options);
273       };
274       wrapError(this, options);
275 
276       // Set temporary attributes if `{wait: true}` to properly find new ids.
277       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
278 
279       var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
280       if (method === 'patch' && !options.attrs) options.attrs = attrs;
281       var xhr = this.sync(method, this, options);
282 
283       // Restore attributes.
284       this.attributes = attributes;
285 
286       return xhr;
287     },
288 
289     // Destroy this model on the server if it was already persisted.
290     // Optimistically removes the model from its collection, if it has one.
291     // If `wait: true` is passed, waits for the server to respond before removal.
292     destroy: function(options) {
293       options = options ? _.clone(options) : {};
294       var model = this;
295       var success = options.success;
296       var wait = options.wait;
297 
298       var destroy = function() {
299         model.stopListening();
300         model.trigger('destroy', model, model.collection, options);
301       };
302 
303       options.success = function(resp) {
304         if (wait) destroy();
305         if (success) success.call(options.context, model, resp, options);
306         if (!model.isNew()) model.trigger('sync', model, resp, options);
307       };
308 
309       var xhr = false;
310       if (this.isNew()) {
311         _.defer(options.success);
312       } else {
313         wrapError(this, options);
314         xhr = this.sync('delete', this, options);
315       }
316       if (!wait) destroy();
317       return xhr;
318     },
319 
320     // Default URL for the model's representation on the server -- if you're
321     // using Backbone's restful methods, override this to change the endpoint
322     // that will be called.
323     url: function() {
324       var base =
325         _.result(this, 'urlRoot') ||
326         _.result(this.collection, 'url') ||
327         urlError();
328       if (this.isNew()) return base;
329       var id = this.get(this.idAttribute);
330       return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
331     },
332 
333     // **parse** converts a response into the hash of attributes to be `set` on
334     // the model. The default implementation is just to pass the response along.
335     //可以覆寫這個方法,有待檢測,沒有測試
336     // parse:function(resp,options){
337         // return options.parse(resp);
338     // }
339     parse: function(resp, options) {
340       return resp;
341     },
342 
343     // Create a new model with identical attributes to this one.
344     clone: function() {
345       return new this.constructor(this.attributes);
346     },
347 
348     // A model is new if it has never been saved to the server, and lacks an id.
349     isNew: function() {
350       return !this.has(this.idAttribute);
351     },
352 
353     // Check if the model is currently in a valid state.
354     isValid: function(options) {
355       return this._validate({}, _.extend({}, options, {validate: true}));
356     },
357 
358     // Run validation against the next complete set of model attributes,
359     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
360     _validate: function(attrs, options) {
361       if (!options.validate || !this.validate) return true;
362       attrs = _.extend({}, this.attributes, attrs);
363       var error = this.validationError = this.validate(attrs, options) || null;
364       if (!error) return true;
365       this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
366       return false;
367     }
368 
369   });
370 
371   // Underscore methods that we want to implement on the Model, mapped to the
372   // number of arguments they take.
373   var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
374       omit: 0, chain: 1, isEmpty: 1};
375 
376   // Mix in each Underscore method as a proxy to `Model#attributes`.
377   addUnderscoreMethods(Model, modelMethods, 'attributes');

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 效果展示 http://hovertree.com/texiao/nav/4/手機掃描二維碼查看效果:源碼下載 http://hovertree.com/h/bjaf/kroft6c7.htm效果圖如下:代碼如下: <!doctype html> <html lang="zh"> <head> <m ...
  • 隨機色有兩種格式: 效果預覽:http://wjf444128852.github.io/DEMOLIST/JS/test/index.html 1、rgb(xxx,xxx,xxx) 2、#xxxxxx 下麵實現兩種隨機的方法 思路: 就是如何讓x都是隨機的, 1、中的xxx是0-255之間的隨機整 ...
  • 總結:總的來說,這些控制項可以在官網找到列子, 部分ui效果不如意的,可根據jQueryUI上添加的類選擇器等,進行再加工 ...
  • 最近CTO給我分配了一個移動端H5開發的任務,主要功能是需要實現翻書效果,我聽過主要需求後,當時是呀!!!接下來自己嘗試使用 fullPage.js和Swiper來實現翻書效果,結果效果都不是非常的理想,後來想起自己曾經做過PC版的翻書效果,當時使用的是Turn.js ,查過其相關API後,整個人突 ...
  • 前言:1.使用setInterval()的定時器會把事件運行的時間也包含在內,如果要精確算定時兩個任務之間的時間,可以使用setTimeout()替換。2.當非同步事件發生時,如mouse click, a timer firing, or an XMLHttpRequest completing(鼠 ...
  • 對象,是javascript中非常重要的一個梗,是否能透徹的理解它直接關係到你對整個javascript體系的基礎理解,說白了,javascript就是一群對象在攪。。(嗶!)。 常用的幾種對象創建模式 使用new關鍵字創建 最基礎的對象創建方式,無非就是和其他多數語言一樣說的一樣:沒對象,你new ...
  • ...
  • 一:GIF(Graphics Interchange Format) 簡介GIF圖形交換格式是一種點陣圖圖形文件格式,以8位色(即256種顏色)重現真彩色的圖像。 它實際上是一種壓縮文檔,採用LZW壓縮演算法進行編碼,有效地減少了圖像文件在網路上傳輸的時間。 它是目前廣泛應用於網路傳輸的圖像格式之一。優 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...