开发者社区> 亦才> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Backbone.js 0.9.2 源码分析收藏

简介:
+关注继续查看

Backbone 为复杂Javascript应用程序提供模型(models)、集合(collections)、视图(views)的结构。其中模型用于绑定键值数据和自定义事件;集合附有可枚举函数的丰富API; 视图可以声明事件处理函数,并通过RESRful JSON接口连接到应用程序。


源码分析转之网上它人的备注,特收藏一下,以免方便阅读。

// Backbone.js 0.9.2

// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function() {

    // 创建一个全局对象, 在浏览器中表示为window对象, 在Node.js中表示global对象
    var root = this;

    // 保存"Backbone"变量被覆盖之前的值
    // 如果出现命名冲突或考虑到规范, 可通过Backbone.noConflict()方法恢复该变量被Backbone占用之前的值, 并返回Backbone对象以便重新命名
    var previousBackbone = root.Backbone;

    // 将Array.prototype中的slice和splice方法缓存到局部变量以供调用
    var slice = Array.prototype.slice;
    var splice = Array.prototype.splice;

    var Backbone;
    if( typeof exports !== 'undefined') {
        Backbone = exports;
    } else {
        Backbone = root.Backbone = {};
    }

    // 定义Backbone版本
    Backbone.VERSION = '0.9.2';

    // 在服务器环境下自动导入Underscore, 在Backbone中部分方法依赖或继承自Underscore
    var _ = root._;
    if(!_ && ( typeof require !== 'undefined'))
        _ = require('underscore');

    // 定义第三方库为统一的变量"$", 用于在视图(View), 事件处理和与服务器数据同步(sync)时调用库中的方法
    // 支持的库包括jQuery, Zepto等, 它们语法相同, 但Zepto更适用移动开发, 它主要针对Webkit内核浏览器
    // 也可以通过自定义一个与jQuery语法相似的自定义库, 供Backbone使用(有时我们可能需要一个比jQuery, Zepto更轻巧的自定义版本)
    // 这里定义的"$"是局部变量, 因此不会影响在Backbone框架之外第三方库的正常使用
    var $ = root.jQuery || root.Zepto || root.ender;

    // 手动设置第三方库
    // 如果在导入了Backbone之前并没有导入第三方库, 可以通过setDomLibrary方法设置"$"局部变量
    // setDomLibrary方法也常用于在Backbone中动态导入自定义库
    Backbone.setDomLibrary = function(lib) {
        $ = lib;
    };
    // 放弃以"Backbone"命名框架, 并返回Backbone对象, 一般用于避免命名冲突或规范命名方式
    // 例如:
    // var bk = Backbone.noConflict(); // 取消"Backbone"命名, 并将Backbone对象存放于bk变量中
    // console.log(Backbone); // 该变量已经无法再访问Backbone对象, 而恢复为Backbone定义前的值
    // var MyBackbone = bk; // 而bk存储了Backbone对象, 我们将它重命名为MyBackbone
    Backbone.noConflict = function() {
        root.Backbone = previousBackbone;
        return this;
    };
    // 对于不支持REST方式的浏览器, 可以设置Backbone.emulateHTTP = true
    // 与服务器请求将以POST方式发送, 并在数据中加入_method参数标识操作名称, 同时也将发送X-HTTP-Method-Override头信息
    Backbone.emulateHTTP = false;

    // 对于不支持application/json编码的浏览器, 可以设置Backbone.emulateJSON = true;
    // 将请求类型设置为application/x-www-form-urlencoded, 并将数据放置在model参数中实现兼容
    Backbone.emulateJSON = false;

    // Backbone.Events 自定义事件相关
    // -----------------

    // eventSplitter指定处理多个事件时, 事件名称的解析规则
    var eventSplitter = /\s+/;

    // 自定义事件管理器
    // 通过在对象中绑定Events相关方法, 允许向对象添加, 删除和触发自定义事件
    var Events = Backbone.Events = {

        // 将自定义事件(events)和回调函数(callback)绑定到当前对象
        // 回调函数中的上下文对象为指定的context, 如果没有设置context则上下文对象默认为当前绑定事件的对象
        // 该方法类似与DOM Level2中的addEventListener方法
        // events允许指定多个事件名称, 通过空白字符进行分隔(如空格, 制表符等)
        // 当事件名称为"all"时, 在调用trigger方法触发任何事件时, 均会调用"all"事件中绑定的所有回调函数
        on : function(events, callback, context) {
            // 定义一些函数中使用到的局部变量
            var calls, event, node, tail, list;
            // 必须设置callback回调函数
            if(!callback)
                return this;
            // 通过eventSplitter对事件名称进行解析, 使用split将多个事件名拆分为一个数组
            // 一般使用空白字符指定多个事件名称
            events = events.split(eventSplitter);
            // calls记录了当前对象中已绑定的事件与回调函数列表
            calls = this._callbacks || (this._callbacks = {});

            // 循环事件名列表, 从头至尾依次将事件名存放至event变量
            while( event = events.shift()) {
                // 获取已经绑定event事件的回调函数
                // list存储单个事件名中绑定的callback回调函数列表
                // 函数列表并没有通过数组方式存储, 而是通过多个对象的next属性进行依次关联
                /** 数据格式如:
                 * {
                 *     tail: {Object},
                 *     next: {
                 *         callback: {Function},
                 *         context: {Object},
                 *         next: {
                 *             callback: {Function},
                 *             context: {Object},
                 *             next: {Object}
                 *         }
                 *     }
                 * }
                 */
                // 列表每一层next对象存储了一次回调事件相关信息(函数体, 上下文和下一次回调事件)
                // 事件列表最顶层存储了一个tail对象, 它存储了最后一次绑定回调事件的标识(与最后一次回调事件的next指向同一个对象)
                // 通过tail标识, 可以在遍历回调列表时得知已经到达最后一个回调函数
                list = calls[event];
                // node变量用于记录本次回调函数的相关信息
                // tail只存储最后一次绑定回调函数的标识
                // 因此如果之前已经绑定过回调函数, 则将之前的tail指定给node作为一个对象使用, 然后创建一个新的对象标识给tail
                // 这里之所以要将本次回调事件添加到上一次回调的tail对象, 是为了让回调函数列表的对象层次关系按照绑定顺序排列(最新绑定的事件将被放到最底层)
                node = list ? list.tail : {};
                node.next = tail = {};
                // 记录本次回调的函数体及上下文信息
                node.context = context;
                node.callback = callback;
                // 重新组装当前事件的回调列表, 列表中已经加入了本次回调事件
                calls[event] = {
                    tail : tail,
                    next : list ? list.next : node
                };
            }
            // 返回当前对象, 方便进行方法链调用
            return this;
        },
        // 移除对象中已绑定的事件或回调函数, 可以通过events, callback和context对需要删除的事件或回调函数进行过滤
        // - 如果context为空, 则移除所有的callback指定的函数
        // - 如果callback为空, 则移除事件中所有的回调函数
        // - 如果events为空, 但指定了callback或context, 则移除callback或context指定的回调函数(不区分事件名称)
        // - 如果没有传递任何参数, 则移除对象中绑定的所有事件和回调函数
        off : function(events, callback, context) {
            var event, calls, node, tail, cb, ctx;

            // No events, or removing *all* events.
            // 当前对象没有绑定任何事件
            if(!( calls = this._callbacks))
                return;
            // 如果没有指定任何参数, 则移除所有事件和回调函数(删除_callbacks属性)
            if(!(events || callback || context)) {
                delete this._callbacks;
                return this;
            }

            // 解析需要移除的事件列表
            // - 如果指定了events, 则按照eventSplitter对事件名进行解析
            // - 如果没有指定events, 则解析已绑定所有事件的名称列表
            events = events ? events.split(eventSplitter) : _.keys(calls);

            // 循环事件名列表
            while( event = events.shift()) {
                // 将当前事件对象从列表中移除, 并缓存到node变量中
                node = calls[event];
                delete calls[event];
                // 如果不存在当前事件对象(或没有指定移除过滤条件, 则认为将移除当前事件及所有回调函数), 则终止此次操作(事件对象在上一步已经移除)
                if(!node || !(callback || context))
                    continue;
                // Create a new list, omitting the indicated callbacks.
                // 根据回调函数或上下文过滤条件, 组装一个新的事件对象并重新绑定
                tail = node.tail;
                // 遍历事件中的所有回调对象
                while(( node = node.next) !== tail) {
                    cb = node.callback;
                    ctx = node.context;
                    // 根据参数中的回调函数和上下文, 对回调函数进行过滤, 将不符合过滤条件的回调函数重新绑定到事件中(因为事件中的所有回调函数在上面已经被移除)
                    if((callback && cb !== callback) || (context && ctx !== context)) {
                        this.on(event, cb, ctx);
                    }
                }
            }

            return this;
        },
        // 触发已经定义的一个或多个事件, 依次执行绑定的回调函数列表
        trigger : function(events) {
            var event, node, calls, tail, args, all, rest;
            // 当前对象没有绑定任何事件
            if(!( calls = this._callbacks))
                return this;
            // 获取回调函数列表中绑定的"all"事件列表
            all = calls.all;
            // 将需要触发的事件名称, 按照eventSplitter规则解析为一个数组
            events = events.split(eventSplitter);
            // 将trigger从第2个之后的参数, 记录到rest变量, 将依次传递给回调函数
            rest = slice.call(arguments, 1);

            // 循环需要触发的事件列表
            while( event = events.shift()) {
                // 此处的node变量记录了当前事件的所有回调函数列表
                if( node = calls[event]) {
                    // tail变量记录最后一次绑定事件的对象标识
                    tail = node.tail;
                    // node变量的值, 按照事件的绑定顺序, 被依次赋值为绑定的单个回调事件对象
                    // 最后一次绑定的事件next属性, 与tail引用同一个对象, 以此作为是否到达列表末尾的判断依据
                    while(( node = node.next) !== tail) {
                        // 执行所有绑定的事件, 并将调用trigger时的参数传递给回调函数
                        node.callback.apply(node.context || this, rest);
                    }
                }
                // 变量all记录了绑定时的"all"事件, 即在调用任何事件时, "all"事件中的回调函数均会被执行
                // - "all"事件中的回调函数无论绑定顺序如何, 都会在当前事件的回调函数列表全部执行完毕后再依次执行
                // - "all"事件应该在触发普通事件时被自动调用, 如果强制触发"all"事件, 事件中的回调函数将被执行两次
                if( node = all) {
                    tail = node.tail;
                    // 与调用普通事件的回调函数不同之处在于, all事件会将当前调用的事件名作为第一个参数传递给回调函数
                    args = [event].concat(rest);
                    // 遍历并执行"all"事件中的回调函数列表
                    while(( node = node.next) !== tail) {
                        node.callback.apply(node.context || this, args);
                    }
                }
            }

            return this;
        }
    };

    // 绑定事件与释放事件的别名, 也为了同时兼容Backbone以前的版本
    Events.bind = Events.on;
    Events.unbind = Events.off;

    // Backbone.Model 数据对象模型
    // --------------

    // Model是Backbone中所有数据对象模型的基类, 用于创建一个数据模型
    // @param {Object} attributes 指定创建模型时的初始化数据
    // @param {Object} options
    /**
     * @format options
     * {
     *     parse: {Boolean},
     *     collection: {Collection}
     * }
     */
    var Model = Backbone.Model = function(attributes, options) {
        // defaults变量用于存储模型的默认数据
        var defaults;
        // 如果没有指定attributes参数, 则设置attributes为空对象
        attributes || ( attributes = {});
        // 设置attributes默认数据的解析方法, 例如默认数据是从服务器获取(或原始数据是XML格式), 为了兼容set方法所需的数据格式, 可使用parse方法进行解析
        if(options && options.parse)
            attributes = this.parse(attributes);
        if( defaults = getValue(this, 'defaults')) {
            // 如果Model在定义时设置了defaults默认数据, 则初始化数据使用defaults与attributes参数合并后的数据(attributes中的数据会覆盖defaults中的同名数据)
            attributes = _.extend({}, defaults, attributes);
        }
        // 显式指定模型所属的Collection对象(在调用Collection的add, push等将模型添加到集合中的方法时, 会自动设置模型所属的Collection对象)
        if(options && options.collection)
            this.collection = options.collection;
        // attributes属性存储了当前模型的JSON对象化数据, 创建模型时默认为空
        this.attributes = {};
        // 定义_escapedAttributes缓存对象, 它将缓存通过escape方法处理过的数据
        this._escapedAttributes = {};
        // 为每一个模型配置一个唯一标识
        this.cid = _.uniqueId('c');
        // 定义一系列用于记录数据状态的对象, 具体含义请参考对象定义时的注释
        this.changed = {};
        this._silent = {};
        this._pending = {};
        // 创建实例时设置初始化数据, 首次设置使用silent参数, 不会触发change事件
        this.set(attributes, {
            silent : true
        });
        // 上面已经设置了初始化数据, changed, _silent, _pending对象的状态可能已经发生变化, 这里重新进行初始化
        this.changed = {};
        this._silent = {};
        this._pending = {};
        // _previousAttributes变量存储模型数据的一个副本
        // 用于在change事件中获取模型数据被改变之前的状态, 可通过previous或previousAttributes方法获取上一个状态的数据
        this._previousAttributes = _.clone(this.attributes);
        // 调用initialize初始化方法
        this.initialize.apply(this, arguments);
    };
    // 使用extend方法为Model原型定义一系列属性和方法
    _.extend(Model.prototype, Events, {

        // changed属性记录了每次调用set方法时, 被改变数据的key集合
        changed : null,

        // // 当指定silent属性时, 不会触发change事件, 被改变的数据会记录下来, 直到下一次触发change事件
        // _silent属性用来记录使用silent时的被改变的数据
        _silent : null,

        _pending : null,

        // 每个模型的唯一标识属性(默认为"id", 通过修改idAttribute可自定义id属性名)
        // 如果在设置数据时包含了id属性, 则id将会覆盖模型的id
        // id用于在Collection集合中查找和标识模型, 与后台接口通信时也会以id作为一条记录的标识
        idAttribute : 'id',

        // 模型初始化方法, 在模型被构造结束后自动调用
        initialize : function() {
        },
        // 返回当前模型中数据的一个副本(JSON对象格式)
        toJSON : function(options) {
            return _.clone(this.attributes);
        },
        // 根据attr属性名, 获取模型中的数据值
        get : function(attr) {
            return this.attributes[attr];
        },
        // 根据attr属性名, 获取模型中的数据值, 数据值包含的HTML特殊字符将被转换为HTML实体, 包含 & < > " ' \
        // 通过 _.escape方法实现
        escape : function(attr) {
            var html;
            // 从_escapedAttributes缓存对象中查找数据, 如果数据已经被缓存则直接返回
            if( html = this._escapedAttributes[attr])
                return html;
            // _escapedAttributes缓存对象中没有找到数据
            // 则先从模型中获取数据
            var val = this.get(attr);
            // 将数据中的HTML使用 _.escape方法转换为实体, 并缓存到_escapedAttributes对象, 便于下次直接获取
            return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
        },
        // 检查模型中是否存在某个属性, 当该属性的值被转换为Boolean类型后值为false, 则认为不存在
        // 如果值为false, null, undefined, 0, NaN, 或空字符串时, 均会被转换为false
        has : function(attr) {
            return this.get(attr) != null;
        },
        // 设置模型中的数据, 如果key值不存在, 则作为新的属性添加到模型, 如果key值已经存在, 则修改为新的值
        set : function(key, value, options) {
            // attrs变量中记录需要设置的数据对象
            var attrs, attr, val;

            // 参数形式允许key-value对象形式, 或通过key, value两个参数进行单独设置
            // 如果key是一个对象, 则认定为使用对象形式设置, 第二个参数将被视为options参数
            if(_.isObject(key) || key == null) {
                attrs = key;
                options = value;
            } else {
                // 通过key, value两个参数单独设置, 将数据放到attrs对象中方便统一处理
                attrs = {};
                attrs[key] = value;
            }

            // options配置项必须是一个对象, 如果没有设置options则默认值为一个空对象
            options || ( options = {});
            // 没有设置参数时不执行任何动作
            if(!attrs)
                return this;
            // 如果被设置的数据对象属于Model类的一个实例, 则将Model对象的attributes数据对象赋给attrs
            // 一般在复制一个Model对象的数据到另一个Model对象时, 会执行该动作
            if( attrs instanceof Model)
                attrs = attrs.attributes;
            // 如果options配置对象中设置了unset属性, 则将attrs数据对象中的所有属性重置为undefined
            // 一般在复制一个Model对象的数据到另一个Model对象时, 但仅仅需要复制Model中的数据而不需要复制值时执行该操作
            if(options.unset)
                for(attr in attrs)
                attrs[attr] =
                void 0;

            // 对当前数据进行验证, 如果验证未通过则停止执行
            if(!this._validate(attrs, options))
                return false;

            // 如果设置的id属性名被包含在数据集合中, 则将id覆盖到模型的id属性
            // 这是为了确保在自定义id属性名后, 访问模型的id属性时, 也能正确访问到id
            if(this.idAttribute in attrs)
                this.id = attrs[this.idAttribute];

            var changes = options.changes = {};
            // now记录当前模型中的数据对象
            var now = this.attributes;
            // escaped记录当前模型中通过escape缓存过的数据
            var escaped = this._escapedAttributes;
            // prev记录模型中数据被改变之前的值
            var prev = this._previousAttributes || {};

            // 遍历需要设置的数据对象
            for(attr in attrs) {
                // attr存储当前属性名称, val存储当前属性的值
                val = attrs[attr];

                // 如果当前数据在模型中不存在, 或已经发生变化, 或在options中指定了unset属性删除, 则删除该数据被换存在_escapedAttributes中的数据
                if(!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) {
                    // 仅删除通过escape缓存过的数据, 这是为了保证缓存中的数据与模型中的真实数据保持同步
                    delete escaped[attr];
                    // 如果指定了silent属性, 则此次set方法调用不会触发change事件, 因此将被改变的数据记录到_silent属性中, 便于下一次触发change事件时, 通知事件监听函数此数据已经改变
                    // 如果没有指定silent属性, 则直接设置changes属性中当前数据为已改变状态
                    (options.silent ? this._silent : changes)[attr] = true;
                }

                // 如果在options中设置了unset, 则从模型中删除该数据(包括key)
                // 如果没有指定unset属性, 则认为将新增或修改数据, 向模型的数据对象中加入新的数据
                options.unset ?
                delete now[attr] : now[attr] = val;

                // 如果模型中的数据与新的数据不一致, 则表示该数据已发生变化
                if(!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
                    // 在changed属性中记录当前属性已经发生变化的状态
                    this.changed[attr] = val;
                    if(!options.silent)
                        this._pending[attr] = true;
                } else {
                    // 如果数据没有发生变化, 则从changed属性中移除已变化状态
                    delete this.changed[attr];
                    delete this._pending[attr];
                }
            }

            // 调用change方法, 将触发change事件绑定的函数
            if(!options.silent)
                this.change(options);
            return this;
        },
        // 从当前模型中删除指定的数据(属性也将被同时删除)
        unset : function(attr, options) {
            (options || ( options = {})).unset = true;
            // 通过options.unset配置项告知set方法进行删除操作
            return this.set(attr, null, options);
        },
        // 清除当前模型中的所有数据和属性
        clear : function(options) {
            (options || ( options = {})).unset = true;
            // 克隆一个当前模型的属性副本, 并通过options.unset配置项告知set方法执行删除操作
            return this.set(_.clone(this.attributes), options);
        },
        // 从服务器获取默认的模型数据, 获取数据后使用set方法将数据填充到模型, 因此如果获取到的数据与当前模型中的数据不一致, 将会触发change事件
        fetch : function(options) {
            // 确保options是一个新的对象, 随后将改变options中的属性
            options = options ? _.clone(options) : {};
            var model = this;
            // 在options中可以指定获取数据成功后的自定义回调函数
            var success = options.success;
            // 当获取数据成功后填充数据并调用自定义成功回调函数
            options.success = function(resp, status, xhr) {
                // 通过parse方法将服务器返回的数据进行转换
                // 通过set方法将转换后的数据填充到模型中, 因此可能会触发change事件(当数据发生变化时)
                // 如果填充数据时验证失败, 则不会调用自定义success回调函数
                if(!model.set(model.parse(resp, xhr), options))
                    return false;
                // 调用自定义的success回调函数
                if(success)
                    success(model, resp);
            };
            // 请求发生错误时通过wrapError处理error事件
            options.error = Backbone.wrapError(options.error, model, options);
            // 调用sync方法从服务器获取数据
            return (this.sync || Backbone.sync).call(this, 'read', this, options);
        },
        // 保存模型中的数据到服务器
        save : function(key, value, options) {
            // attrs存储需要保存到服务器的数据对象
            var attrs, current;

            // 支持设置单个属性的方式 key: value
            // 支持对象形式的批量设置方式 {key: value}
            if(_.isObject(key) || key == null) {
                // 如果key是一个对象, 则认为是通过对象方式设置
                // 此时第二个参数被认为是options
                attrs = key;
                options = value;
            } else {
                // 如果是通过key: value形式设置单个属性, 则直接设置attrs
                attrs = {};
                attrs[key] = value;
            }
            // 配置对象必须是一个新的对象
            options = options ? _.clone(options) : {};

            // 如果在options中设置了wait选项, 则被改变的数据将会被提前验证, 且服务器没有响应新数据(或响应失败)时, 本地数据会被还原为修改前的状态
            // 如果没有设置wait选项, 则无论服务器是否设置成功, 本地数据均会被修改为最新状态
            if(options.wait) {
                // 对需要保存的数据提前进行验证
                if(!this._validate(attrs, options))
                    return false;
                // 记录当前模型中的数据, 用于在将数据发送到服务器后, 将数据进行还原
                // 如果服务器响应失败或没有返回数据, 则可以保持修改前的状态
                current = _.clone(this.attributes);
            }

            // silentOptions在options对象中加入了silent(不对数据进行验证)
            // 当使用wait参数时使用silentOptions配置项, 因为在上面已经对数据进行过验证
            // 如果没有设置wait参数, 则仍然使用原始的options配置项
            var silentOptions = _.extend({}, options, {
                silent : true
            });
            // 将修改过最新的数据保存到模型中, 便于在sync方法中获取模型数据保存到服务器
            if(attrs && !this.set(attrs, options.wait ? silentOptions : options)) {
                return false;
            }

            var model = this;
            // 在options中可以指定保存数据成功后的自定义回调函数
            var success = options.success;
            // 服务器响应成功后执行success
            options.success = function(resp, status, xhr) {
                // 获取服务器响应最新状态的数据
                var serverAttrs = model.parse(resp, xhr);
                // 如果使用了wait参数, 则优先将修改后的数据状态直接设置到模型
                if(options.wait) {
                    delete options.wait;
                    serverAttrs = _.extend(attrs || {}, serverAttrs);
                }
                // 将最新的数据状态设置到模型中
                // 如果调用set方法时验证失败, 则不会调用自定义的success回调函数
                if(!model.set(serverAttrs, options))
                    return false;
                if(success) {
                    // 调用响应成功后自定义的success回调函数
                    success(model, resp);
                } else {
                    // 如果没有指定自定义回调, 则默认触发sync事件
                    model.trigger('sync', model, resp, options);
                }
            };
            // 请求发生错误时通过wrapError处理error事件
            options.error = Backbone.wrapError(options.error, model, options);
            // 将模型中的数据保存到服务器
            // 如果当前模型是一个新建的模型(没有id), 则使用create方法(新增), 否则认为是update方法(修改)
            var method = this.isNew() ? 'create' : 'update';
            var xhr = (this.sync || Backbone.sync).call(this, method, this, options);
            // 如果设置了options.wait, 则将数据还原为修改前的状态
            // 此时保存的请求还没有得到响应, 因此如果响应失败, 模型中将保持修改前的状态, 如果服务器响应成功, 则会在success中设置模型中的数据为最新状态
            if(options.wait)
                this.set(current, silentOptions);
            return xhr;
        },
        // 删除模型, 模型将同时从所属的Collection集合中被删除
        // 如果模型是在客户端新建的, 则直接从客户端删除
        // 如果模型数据同时存在服务器, 则同时会删除服务器端的数据
        destroy : function(options) {
            // 配置项必须是一个新的对象
            options = options ? _.clone(options) : {};
            var model = this;
            // 在options中可以指定删除数据成功后的自定义回调函数
            var success = options.success;
            // 删除数据成功调用, 触发destroy事件, 如果模型存在于Collection集合中, 集合将监听destroy事件并在触发时从集合中移除该模型
            // 删除模型时, 模型中的数据并没有被清空, 但模型已经从集合中移除, 因此当没有任何地方引用该模型时, 会被自动从内存中释放
            // 建议在删除模型时, 将模型对象的引用变量设置为null
            var triggerDestroy = function() {
                model.trigger('destroy', model, model.collection, options);
            };
            // 如果该模型是一个客户端新建的模型, 则直接调用triggerDestroy从集合中将模型移除
            if(this.isNew()) {
                triggerDestroy();
                return false;
            }

            // 当从服务器删除数据成功时
            options.success = function(resp) {
                // 如果在options对象中配置wait项, 则表示本地内存中的模型数据, 会在服务器数据被删除成功后再删除
                // 如果服务器响应失败, 则本地数据

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Ubuntu 20.04.2 LTS安装 最新版 微信(wine)
Ubuntu 20.04.2 LTS安装 最新版 微信(wine)
671 0
SpringCloud容错处理:Hystrix源码分析
SpringCloud容错处理:Hystrix源码分析
87 0
Jvm-Sandbox源码分析--增强目标类
在前两篇文章Jvm-Sandbox源码分析--启动简析和Jvm-Sandbox源码分析--启动时加载模块中我们分析了jvm-sandbox启动及启动时加载模块的过程,这一篇我们来看一下如何使用模块增强目标类的流程。
3571 0
虚拟机安装ubuntu之后无法使用ssh连接
虚拟机安装好ubuntu后,使用xhell连接不上: ![1](https://yqfile.alicdn.com/f05e212860806cef77ed0b5a5742c48ad0062178.
2196 0
MySQL · 源码分析 · Query Cache并发处理
Query cache 的并发处理 上期介绍了Query cache的一个基本工作原理,请参考MySQL · 源码分析 · Query Cache内部剖析。本期将对Query cache的并发处理过程进行一个剖析。 当前Query cache是所有session共享的,也就是说同一条SELECT语句 + database + flag(包含影响执行结果的所有环境变量)构成的Key如果已经存储在
1412 0
Ubuntu下(c/c++)软件环境的安装
#!/bin/sh sudo apt-get update sudo apt-get install tree sudo apt-get install vim -y sudo echo "sudo cp vimrc /etc/vim/vimrc" sudo cp vimrc /etc/vim/vimrc sudo apt-get install stardict -y su
1203 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
React Native项目实战优化之路
立即下载
Spring框架入门
立即下载
Storm源码走读笔记
立即下载