7-14 backbone源码
1 _.extend = function(obj) { 2 // each循环参数中的一个或多个对象 3 each(slice.call(arguments, 1), function(source) { 4 // 将对象中的全部属性复制或覆盖到obj对象 5 for(var prop in source) { 6 obj[prop] = source[prop]; 7 } 8 }); 9 return obj; 10 };
1:上面是underscore旧版本的extend方法代码(和现在API调用方法不同),经我测试object.slice无法使用,但是加入下面两行就可以slice.call(x,y)这样调用了,但是依旧不可以object.slice(undefined)。
1 var ArrayProto = Array.prototype; 2 var slice = ArrayProto.slice;
2:现版本的extend方法
1 // Retrieve all the property names of an object. 2 _.allKeys = function(obj) { 3 if (!_.isObject(obj)) return []; 4 var keys = []; 5 for (var key in obj) keys.push(key); 6 // Ahem, IE < 9. 7 if (hasEnumBug) collectNonEnumProps(obj, keys); 8 return keys; 9 }; 10 11 _.extend = createAssigner(_.allKeys); 12 13 var createAssigner = function(keysFunc, undefinedOnly) { 14 return function(obj) { 15 var length = arguments.length; 16 if (length < 2 || obj == null) return obj; 17 for (var index = 1; index < length; index++) { 18 var source = arguments[index], 19 keys = keysFunc(source), 20 l = keys.length; 21 for (var i = 0; i < l; i++) { 22 var key = keys[i]; 23 if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; 24 } 25 } 26 return obj; 27 }; 28 };
3:JS精确整数最大值和判断应该以数组还是对象的方式进行迭代
1 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; 2 var isArrayLike = function(collection) { 3 var length = collection != null && collection.length; 4 return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; 5 };
1:模块化开发,AMD
2:从backbone的总体结构来看,是一个立即执行的函数表达式,参数是一个匿名函数。(function(){})()和(function(){}())的目的是将函数声明转换为函数表达式,消除Js引擎在识别函数声明和函数表达式上的歧义,除了小括号外还有其他运算符能够做到,详细介绍可以参照这篇文章:js中(function(){…})()立即执行函数写法理解
1 (function(factory) { 2 //模块定义 3 })(function(root, Backbone, _, $) { 4 //Backbone 5 });
模块处理内容如下:
1 function(factory) { 2 3 // Establish the root object, `window` (`self`) in the browser, or `global` on the server. 4 // We use `self` instead of `window` for `WebWorker` support. 5 //拿到当前环境中的全局对象;浏览器中为window,self也是浏览器提供的一个全局对象,始终指向window 6 //server端的运行环境则提供global这个全局对象 7 var root = (typeof self == 'object' && self.self === self && self) || 8 (typeof global == 'object' && global.global === global && global); 9 10 // Set up Backbone appropriately for the environment. Start with AMD. 11 //如果有amd加载器则将Backbone定义包装成amd加载器可识别的模块 12 if (typeof define === 'function' && define.amd) { 13 //AMD规范定义两个全局函数define和requrie,并且规定define有个amd属性,来区分amd的define和普通名为define的函数 14 define(['underscore', 'jquery', 'exports'], function(_, $, exports) { 15 // Export global even in AMD case in case this script is loaded with 16 // others that may still expect a global Backbone. 17 root.Backbone = factory(root, exports, _, $); 18 }); 19 20 // Next for Node.js or CommonJS. jQuery may not be needed as a module. 21 //如果运行在Node端,则将Backbone包装成CommonJs的模块 22 } else if (typeof exports !== 'undefined') { 23 var _ = require('underscore'), $; 24 try { $ = require('jquery'); } catch (e) {} 25 factory(root, exports, _, $); 26 27 // Finally, as a browser global. 28 //以上两种情况都没有,则以最简单的执行函数方式,将函数的返回值作为全局对象Backbone 29 } else { 30 root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); 31 } 32 33 }
factory部分整体结构如下:
1 function(root, Backbone, _, $) { 2 // Backbone.Events 3 // --------------- 4 5 // Backbone.Model 6 // -------------- 7 8 // Backbone.Collection 9 // ------------------- 10 11 // Backbone.View 12 // ------------- 13 14 // Backbone.Router 15 // --------------- 16 17 // Backbone.History 18 // ---------------- 19 20 // Helpers 21 // ------- 22 }
Backbone的每个部分都有自己的extend属性,并且都有默认继承的方法,参数只是对默认方法的覆盖
1 // Helper function to correctly set up the prototype chain for subclasses. 2 // Similar to `goog.inherits`, but uses a hash of prototype properties and 3 // class properties to be extended. 4 //protoProps放置到子类原型上的属性 5 //staticProps模拟静态属性,直接放置到子类上 6 var extend = function(protoProps, staticProps) { 7 var parent = this;//利用局部变量保存this关键字 8 var child; 9 10 // The constructor function for the new subclass is either defined by you 11 // (the "constructor" property in your `extend` definition), or defaulted 12 // by us to simply call the parent constructor. 13 //如果protoProps中有constructor属性,则将constructor指向的函数作为构造函数 14 if (protoProps && _.has(protoProps, 'constructor')) { 15 child = protoProps.constructor; 16 } else {//没有构造函数,则利用一个默认的函数作为构造函数。 17 //基本上属于组合式继承 18 child = function(){ return parent.apply(this, arguments); }; 19 } 20 21 // Add static properties to the constructor function, if supplied. 22 //underscore中的方法,与常见的mixin函数类似 23 _.extend(child, parent, staticProps); 24 25 // Set the prototype chain to inherit from `parent`, without calling 26 // `parent`'s constructor function and add the prototype properties. 27 //将child的原型链与parent.prototype关联。 28 //_.create函数,的作用类似Object.create,第一个参数是要被继承的原型对象,第二个参数是要混入到新对象的键值对 29 child.prototype = _.create(parent.prototype, protoProps); 30 child.prototype.constructor = child;//原型中的constructor属性指向child 31 32 // Set a convenience property in case the parent's prototype is needed 33 // later. 34 child.__super__ = parent.prototype;//设置一个私有属性指向父类的原型 35 36 return child; 37 };
1 // Set up inheritance for the model, collection, router, view and history. 2 Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
注意:具体model的属性是在new的时候输入的,extend的时候只是对实例进行一些方法和属性的设置,比如default就是当new的时候不输入的属性的默认值!
①模型重点
当模型实例化时,他的initialize方法可以接受任意实例参数,其工作原理是backbone模型本身就是构造函数,所以可以使用new生成实例:
var User = Backbone.Model.extend({ initialize: function (name) { this.set({name: name}); } }); var user = new User('刀狂剑痴'); assertEqual(user.get('name'), '刀狂剑痴');
②
constructor / initializenew Model([attributes], [options])
当创建model实例时,可以传入 属性 (attributes)初始值,这些值会被 set (设置)到 model。 如果定义了 initialize 函数,该函数会在model创建后执行。
new Book({ title: "One Thousand and One Nights", author: "Scheherazade" });