国产无遮挡裸体免费直播视频,久久精品国产蜜臀av,动漫在线视频一区二区,欧亚日韩一区二区三区,久艹在线 免费视频,国产精品美女网站免费,正在播放 97超级视频在线观看,斗破苍穹年番在线观看免费,51最新乱码中文字幕

Backbone.js 0.9.2 源碼注釋中文翻譯版

 更新時間:2015年06月25日 09:11:19   投稿:junjie  
Backbone.js為復雜WEB應用程序提供模型(models)、集合(collections)、視圖(views)的結構。其中模型用于綁定鍵值數(shù)據(jù)和自定義事件;集合附有可枚舉函數(shù)的豐富API; 視圖可以聲明事件處理函數(shù),并通過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() {

 

  // 創(chuàng)建一個全局對象, 在瀏覽器中表示為window對象, 在Node.js中表示global對象

  var root = this;

 

  // 保存"Backbone"變量被覆蓋之前的值

  // 如果出現(xiàn)命名沖突或考慮到規(guī)范, 可通過Backbone.noConflict()方法恢復該變量被Backbone占用之前的值, 并返回Backbone對象以便重新命名

  var previousBackbone = root.Backbone;

 

  // 將Array.prototype中的slice和splice方法緩存到局部變量以供調(diào)用

  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';

 

  // 在服務器環(huán)境下自動導入Underscore, 在Backbone中部分方法依賴或繼承自Underscore

  var _ = root._;

  if(!_ && ( typeof require !== 'undefined'))

    _ = require('underscore');

 

  // 定義第三方庫為統(tǒng)一的變量"$", 用于在視圖(View), 事件處理和與服務器數(shù)據(jù)同步(sync)時調(diào)用庫中的方法

  // 支持的庫包括jQuery, Zepto等, 它們語法相同, 但Zepto更適用移動開發(fā), 它主要針對Webkit內(nèi)核瀏覽器

  // 也可以通過自定義一個與jQuery語法相似的自定義庫, 供Backbone使用(有時我們可能需要一個比jQuery, Zepto更輕巧的自定義版本)

  // 這里定義的"$"是局部變量, 因此不會影響在Backbone框架之外第三方庫的正常使用

  var $ = root.jQuery || root.Zepto || root.ender;

 

  // 手動設置第三方庫

  // 如果在導入了Backbone之前并沒有導入第三方庫, 可以通過setDomLibrary方法設置"$"局部變量

  // setDomLibrary方法也常用于在Backbone中動態(tài)導入自定義庫

  Backbone.setDomLibrary = function(lib) {

    $ = lib;

  };

  // 放棄以"Backbone"命名框架, 并返回Backbone對象, 一般用于避免命名沖突或規(guī)范命名方式

  // 例如:

  // var bk = Backbone.noConflict(); // 取消"Backbone"命名, 并將Backbone對象存放于bk變量中

  // console.log(Backbone); // 該變量已經(jīng)無法再訪問Backbone對象, 而恢復為Backbone定義前的值

  // var MyBackbone = bk; // 而bk存儲了Backbone對象, 我們將它重命名為MyBackbone

  Backbone.noConflict = function() {

    root.Backbone = previousBackbone;

    return this;

  };

  // 對于不支持REST方式的瀏覽器, 可以設置Backbone.emulateHTTP = true

  // 與服務器請求將以POST方式發(fā)送, 并在數(shù)據(jù)中加入_method參數(shù)標識操作名稱, 同時也將發(fā)送X-HTTP-Method-Override頭信息

  Backbone.emulateHTTP = false;

 

  // 對于不支持application/json編碼的瀏覽器, 可以設置Backbone.emulateJSON = true;

  // 將請求類型設置為application/x-www-form-urlencoded, 并將數(shù)據(jù)放置在model參數(shù)中實現(xiàn)兼容

  Backbone.emulateJSON = false;

 

  // Backbone.Events 自定義事件相關

  // -----------------

 

  // eventSplitter指定處理多個事件時, 事件名稱的解析規(guī)則

  var eventSplitter = /\s+/;

 

  // 自定義事件管理器

  // 通過在對象中綁定Events相關方法, 允許向對象添加, 刪除和觸發(fā)自定義事件

  var Events = Backbone.Events = {

 

    // 將自定義事件(events)和回調(diào)函數(shù)(callback)綁定到當前對象

    // 回調(diào)函數(shù)中的上下文對象為指定的context, 如果沒有設置context則上下文對象默認為當前綁定事件的對象

    // 該方法類似與DOM Level2中的addEventListener方法

    // events允許指定多個事件名稱, 通過空白字符進行分隔(如空格, 制表符等)

    // 當事件名稱為"all"時, 在調(diào)用trigger方法觸發(fā)任何事件時, 均會調(diào)用"all"事件中綁定的所有回調(diào)函數(shù)

    on : function(events, callback, context) {

      // 定義一些函數(shù)中使用到的局部變量

      var calls, event, node, tail, list;

      // 必須設置callback回調(diào)函數(shù)

      if(!callback)

        return this;

      // 通過eventSplitter對事件名稱進行解析, 使用split將多個事件名拆分為一個數(shù)組

      // 一般使用空白字符指定多個事件名稱

      events = events.split(eventSplitter);

      // calls記錄了當前對象中已綁定的事件與回調(diào)函數(shù)列表

      calls = this._callbacks || (this._callbacks = {});

 

      // 循環(huán)事件名列表, 從頭至尾依次將事件名存放至event變量

      while( event = events.shift()) {

        // 獲取已經(jīng)綁定event事件的回調(diào)函數(shù)

        // list存儲單個事件名中綁定的callback回調(diào)函數(shù)列表

        // 函數(shù)列表并沒有通過數(shù)組方式存儲, 而是通過多個對象的next屬性進行依次關聯(lián)

        /** 數(shù)據(jù)格式如:

         * {

         *   tail: {Object},

         *   next: {

         *     callback: {Function},

         *     context: {Object},

         *     next: {

         *       callback: {Function},

         *       context: {Object},

         *       next: {Object}

         *     }

         *   }

         * }

         */

        // 列表每一層next對象存儲了一次回調(diào)事件相關信息(函數(shù)體, 上下文和下一次回調(diào)事件)

        // 事件列表最頂層存儲了一個tail對象, 它存儲了最后一次綁定回調(diào)事件的標識(與最后一次回調(diào)事件的next指向同一個對象)

        // 通過tail標識, 可以在遍歷回調(diào)列表時得知已經(jīng)到達最后一個回調(diào)函數(shù)

        list = calls[event];

        // node變量用于記錄本次回調(diào)函數(shù)的相關信息

        // tail只存儲最后一次綁定回調(diào)函數(shù)的標識

        // 因此如果之前已經(jīng)綁定過回調(diào)函數(shù), 則將之前的tail指定給node作為一個對象使用, 然后創(chuàng)建一個新的對象標識給tail

        // 這里之所以要將本次回調(diào)事件添加到上一次回調(diào)的tail對象, 是為了讓回調(diào)函數(shù)列表的對象層次關系按照綁定順序排列(最新綁定的事件將被放到最底層)

        node = list ? list.tail : {};

        node.next = tail = {};

        // 記錄本次回調(diào)的函數(shù)體及上下文信息

        node.context = context;

        node.callback = callback;

        // 重新組裝當前事件的回調(diào)列表, 列表中已經(jīng)加入了本次回調(diào)事件

        calls[event] = {

          tail : tail,

          next : list ? list.next : node

        };

      }

      // 返回當前對象, 方便進行方法鏈調(diào)用

      return this;

    },

    // 移除對象中已綁定的事件或回調(diào)函數(shù), 可以通過events, callback和context對需要刪除的事件或回調(diào)函數(shù)進行過濾

    // - 如果context為空, 則移除所有的callback指定的函數(shù)

    // - 如果callback為空, 則移除事件中所有的回調(diào)函數(shù)

    // - 如果events為空, 但指定了callback或context, 則移除callback或context指定的回調(diào)函數(shù)(不區(qū)分事件名稱)

    // - 如果沒有傳遞任何參數(shù), 則移除對象中綁定的所有事件和回調(diào)函數(shù)

    off : function(events, callback, context) {

      var event, calls, node, tail, cb, ctx;

 

      // No events, or removing *all* events.

      // 當前對象沒有綁定任何事件

      if(!( calls = this._callbacks))

        return;

      // 如果沒有指定任何參數(shù), 則移除所有事件和回調(diào)函數(shù)(刪除_callbacks屬性)

      if(!(events || callback || context)) {

        delete this._callbacks;

        return this;

      }

 

      // 解析需要移除的事件列表

      // - 如果指定了events, 則按照eventSplitter對事件名進行解析

      // - 如果沒有指定events, 則解析已綁定所有事件的名稱列表

      events = events ? events.split(eventSplitter) : _.keys(calls);

 

      // 循環(huán)事件名列表

      while( event = events.shift()) {

        // 將當前事件對象從列表中移除, 并緩存到node變量中

        node = calls[event];

        delete calls[event];

        // 如果不存在當前事件對象(或沒有指定移除過濾條件, 則認為將移除當前事件及所有回調(diào)函數(shù)), 則終止此次操作(事件對象在上一步已經(jīng)移除)

        if(!node || !(callback || context))

          continue;

        // Create a new list, omitting the indicated callbacks.

        // 根據(jù)回調(diào)函數(shù)或上下文過濾條件, 組裝一個新的事件對象并重新綁定

        tail = node.tail;

        // 遍歷事件中的所有回調(diào)對象

        while(( node = node.next) !== tail) {

          cb = node.callback;

          ctx = node.context;

          // 根據(jù)參數(shù)中的回調(diào)函數(shù)和上下文, 對回調(diào)函數(shù)進行過濾, 將不符合過濾條件的回調(diào)函數(shù)重新綁定到事件中(因為事件中的所有回調(diào)函數(shù)在上面已經(jīng)被移除)

          if((callback && cb !== callback) || (context && ctx !== context)) {

            this.on(event, cb, ctx);

          }

        }

      }

 

      return this;

    },

    // 觸發(fā)已經(jīng)定義的一個或多個事件, 依次執(zhí)行綁定的回調(diào)函數(shù)列表

    trigger : function(events) {

      var event, node, calls, tail, args, all, rest;

      // 當前對象沒有綁定任何事件

      if(!( calls = this._callbacks))

        return this;

      // 獲取回調(diào)函數(shù)列表中綁定的"all"事件列表

      all = calls.all;

      // 將需要觸發(fā)的事件名稱, 按照eventSplitter規(guī)則解析為一個數(shù)組

      events = events.split(eventSplitter);

      // 將trigger從第2個之后的參數(shù), 記錄到rest變量, 將依次傳遞給回調(diào)函數(shù)

      rest = slice.call(arguments, 1);

 

      // 循環(huán)需要觸發(fā)的事件列表

      while( event = events.shift()) {

        // 此處的node變量記錄了當前事件的所有回調(diào)函數(shù)列表

        if( node = calls[event]) {

          // tail變量記錄最后一次綁定事件的對象標識

          tail = node.tail;

          // node變量的值, 按照事件的綁定順序, 被依次賦值為綁定的單個回調(diào)事件對象

          // 最后一次綁定的事件next屬性, 與tail引用同一個對象, 以此作為是否到達列表末尾的判斷依據(jù)

          while(( node = node.next) !== tail) {

            // 執(zhí)行所有綁定的事件, 并將調(diào)用trigger時的參數(shù)傳遞給回調(diào)函數(shù)

            node.callback.apply(node.context || this, rest);

          }

        }

        // 變量all記錄了綁定時的"all"事件, 即在調(diào)用任何事件時, "all"事件中的回調(diào)函數(shù)均會被執(zhí)行

        // - "all"事件中的回調(diào)函數(shù)無論綁定順序如何, 都會在當前事件的回調(diào)函數(shù)列表全部執(zhí)行完畢后再依次執(zhí)行

        // - "all"事件應該在觸發(fā)普通事件時被自動調(diào)用, 如果強制觸發(fā)"all"事件, 事件中的回調(diào)函數(shù)將被執(zhí)行兩次

        if( node = all) {

          tail = node.tail;

          // 與調(diào)用普通事件的回調(diào)函數(shù)不同之處在于, all事件會將當前調(diào)用的事件名作為第一個參數(shù)傳遞給回調(diào)函數(shù)

          args = [event].concat(rest);

          // 遍歷并執(zhí)行"all"事件中的回調(diào)函數(shù)列表

          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 數(shù)據(jù)對象模型

  // --------------

 

  // Model是Backbone中所有數(shù)據(jù)對象模型的基類, 用于創(chuàng)建一個數(shù)據(jù)模型

  // @param {Object} attributes 指定創(chuàng)建模型時的初始化數(shù)據(jù)

  // @param {Object} options

  /**

   * @format options

   * {

   *   parse: {Boolean},

   *   collection: {Collection}

   * }

   */

  var Model = Backbone.Model = function(attributes, options) {

    // defaults變量用于存儲模型的默認數(shù)據(jù)

    var defaults;

    // 如果沒有指定attributes參數(shù), 則設置attributes為空對象

    attributes || ( attributes = {});

    // 設置attributes默認數(shù)據(jù)的解析方法, 例如默認數(shù)據(jù)是從服務器獲取(或原始數(shù)據(jù)是XML格式), 為了兼容set方法所需的數(shù)據(jù)格式, 可使用parse方法進行解析

    if(options && options.parse)

      attributes = this.parse(attributes);

    if( defaults = getValue(this, 'defaults')) {

      // 如果Model在定義時設置了defaults默認數(shù)據(jù), 則初始化數(shù)據(jù)使用defaults與attributes參數(shù)合并后的數(shù)據(jù)(attributes中的數(shù)據(jù)會覆蓋defaults中的同名數(shù)據(jù))

      attributes = _.extend({}, defaults, attributes);

    }

    // 顯式指定模型所屬的Collection對象(在調(diào)用Collection的add, push等將模型添加到集合中的方法時, 會自動設置模型所屬的Collection對象)

    if(options && options.collection)

      this.collection = options.collection;

    // attributes屬性存儲了當前模型的JSON對象化數(shù)據(jù), 創(chuàng)建模型時默認為空

    this.attributes = {};

    // 定義_escapedAttributes緩存對象, 它將緩存通過escape方法處理過的數(shù)據(jù)

    this._escapedAttributes = {};

    // 為每一個模型配置一個唯一標識

    this.cid = _.uniqueId('c');

    // 定義一系列用于記錄數(shù)據(jù)狀態(tài)的對象, 具體含義請參考對象定義時的注釋

    this.changed = {};

    this._silent = {};

    this._pending = {};

    // 創(chuàng)建實例時設置初始化數(shù)據(jù), 首次設置使用silent參數(shù), 不會觸發(fā)change事件

    this.set(attributes, {

      silent : true

    });

    // 上面已經(jīng)設置了初始化數(shù)據(jù), changed, _silent, _pending對象的狀態(tài)可能已經(jīng)發(fā)生變化, 這里重新進行初始化

    this.changed = {};

    this._silent = {};

    this._pending = {};

    // _previousAttributes變量存儲模型數(shù)據(jù)的一個副本

    // 用于在change事件中獲取模型數(shù)據(jù)被改變之前的狀態(tài), 可通過previous或previousAttributes方法獲取上一個狀態(tài)的數(shù)據(jù)

    this._previousAttributes = _.clone(this.attributes);

    // 調(diào)用initialize初始化方法

    this.initialize.apply(this, arguments);

  };

  // 使用extend方法為Model原型定義一系列屬性和方法

  _.extend(Model.prototype, Events, {

 

    // changed屬性記錄了每次調(diào)用set方法時, 被改變數(shù)據(jù)的key集合

    changed : null,

 

    // // 當指定silent屬性時, 不會觸發(fā)change事件, 被改變的數(shù)據(jù)會記錄下來, 直到下一次觸發(fā)change事件

    // _silent屬性用來記錄使用silent時的被改變的數(shù)據(jù)

    _silent : null,

 

    _pending : null,

 

    // 每個模型的唯一標識屬性(默認為"id", 通過修改idAttribute可自定義id屬性名)

    // 如果在設置數(shù)據(jù)時包含了id屬性, 則id將會覆蓋模型的id

    // id用于在Collection集合中查找和標識模型, 與后臺接口通信時也會以id作為一條記錄的標識

    idAttribute : 'id',

 

    // 模型初始化方法, 在模型被構造結束后自動調(diào)用

    initialize : function() {

    },

    // 返回當前模型中數(shù)據(jù)的一個副本(JSON對象格式)

    toJSON : function(options) {

      return _.clone(this.attributes);

    },

    // 根據(jù)attr屬性名, 獲取模型中的數(shù)據(jù)值

    get : function(attr) {

      return this.attributes[attr];

    },

    // 根據(jù)attr屬性名, 獲取模型中的數(shù)據(jù)值, 數(shù)據(jù)值包含的HTML特殊字符將被轉換為HTML實體, 包含 & < > " ' \

    // 通過 _.escape方法實現(xiàn)

    escape : function(attr) {

      var html;

      // 從_escapedAttributes緩存對象中查找數(shù)據(jù), 如果數(shù)據(jù)已經(jīng)被緩存則直接返回

      if( html = this._escapedAttributes[attr])

        return html;

      // _escapedAttributes緩存對象中沒有找到數(shù)據(jù)

      // 則先從模型中獲取數(shù)據(jù)

      var val = this.get(attr);

      // 將數(shù)據(jù)中的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;

    },

    // 設置模型中的數(shù)據(jù), 如果key值不存在, 則作為新的屬性添加到模型, 如果key值已經(jīng)存在, 則修改為新的值

    set : function(key, value, options) {

      // attrs變量中記錄需要設置的數(shù)據(jù)對象

      var attrs, attr, val;

 

      // 參數(shù)形式允許key-value對象形式, 或通過key, value兩個參數(shù)進行單獨設置

      // 如果key是一個對象, 則認定為使用對象形式設置, 第二個參數(shù)將被視為options參數(shù)

      if(_.isObject(key) || key == null) {

        attrs = key;

        options = value;

      } else {

        // 通過key, value兩個參數(shù)單獨設置, 將數(shù)據(jù)放到attrs對象中方便統(tǒng)一處理

        attrs = {};

        attrs[key] = value;

      }

 

      // options配置項必須是一個對象, 如果沒有設置options則默認值為一個空對象

      options || ( options = {});

      // 沒有設置參數(shù)時不執(zhí)行任何動作

      if(!attrs)

        return this;

      // 如果被設置的數(shù)據(jù)對象屬于Model類的一個實例, 則將Model對象的attributes數(shù)據(jù)對象賦給attrs

      // 一般在復制一個Model對象的數(shù)據(jù)到另一個Model對象時, 會執(zhí)行該動作

      if( attrs instanceof Model)

        attrs = attrs.attributes;

      // 如果options配置對象中設置了unset屬性, 則將attrs數(shù)據(jù)對象中的所有屬性重置為undefined

      // 一般在復制一個Model對象的數(shù)據(jù)到另一個Model對象時, 但僅僅需要復制Model中的數(shù)據(jù)而不需要復制值時執(zhí)行該操作

      if(options.unset)

        for(attr in attrs)

        attrs[attr] =

        void 0;

 

      // 對當前數(shù)據(jù)進行驗證, 如果驗證未通過則停止執(zhí)行

      if(!this._validate(attrs, options))

        return false;

 

      // 如果設置的id屬性名被包含在數(shù)據(jù)集合中, 則將id覆蓋到模型的id屬性

      // 這是為了確保在自定義id屬性名后, 訪問模型的id屬性時, 也能正確訪問到id

      if(this.idAttribute in attrs)

        this.id = attrs[this.idAttribute];

 

      var changes = options.changes = {};

      // now記錄當前模型中的數(shù)據(jù)對象

      var now = this.attributes;

      // escaped記錄當前模型中通過escape緩存過的數(shù)據(jù)

      var escaped = this._escapedAttributes;

      // prev記錄模型中數(shù)據(jù)被改變之前的值

      var prev = this._previousAttributes || {};

 

      // 遍歷需要設置的數(shù)據(jù)對象

      for(attr in attrs) {

        // attr存儲當前屬性名稱, val存儲當前屬性的值

        val = attrs[attr];

 

        // 如果當前數(shù)據(jù)在模型中不存在, 或已經(jīng)發(fā)生變化, 或在options中指定了unset屬性刪除, 則刪除該數(shù)據(jù)被換存在_escapedAttributes中的數(shù)據(jù)

        if(!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) {

          // 僅刪除通過escape緩存過的數(shù)據(jù), 這是為了保證緩存中的數(shù)據(jù)與模型中的真實數(shù)據(jù)保持同步

          delete escaped[attr];

          // 如果指定了silent屬性, 則此次set方法調(diào)用不會觸發(fā)change事件, 因此將被改變的數(shù)據(jù)記錄到_silent屬性中, 便于下一次觸發(fā)change事件時, 通知事件監(jiān)聽函數(shù)此數(shù)據(jù)已經(jīng)改變

          // 如果沒有指定silent屬性, 則直接設置changes屬性中當前數(shù)據(jù)為已改變狀態(tài)

          (options.silent ? this._silent : changes)[attr] = true;

        }

 

        // 如果在options中設置了unset, 則從模型中刪除該數(shù)據(jù)(包括key)

        // 如果沒有指定unset屬性, 則認為將新增或修改數(shù)據(jù), 向模型的數(shù)據(jù)對象中加入新的數(shù)據(jù)

        options.unset ?

        delete now[attr] : now[attr] = val;

 

        // 如果模型中的數(shù)據(jù)與新的數(shù)據(jù)不一致, 則表示該數(shù)據(jù)已發(fā)生變化

        if(!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {

          // 在changed屬性中記錄當前屬性已經(jīng)發(fā)生變化的狀態(tài)

          this.changed[attr] = val;

          if(!options.silent)

            this._pending[attr] = true;

        } else {

          // 如果數(shù)據(jù)沒有發(fā)生變化, 則從changed屬性中移除已變化狀態(tài)

          delete this.changed[attr];

          delete this._pending[attr];

        }

      }

 

      // 調(diào)用change方法, 將觸發(fā)change事件綁定的函數(shù)

      if(!options.silent)

        this.change(options);

      return this;

    },

    // 從當前模型中刪除指定的數(shù)據(jù)(屬性也將被同時刪除)

    unset : function(attr, options) {

      (options || ( options = {})).unset = true;

      // 通過options.unset配置項告知set方法進行刪除操作

      return this.set(attr, null, options);

    },

    // 清除當前模型中的所有數(shù)據(jù)和屬性

    clear : function(options) {

      (options || ( options = {})).unset = true;

      // 克隆一個當前模型的屬性副本, 并通過options.unset配置項告知set方法執(zhí)行刪除操作

      return this.set(_.clone(this.attributes), options);

    },

    // 從服務器獲取默認的模型數(shù)據(jù), 獲取數(shù)據(jù)后使用set方法將數(shù)據(jù)填充到模型, 因此如果獲取到的數(shù)據(jù)與當前模型中的數(shù)據(jù)不一致, 將會觸發(fā)change事件

    fetch : function(options) {

      // 確保options是一個新的對象, 隨后將改變options中的屬性

      options = options ? _.clone(options) : {};

      var model = this;

      // 在options中可以指定獲取數(shù)據(jù)成功后的自定義回調(diào)函數(shù)

      var success = options.success;

      // 當獲取數(shù)據(jù)成功后填充數(shù)據(jù)并調(diào)用自定義成功回調(diào)函數(shù)

      options.success = function(resp, status, xhr) {

        // 通過parse方法將服務器返回的數(shù)據(jù)進行轉換

        // 通過set方法將轉換后的數(shù)據(jù)填充到模型中, 因此可能會觸發(fā)change事件(當數(shù)據(jù)發(fā)生變化時)

        // 如果填充數(shù)據(jù)時驗證失敗, 則不會調(diào)用自定義success回調(diào)函數(shù)

        if(!model.set(model.parse(resp, xhr), options))

          return false;

        // 調(diào)用自定義的success回調(diào)函數(shù)

        if(success)

          success(model, resp);

      };

      // 請求發(fā)生錯誤時通過wrapError處理error事件

      options.error = Backbone.wrapError(options.error, model, options);

      // 調(diào)用sync方法從服務器獲取數(shù)據(jù)

      return (this.sync || Backbone.sync).call(this, 'read', this, options);

    },

    // 保存模型中的數(shù)據(jù)到服務器

    save : function(key, value, options) {

      // attrs存儲需要保存到服務器的數(shù)據(jù)對象

      var attrs, current;

 

      // 支持設置單個屬性的方式 key: value

      // 支持對象形式的批量設置方式 {key: value}

      if(_.isObject(key) || key == null) {

        // 如果key是一個對象, 則認為是通過對象方式設置

        // 此時第二個參數(shù)被認為是options

        attrs = key;

        options = value;

      } else {

        // 如果是通過key: value形式設置單個屬性, 則直接設置attrs

        attrs = {};

        attrs[key] = value;

      }

      // 配置對象必須是一個新的對象

      options = options ? _.clone(options) : {};

 

      // 如果在options中設置了wait選項, 則被改變的數(shù)據(jù)將會被提前驗證, 且服務器沒有響應新數(shù)據(jù)(或響應失敗)時, 本地數(shù)據(jù)會被還原為修改前的狀態(tài)

      // 如果沒有設置wait選項, 則無論服務器是否設置成功, 本地數(shù)據(jù)均會被修改為最新狀態(tài)

      if(options.wait) {

        // 對需要保存的數(shù)據(jù)提前進行驗證

        if(!this._validate(attrs, options))

          return false;

        // 記錄當前模型中的數(shù)據(jù), 用于在將數(shù)據(jù)發(fā)送到服務器后, 將數(shù)據(jù)進行還原

        // 如果服務器響應失敗或沒有返回數(shù)據(jù), 則可以保持修改前的狀態(tài)

        current = _.clone(this.attributes);

      }

 

      // silentOptions在options對象中加入了silent(不對數(shù)據(jù)進行驗證)

      // 當使用wait參數(shù)時使用silentOptions配置項, 因為在上面已經(jīng)對數(shù)據(jù)進行過驗證

      // 如果沒有設置wait參數(shù), 則仍然使用原始的options配置項

      var silentOptions = _.extend({}, options, {

        silent : true

      });

      // 將修改過最新的數(shù)據(jù)保存到模型中, 便于在sync方法中獲取模型數(shù)據(jù)保存到服務器

      if(attrs && !this.set(attrs, options.wait ? silentOptions : options)) {

        return false;

      }

 

      var model = this;

      // 在options中可以指定保存數(shù)據(jù)成功后的自定義回調(diào)函數(shù)

      var success = options.success;

      // 服務器響應成功后執(zhí)行success

      options.success = function(resp, status, xhr) {

        // 獲取服務器響應最新狀態(tài)的數(shù)據(jù)

        var serverAttrs = model.parse(resp, xhr);

        // 如果使用了wait參數(shù), 則優(yōu)先將修改后的數(shù)據(jù)狀態(tài)直接設置到模型

        if(options.wait) {

          delete options.wait;

          serverAttrs = _.extend(attrs || {}, serverAttrs);

        }

        // 將最新的數(shù)據(jù)狀態(tài)設置到模型中

        // 如果調(diào)用set方法時驗證失敗, 則不會調(diào)用自定義的success回調(diào)函數(shù)

        if(!model.set(serverAttrs, options))

          return false;

        if(success) {

          // 調(diào)用響應成功后自定義的success回調(diào)函數(shù)

          success(model, resp);

        } else {

          // 如果沒有指定自定義回調(diào), 則默認觸發(fā)sync事件

          model.trigger('sync', model, resp, options);

        }

      };

      // 請求發(fā)生錯誤時通過wrapError處理error事件

      options.error = Backbone.wrapError(options.error, model, options);

      // 將模型中的數(shù)據(jù)保存到服務器

      // 如果當前模型是一個新建的模型(沒有id), 則使用create方法(新增), 否則認為是update方法(修改)

      var method = this.isNew() ? 'create' : 'update';

      var xhr = (this.sync || Backbone.sync).call(this, method, this, options);

      // 如果設置了options.wait, 則將數(shù)據(jù)還原為修改前的狀態(tài)

      // 此時保存的請求還沒有得到響應, 因此如果響應失敗, 模型中將保持修改前的狀態(tài), 如果服務器響應成功, 則會在success中設置模型中的數(shù)據(jù)為最新狀態(tài)

      if(options.wait)

        this.set(current, silentOptions);

      return xhr;

    },

    // 刪除模型, 模型將同時從所屬的Collection集合中被刪除

    // 如果模型是在客戶端新建的, 則直接從客戶端刪除

    // 如果模型數(shù)據(jù)同時存在服務器, 則同時會刪除服務器端的數(shù)據(jù)

    destroy : function(options) {

      // 配置項必須是一個新的對象

      options = options ? _.clone(options) : {};

      var model = this;

      // 在options中可以指定刪除數(shù)據(jù)成功后的自定義回調(diào)函數(shù)

      var success = options.success;

      // 刪除數(shù)據(jù)成功調(diào)用, 觸發(fā)destroy事件, 如果模型存在于Collection集合中, 集合將監(jiān)聽destroy事件并在觸發(fā)時從集合中移除該模型

      // 刪除模型時, 模型中的數(shù)據(jù)并沒有被清空, 但模型已經(jīng)從集合中移除, 因此當沒有任何地方引用該模型時, 會被自動從內(nèi)存中釋放

      // 建議在刪除模型時, 將模型對象的引用變量設置為null

      var triggerDestroy = function() {

        model.trigger('destroy', model, model.collection, options);

      };

      // 如果該模型是一個客戶端新建的模型, 則直接調(diào)用triggerDestroy從集合中將模型移除

      if(this.isNew()) {

        triggerDestroy();

        return false;

      }

 

      // 當從服務器刪除數(shù)據(jù)成功時

      options.success = function(resp) {

        // 如果在options對象中配置wait項, 則表示本地內(nèi)存中的模型數(shù)據(jù), 會在服務器數(shù)據(jù)被刪除成功后再刪除

        // 如果服務器響應失敗, 則本地數(shù)據(jù)不會被刪除

        if(options.wait)

          triggerDestroy();

        if(success) {

          // 調(diào)用自定義的成功回調(diào)函數(shù)

          success(model, resp);

        } else {

          // 如果沒有自定義回調(diào), 則默認觸發(fā)sync事件

          model.trigger('sync', model, resp, options);

        }

      };

      // 請求發(fā)生錯誤時通過wrapError處理error事件

      options.error = Backbone.wrapError(options.error, model, options);

      // 通過sync方法發(fā)送刪除數(shù)據(jù)的請求

      var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options);

      // 如果沒有在options對象中配置wait項, 則會先刪除本地數(shù)據(jù), 再發(fā)送請求刪除服務器數(shù)據(jù)

      // 此時無論服務器刪除是否成功, 本地模型數(shù)據(jù)已被刪除

      if(!options.wait)

        triggerDestroy();

      return xhr;

    },

    // 獲取模型在服務器接口中對應的url, 在調(diào)用save, fetch, destroy等與服務器交互的方法時, 將使用該方法獲取url

    // 生成的url類似于"PATHINFO"模式, 服務器對模型的操作只有一個url, 對于修改和刪除操作會在url后追加模型id便于標識

    // 如果在模型中定義了urlRoot, 服務器接口應為[urlRoot/id]形式

    // 如果模型所屬的Collection集合定義了url方法或屬性, 則使用集合中的url形式: [collection.url/id]

    // 在訪問服務器url時會在url后面追加上模型的id, 便于服務器標識一條記錄, 因此模型中的id需要與服務器記錄對應

    // 如果無法獲取模型或集合的url, 將調(diào)用urlError方法拋出一個異常

    // 如果服務器接口并沒有按照"PATHINFO"方式進行組織, 可以通過重載url方法實現(xiàn)與服務器的無縫交互

    url : function() {

      // 定義服務器對應的url路徑

      var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError();

      // 如果當前模型是客戶端新建的模型, 則不存在id屬性, 服務器url直接使用base

      if(this.isNew())

        return base;

      // 如果當前模型具有id屬性, 可能是調(diào)用了save或destroy方法, 將在base后面追加模型的id

      // 下面將判斷base最后一個字符是否是"/", 生成的url格式為[base/id]

      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);

    },

    // parse方法用于解析從服務器獲取的數(shù)據(jù), 返回一個能夠被set方法解析的模型數(shù)據(jù)

    // 一般parse方法會根據(jù)服務器返回的數(shù)據(jù)進行重載, 以便構建與服務器的無縫連接

    // 當服務器返回的數(shù)據(jù)結構與set方法所需的數(shù)據(jù)結構不一致(例如服務器返回XML格式數(shù)據(jù)時), 可使用parse方法進行轉換

    parse : function(resp, xhr) {

      return resp;

    },

    // 創(chuàng)建一個新的模型, 它具有和當前模型相同的數(shù)據(jù)

    clone : function() {

      return new this.constructor(this.attributes);

    },

    // 檢查當前模型是否是客戶端創(chuàng)建的新模型

    // 檢查方式是根據(jù)模型是否存在id標識, 客戶端創(chuàng)建的新模型沒有id標識

    // 因此服務器響應的模型數(shù)據(jù)中必須包含id標識, 標識的屬性名默認為"id", 也可以通過修改idAttribute屬性自定義標識

    isNew : function() {

      return this.id == null;

    },

    // 數(shù)據(jù)被更新時觸發(fā)change事件綁定的函數(shù)

    // 當set方法被調(diào)用, 會自動調(diào)用change方法, 如果在set方法被調(diào)用時指定了silent配置, 則需要手動調(diào)用change方法

    change : function(options) {

      // options必須是一個對象

      options || ( options = {});

      // this._changing相關的邏輯有些問題

      // this._changing在方法最后被設置為false, 因此方法上面changing變量的值始終為false(第一次為undefined)

      // 作者的初衷應該是想用該變量標示change方法是否執(zhí)行完畢, 對于瀏覽器端單線程的腳本來說沒有意義, 因為該方法被執(zhí)行時會阻塞其它腳本

      // changing獲取上一次執(zhí)行的狀態(tài), 如果上一次腳本沒有執(zhí)行完畢, 則值為true

      var changing = this._changing;

      // 開始執(zhí)行標識, 執(zhí)行過程中值始終為true, 執(zhí)行完畢后this._changing被修改為false

      this._changing = true;

 

      // 將非本次改變的數(shù)據(jù)狀態(tài)添加到_pending對象中

      for(var attr in this._silent)

      this._pending[attr] = true;

 

      // changes對象包含了當前數(shù)據(jù)上一次執(zhí)行change事件至今, 已被改變的所有數(shù)據(jù)

      // 如果之前使用silent未觸發(fā)change事件, 則本次會被放到changes對象中

      var changes = _.extend({}, options.changes, this._silent);

      // 重置_silent對象

      this._silent = {};

      // 遍歷changes對象, 分別針對每一個屬性觸發(fā)單獨的change事件

      for(var attr in changes) {

        // 將Model對象, 屬性值, 配置項作為參數(shù)以此傳遞給事件的監(jiān)聽函數(shù)

        this.trigger('change:' + attr, this, this.get(attr), options);

      }

 

      // 如果方法處于執(zhí)行中, 則停止執(zhí)行

      if(changing)

        return this;

 

      // 觸發(fā)change事件, 任意數(shù)據(jù)被改變后, 都會依次觸發(fā)"change:屬性"事件和"change"事件

      while(!_.isEmpty(this._pending)) {

        this._pending = {};

        // 觸發(fā)change事件, 并將Model實例和配置項作為參數(shù)傳遞給監(jiān)聽函數(shù)

        this.trigger('change', this, options);

        // 遍歷changed對象中的數(shù)據(jù), 并依次將已改變數(shù)據(jù)的狀態(tài)從changed中移除

        // 在此之后如果調(diào)用hasChanged檢查數(shù)據(jù)狀態(tài), 將得到false(未改變)

        for(var attr in this.changed) {

          if(this._pending[attr] || this._silent[attr])

            continue;

          // 移除changed中數(shù)據(jù)的狀態(tài)

          delete this.changed[attr];

        }

        // change事件執(zhí)行完畢, _previousAttributes屬性將記錄當前模型最新的數(shù)據(jù)副本

        // 因此如果需要獲取數(shù)據(jù)的上一個狀態(tài), 一般只通過在觸發(fā)的change事件中通過previous或previousAttributes方法獲取

        this._previousAttributes = _.clone(this.attributes);

      }

 

      // 執(zhí)行完畢標識

      this._changing = false;

      return this;

    },

    // 檢查某個數(shù)據(jù)是否在上一次執(zhí)行change事件后被改變過

    /**

     * 一般在change事件中配合previous或previousAttributes方法使用, 如:

     * if(model.hasChanged('attr')) {

     *   var attrPrev = model.previous('attr');

     * }

     */

    hasChanged : function(attr) {

      if(!arguments.length)

        return !_.isEmpty(this.changed);

      return _.has(this.changed, attr);

    },

    // 獲取當前模型中的數(shù)據(jù)與上一次數(shù)據(jù)中已經(jīng)發(fā)生變化的數(shù)據(jù)集合

    // (一般在使用silent屬性時沒有調(diào)用change方法, 因此數(shù)據(jù)會被臨時抱存在changed屬性中, 上一次的數(shù)據(jù)可通過previousAttributes方法獲取)

    // 如果傳遞了diff集合, 將使用上一次模型數(shù)據(jù)與diff集合中的數(shù)據(jù)進行比較, 返回不一致的數(shù)據(jù)集合

    // 如果比較結果中沒有差異, 則返回false

    changedAttributes : function(diff) {

      // 如果沒有指定diff, 將返回當前模型較上一次狀態(tài)已改變的數(shù)據(jù)集合, 這些數(shù)據(jù)已經(jīng)被存在changed屬性中, 因此返回changed集合的一個副本

      if(!diff)

        return this.hasChanged() ? _.clone(this.changed) : false;

      // 指定了需要進行比較的diff集合, 將返回上一次的數(shù)據(jù)與diff集合的比較結果

      // old變量存儲了上一個狀態(tài)的模型數(shù)據(jù)

      var val, changed = false, old = this._previousAttributes;

      // 遍歷diff集合, 并將每一項與上一個狀態(tài)的集合進行比較

      for(var attr in diff) {

        // 將比較結果不一致的數(shù)據(jù)臨時存儲到changed變量

        if(_.isEqual(old[attr], ( val = diff[attr])))

          continue;

        (changed || (changed = {}))[attr] = val;

      }

      // 返回比較結果

      return changed;

    },

    // 在模型觸發(fā)的change事件中, 獲取某個屬性被改變前上一個狀態(tài)的數(shù)據(jù), 一般用于進行數(shù)據(jù)比較或回滾

    // 該方法一般在change事件中調(diào)用, change事件被觸發(fā)后, _previousAttributes屬性存放最新的數(shù)據(jù)

    previous : function(attr) {

      // attr指定需要獲取上一個狀態(tài)的屬性名稱

      if(!arguments.length || !this._previousAttributes)

        return null;

      return this._previousAttributes[attr];

    },

    // 在模型觸發(fā)change事件中, 獲取所有屬性上一個狀態(tài)的數(shù)據(jù)集合

    // 該方法類似于previous()方法, 一般在change事件中調(diào)用, 用于數(shù)據(jù)比較或回滾

    previousAttributes : function() {

      // 將上一個狀態(tài)的數(shù)據(jù)對象克隆為一個新對象并返回

      return _.clone(this._previousAttributes);

    },

    // Check if the model is currently in a valid state. It's only possible to

    // get into an *invalid* state if you're using silent changes.

    // 驗證當前模型中的數(shù)據(jù)是否能通過validate方法驗證, 調(diào)用前請確保定義了validate方法

    isValid : function() {

      return !this.validate(this.attributes);

    },

    // 數(shù)據(jù)驗證方法, 在調(diào)用set, save, add等數(shù)據(jù)更新方法時, 被自動執(zhí)行

    // 驗證失敗會觸發(fā)模型對象的"error"事件, 如果在options中指定了error處理函數(shù), 則只會執(zhí)行options.error函數(shù)

    // @param {Object} attrs 數(shù)據(jù)模型的attributes屬性, 存儲模型的對象化數(shù)據(jù)

    // @param {Object} options 配置項

    // @return {Boolean} 驗證通過返回true, 不通過返回false

    _validate : function(attrs, options) {

      // 如果在調(diào)用set, save, add等數(shù)據(jù)更新方法時設置了options.silent屬性, 則忽略驗證

      // 如果Model中沒有添加validate方法, 則忽略驗證

      if(options.silent || !this.validate)

        return true;

      // 獲取對象中所有的屬性值, 并放入validate方法中進行驗證

      // validate方法包含2個參數(shù), 分別為模型中的數(shù)據(jù)集合與配置對象, 如果驗證通過則不返回任何數(shù)據(jù)(默認為undefined), 驗證失敗則返回帶有錯誤信息數(shù)據(jù)

      attrs = _.extend({}, this.attributes, attrs);

      var error = this.validate(attrs, options);

      // 驗證通過

      if(!error)

        return true;

      // 驗證未通過

      // 如果配置對象中設置了error錯誤處理方法, 則調(diào)用該方法并將錯誤數(shù)據(jù)和配置對象傳遞給該方法

      if(options && options.error) {

        options.error(this, error, options);

      } else {

        // 如果對模型綁定了error事件監(jiān)聽, 則觸發(fā)綁定事件

        this.trigger('error', this, error, options);

      }

      // 返回驗證未通過標識

      return false;

    }

  });

 

  // Backbone.Collection 數(shù)據(jù)模型集合相關

  // -------------------

 

  // Collection集合存儲一系列相同類的數(shù)據(jù)模型, 并提供相關方法對模型進行操作

  var Collection = Backbone.Collection = function(models, options) {

    // 配置對象

    options || ( options = {});

    // 在配置參數(shù)中設置集合的模型類

    if(options.model)

      this.model = options.model;

    // 如果設置了comparator屬性, 則集合中的數(shù)據(jù)將按照comparator方法中的排序算法進行排序(在add方法中會自動調(diào)用)

    if(options.comparator)

      this.comparator = options.comparator;

    // 實例化時重置集合的內(nèi)部狀態(tài)(第一次調(diào)用時可理解為定義狀態(tài))

    this._reset();

    // 調(diào)用自定義初始化方法, 如果需要一般會重載initialize方法

    this.initialize.apply(this, arguments);

    // 如果指定了models數(shù)據(jù), 則調(diào)用reset方法將數(shù)據(jù)添加到集合中

    // 首次調(diào)用時設置了silent參數(shù), 因此不會觸發(fā)"reset"事件

    if(models)

      this.reset(models, {

        silent : true,

        parse : options.parse

      });

  };

  // 通過extend方法定義集合類原型方法

  _.extend(Collection.prototype, Events, {

 

    // 定義集合的模型類, 模型類必須是一個Backbone.Model的子類

    // 在使用集合相關方法(如add, create等)時, 允許傳入數(shù)據(jù)對象, 集合方法會根據(jù)定義的模型類自動創(chuàng)建對應的實例

    // 集合中存儲的數(shù)據(jù)模型應該都是同一個模型類的實例

    model : Model,

 

    // 初始化方法, 該方法在集合實例被創(chuàng)建后自動調(diào)用

    // 一般會在定義集合類時重載該方法

    initialize : function() {

    },

    // 返回一個數(shù)組, 包含了集合中每個模型的數(shù)據(jù)對象

    toJSON : function(options) {

      // 通過Undersocre的map方法將集合中每一個模型的toJSON結果組成一個數(shù)組, 并返回

      return this.map(function(model) {

        // 依次調(diào)用每個模型對象的toJSON方法, 該方法默認將返回模型的數(shù)據(jù)對象(復制的副本)

        // 如果需要返回字符串等其它形式, 可以重載toJSON方法

        return model.toJSON(options);

      });

    },

    // 向集合中添加一個或多個模型對象

    // 默認會觸發(fā)"add"事件, 如果在options中設置了silent屬性, 可以關閉此次事件觸發(fā)

    // 傳入的models可以是一個或一系列的模型對象(Model類的實例), 如果在集合中設置了model屬性, 則允許直接傳入數(shù)據(jù)對象(如 {name: 'test'}), 將自動將數(shù)據(jù)對象實例化為model指向的模型對象

    add : function(models, options) {

      // 局部變量定義

      var i, index, length, model, cid, id, cids = {}, ids = {}, dups = [];

      options || ( options = {});

      // models必須是一個數(shù)組, 如果只傳入了一個模型, 則將其轉換為數(shù)組

      models = _.isArray(models) ? models.slice() : [models];

 

      // 遍歷需要添加的模型列表, 遍歷過程中, 將執(zhí)行以下操作:

      // - 將數(shù)據(jù)對象轉化模型對象

      // - 建立模型與集合之間的引用

      // - 記錄無效和重復的模型, 并在后面進行過濾

      for( i = 0, length = models.length; i < length; i++) {

        // 將數(shù)據(jù)對象轉換為模型對象, 簡歷模型與集合的引用, 并存儲到model(同時models中對應的模型已經(jīng)被替換為模型對象)

        if(!( model = models[i] = this._prepareModel(models[i], options))) {

          throw new Error("Can't add an invalid model to a collection");

        }

        // 當前模型的cid和id

        cid = model.cid;

        id = model.id;

        // dups數(shù)組中記錄了無效或重復的模型索引(models數(shù)組中的索引), 并在下一步進行過濾刪除

        // 如果cids, ids變量中已經(jīng)存在了該模型的索引, 則認為是同一個模型在傳入的models數(shù)組中聲明了多次

        // 如果_byCid, _byId對象中已經(jīng)存在了該模型的索引, 則認為同一個模型在當前集合中已經(jīng)存在

        // 對于上述兩種情況, 將模型的索引記錄到dups進行過濾刪除

        if(cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) {

          dups.push(i);

          continue;

        }

        // 將models中已經(jīng)遍歷過的模型記錄下來, 用于在下一次循環(huán)時進行重復檢查

        cids[cid] = ids[id] = model;

      }

 

      // 從models中刪除無效或重復的模型, 保留目前集合中真正需要添加的模型列表

      i = dups.length;

      while(i--) {

        models.splice(dups[i], 1);

      }

 

      // 遍歷需要添加的模型, 監(jiān)聽模型事件并記錄_byCid, _byId列表, 用于在調(diào)用get和getByCid方法時作為索引

      for( i = 0, length = models.length; i < length; i++) {

        // 監(jiān)聽模型中的所有事件, 并執(zhí)行_onModelEvent方法

        // _onModelEvent方法中會對模型拋出的add, remove, destroy和change事件進行處理, 以便模型與集合中的狀態(tài)保持同步

        ( model = models[i]).on('all', this._onModelEvent, this);

        // 將模型根據(jù)cid記錄到_byCid對象, 便于根據(jù)cid進行查找

        this._byCid[model.cid] = model;

        // 將模型根據(jù)id記錄到_byId對象, 便于根據(jù)id進行查找

        if(model.id != null)

          this._byId[model.id] = model;

      }

 

      // 改變集合的length屬性, length屬性記錄了當前集合中模型的數(shù)量

      this.length += length;

      // 設置新模型列表插入到集合中的位置, 如果在options中設置了at參數(shù), 則在集合的at位置插入

      // 默認將插入到集合的末尾

      // 如果設置了comparator自定義排序方法, 則設置at后還將按照comparator中的方法進行排序, 因此最終的順序可能并非在at指定的位置

      index = options.at != null ? options.at : this.models.length;

      splice.apply(this.models, [index, 0].concat(models));

      // 如果設置了comparator方法, 則將數(shù)據(jù)按照comparator中的算法進行排序

      // 自動排序使用silent屬性阻止觸發(fā)reset事件

      if(this.comparator)

        this.sort({

          silent : true

        });

      // 依次對每個模型對象觸發(fā)"add"事件, 如果設置了silent屬性, 則阻止事件觸發(fā)

      if(options.silent)

        return this;

      // 遍歷新增加的模型列表

      for( i = 0, length = this.models.length; i < length; i++) {

        if(!cids[( model = this.models[i]).cid])

          continue;

        options.index = i;

        // 觸發(fā)模型的"add"事件, 因為集合監(jiān)聽了模型的"all"事件, 因此在_onModelEvent方法中, 集合也將觸發(fā)"add"事件

        // 詳細信息可參考Collection.prototype._onModelEvent方法

        model.trigger('add', model, this, options);

      }

      return this;

    },

    // 從集合中移除模型對象(支持移除多個模型)

    // 傳入的models可以是需要移除的模型對象, 或模型的cid和模型的id

    // 移除模型并不會調(diào)用模型的destroy方法

    // 如果沒有設置options.silent參數(shù), 將觸發(fā)模型的remove事件, 同時將觸發(fā)集合的remove事件(集合通過_onModelEvent方法監(jiān)聽了模型的所有事件)

    remove : function(models, options) {

      var i, l, index, model;

      // options默認為空對象

      options || ( options = {});

      // models必須是數(shù)組類型, 當只移除一個模型時, 將其放入一個數(shù)組

      models = _.isArray(models) ? models.slice() : [models];

      // 遍歷需要移除的模型列表

      for( i = 0, l = models.length; i < l; i++) {

        // 所傳入的models列表中可以是需要移除的模型對象, 或模型的cid和模型的id

        // (在getByCid和get方法中, 可通過cid, id來獲取模型, 如果傳入的是一個模型對象, 則返回模型本身)

        model = this.getByCid(models[i]) || this.get(models[i]);

        // 沒有獲取到模型

        if(!model)

          continue;

        // 從_byId列表中移除模型的id引用

        delete this._byId[model.id];

        // 從_byCid列表中移除模型的cid引用

        delete this._byCid[model.cid];

        // indexOf是Underscore對象中的方法, 這里通過indexOf方法獲取模型在集合中首次出現(xiàn)的位置

        index = this.indexOf(model);

        // 從集合列表中移除該模型

        this.models.splice(index, 1);

        // 重置當前集合的length屬性(記錄集合中模型的數(shù)量)

        this.length--;

        // 如果沒有設置silent屬性, 則觸發(fā)模型的remove事件

        if(!options.silent) {

          // 將當前模型在集合中的位置添加到options對象并傳遞給remove監(jiān)聽事件, 以便在事件函數(shù)中可以使用

          options.index = index;

          model.trigger('remove', model, this, options);

        }

        // 解除模型與集合的關系, 包括集合中對模型的引用和事件監(jiān)聽

        this._removeReference(model);

      }

      return this;

    },

    // 向集合的末尾添加模型對象

    // 如果集合類中定義了comparator排序方法, 則通過push方法添加的模型將按照comparator定義的算法進行排序, 因此模型順序可能會被改變

    push : function(model, options) {

      // 通過_prepareModel方法將model實例化為模型對象, 這句代碼是多余的, 因為在下面調(diào)用的add方法中還會通過_prepareModel獲取一次模型

      model = this._prepareModel(model, options);

      // 調(diào)用add方法將模型添加到集合中(默認添加到集合末尾)

      this.add(model, options);

      return model;

    },

    // 移除集合中最后一個模型對象

    pop : function(options) {

      // 獲取集合中最后一個模型

      var model = this.at(this.length - 1);

      // 通過remove方法移除該模型

      this.remove(model, options);

      return model;

    },

    // 向集合的第一個位置插入模型

    // 如果集合類中定義了comparator排序方法, 則通過unshift方法添加的模型將按照comparator定義的算法進行排序, 因此模型順序可能會被改變

    unshift : function(model, options) {

      // 通過_prepareModel方法將model實例化為模型對象

      model = this._prepareModel(model, options);

      // 調(diào)用add方法將模型插入到集合的第一個位置(設置at為0)

      // 如果定義了comparator排序方法, 集合的順序將被重排

      this.add(model, _.extend({

        at : 0

      }, options));

      return model;

    },

    // 移除并返回集合中的第一個模型對象

    shift : function(options) {

      // 獲得集合中的第一個模型

      var model = this.at(0);

      // 從集合中刪除該模型

      this.remove(model, options);

      // 返回模型對象

      return model;

    },

    // 根據(jù)id從集合中查找模型并返回

    get : function(id) {

      if(id == null)

        return

        void 0;

      return this._byId[id.id != null ? id.id : id];

    },

    // 根據(jù)cid從集合中查找模型并返回

    getByCid : function(cid) {

      return cid && this._byCid[cid.cid || cid];

    },

    // 根據(jù)索引(下標, 從0開始)從集合中查找模型并返回

    at : function(index) {

      return this.models[index];

    },

    // 對集合中的模型根據(jù)值進行篩選

    // attrs是一個篩選對象, 如 {name: 'Jack'}, 將返回集合中所有name為"Jack"的模型(數(shù)組)

    where : function(attrs) {

      // attrs不能為空值

      if(_.isEmpty(attrs))

        return [];

      // 通過filter方法對集合中的模型進行篩選

      // filter方法是Underscore中的方法, 用于將遍歷集合中的元素, 并將能通過處理器驗證(返回值為true)的元素作為數(shù)組返回

      return this.filter(function(model) {

        // 遍歷attrs對象中的驗證規(guī)則

        for(var key in attrs) {

          // 將attrs中的驗證規(guī)則與集合中的模型進行匹配

          if(attrs[key] !== model.get(key))

            return false;

        }

        return true;

      });

    },

    // 對集合中的模型按照comparator屬性指定的方法進行排序

    // 如果沒有在options中設置silent參數(shù), 則排序后將觸發(fā)reset事件

    sort : function(options) {

      // options默認是一個對象

      options || ( options = {});

      // 調(diào)用sort方法必須指定了comparator屬性(排序算法方法), 否則將拋出一個錯誤

      if(!this.comparator)

        throw new Error('Cannot sort a set without a comparator');

      // boundComparator存儲了綁定當前集合上下文對象的comparator排序算法方法

      var boundComparator = _.bind(this.comparator, this);

      if(this.comparator.length == 1) {

        this.models = this.sortBy(boundComparator);

      } else {

        // 調(diào)用Array.prototype.sort通過comparator算法對數(shù)據(jù)進行自定義排序

        this.models.sort(boundComparator);

      }

      // 如果沒有指定silent參數(shù), 則觸發(fā)reset事件

      if(!options.silent)

        this.trigger('reset', this, options);

      return this;

    },

    // 將集合中所有模型的attr屬性值存放到一個數(shù)組并返回

    pluck : function(attr) {

      // map是Underscore中的方法, 用于遍歷一個集合, 并將所有處理器的返回值作為一個數(shù)組返回

      return _.map(this.models, function(model) {

        // 返回當前模型的attr屬性值

        return model.get(attr);

      });

    },

    // 替換集合中的所有模型數(shù)據(jù)(models)

    // 該操作將刪除集合中當前的所有數(shù)據(jù)和狀態(tài), 并重新將數(shù)據(jù)設置為models

    // models應該是一個數(shù)組, 可以包含一系列Model模型對象, 或原始對象(將在add方法中自動創(chuàng)建為模型對象)

    reset : function(models, options) {

      // models是進行替換的模型(或數(shù)據(jù))數(shù)組

      models || ( models = []);

      // options默認是一個空對象

      options || ( options = {});

      // 遍歷當前集合中的模型, 依次刪除并解除它們與集合的引用關系

      for(var i = 0, l = this.models.length; i < l; i++) {

        this._removeReference(this.models[i]);

      }

      // 刪除集合數(shù)據(jù)并重置狀態(tài)

      this._reset();

      // 通過add方法將新的模型數(shù)據(jù)添加到集合

      // 這里通過exnted方法將配置項覆蓋到一個新的對象, 該對象默認silent為true, 因此不會觸發(fā)"add"事件

      // 如果在調(diào)用reset方法時沒有設置silent屬性則會觸發(fā)reset事件, 如果設置為true則不會觸發(fā)任何事件, 如果設置為false, 將依次觸發(fā)"add"和"reset"事件

      this.add(models, _.extend({

        silent : true

      }, options));

      // 如果在調(diào)用reset方法時沒有設置silent屬性, 則觸發(fā)reset事件

      if(!options.silent)

        this.trigger('reset', this, options);

      return this;

    },

    // 從服務器獲取集合的初始化數(shù)據(jù)

    // 如果在options中設置參數(shù)add=true, 則獲取到的數(shù)據(jù)會被追加到集合中, 否則將以服務器返回的數(shù)據(jù)替換集合中的當前數(shù)據(jù)

    fetch : function(options) {

      // 復制options對象, 因為options對象在后面會被修改用于臨時存儲數(shù)據(jù)

      options = options ? _.clone(options) : {};

      if(options.parse === undefined)

        options.parse = true;

      // collection記錄當前集合對象, 用于在success回調(diào)函數(shù)中使用

      var collection = this;

      // 自定義回調(diào)函數(shù), 數(shù)據(jù)請求成功后并添加完成后, 會調(diào)用自定義success函數(shù)

      var success = options.success;

      // 當從服務器請求數(shù)據(jù)成功時執(zhí)行options.success, 該函數(shù)中將解析并添加數(shù)據(jù)

      options.success = function(resp, status, xhr) {

        // 通過parse方法對服務器返回的數(shù)據(jù)進行解析, 如果需要自定義數(shù)據(jù)結構, 可以重載parse方法

        // 如果在options中設置add=true, 則調(diào)用add方法將數(shù)據(jù)添加到集合, 否則將通過reset方法將集合中的數(shù)據(jù)替換為服務器的返回數(shù)據(jù)

        collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);

        // 如果設置了自定義成功回調(diào), 則執(zhí)行

        if(success)

          success(collection, resp);

      };

      // 當服務器返回狀態(tài)錯誤時, 通過wrapError方法處理錯誤事件

      options.error = Backbone.wrapError(options.error, collection, options);

      // 調(diào)用Backbone.sync方法發(fā)送請求從服務器獲取數(shù)據(jù)

      // 如果需要的數(shù)據(jù)并不是從服務器獲取, 或獲取方式不使用AJAX, 可以重載Backbone.sync方法

      return (this.sync || Backbone.sync).call(this, 'read', this, options);

    },

    // 向集合中添加并創(chuàng)建一個模型, 同時將該模型保存到服務器

    // 如果是通過數(shù)據(jù)對象來創(chuàng)建模型, 需要在集合中聲明model屬性對應的模型類

    // 如果在options中聲明了wait屬性, 則會在服務器創(chuàng)建成功后再將模型添加到集合, 否則先將模型添加到集合, 再保存到服務器(無論保存是否成功)

    create : function(model, options) {

      var coll = this;

      // 定義options對象

      options = options ? _.clone(options) : {};

      // 通過_prepareModel獲取模型類的實例

      model = this._prepareModel(model, options);

      // 模型創(chuàng)建失敗

      if(!model)

        return false;

      // 如果沒有聲明wait屬性, 則通過add方法將模型添加到集合中

      if(!options.wait)

        coll.add(model, options);

      // success存儲保存到服務器成功之后的自定義回調(diào)函數(shù)(通過options.success聲明)

      var success = options.success;

      // 監(jiān)聽模型數(shù)據(jù)保存成功后的回調(diào)函數(shù)

      options.success = function(nextModel, resp, xhr) {

        // 如果聲明了wait屬性, 則在只有在服務器保存成功后才會將模型添加到集合中

        if(options.wait)

          coll.add(nextModel, options);

        // 如果聲明了自定義成功回調(diào), 則執(zhí)行自定義函數(shù), 否則將默認觸發(fā)模型的sync事件

        if(success) {

          success(nextModel, resp);

        } else {

          nextModel.trigger('sync', model, resp, options);

        }

      };

      // 調(diào)用模型的save方法, 將模型數(shù)據(jù)保存到服務器

      model.save(null, options);

      return model;

    },

    // 數(shù)據(jù)解析方法, 用于將服務器數(shù)據(jù)解析為模型和集合可用的結構化數(shù)據(jù)

    // 默認將返回resp本身, 這需要與服務器定義Backbone支持的數(shù)據(jù)格式, 如果需要自定義數(shù)據(jù)格式, 可以重載parse方法

    parse : function(resp, xhr) {

      return resp;

    },

    // chain用于構建集合數(shù)據(jù)的鏈式操作, 它將集合中的數(shù)據(jù)轉換為一個Underscore對象, 并使用Underscore的chain方法轉換為鏈式結構

    // 關于chain方法的轉換方式, 可參考Underscore中chain方法的注釋

    chain : function() {

      return _(this.models).chain();

    },

    // 刪除所有集合元素并重置集合中的數(shù)據(jù)狀態(tài)

    _reset : function(options) {

      // 刪除集合元素

      this.length = 0;

      this.models = [];

      // 重置集合狀態(tài)

      this._byId = {};

      this._byCid = {};

    },

    // 將模型添加到集合中之前的一些準備工作

    // 包括將數(shù)據(jù)實例化為一個模型對象, 和將集合引用到模型的collection屬性

    _prepareModel : function(model, options) {

      options || ( options = {});

      // 檢查model是否是一個模型對象(即Model類的實例)

      if(!( model instanceof Model)) {

        // 傳入的model是模型數(shù)據(jù)對象, 而并非模型對象

        // 將數(shù)據(jù)作為參數(shù)傳遞給Model, 以創(chuàng)建一個新的模型對象

        var attrs = model;

        // 設置模型引用的集合

        options.collection = this;

        // 將數(shù)據(jù)轉化為模型

        model = new this.model(attrs, options);

        // 對模型中的數(shù)據(jù)進行驗證

        if(!model._validate(model.attributes, options))

          model = false;

      } else if(!model.collection) {

        // 如果傳入的是一個模型對象但沒有建立與集合的引用, 則設置模型的collection屬性為當前集合

        model.collection = this;

      }

      return model;

    },

    // 解綁某個模型與集合的關系, 包括對集合的引用和事件監(jiān)聽

    // 一般在調(diào)用remove方法刪除模型或調(diào)用reset方法重置狀態(tài)時自動調(diào)用

    _removeReference : function(model) {

      // 如果模型引用了當前集合, 則移除該引用(必須確保所有對模型的引用已經(jīng)解除, 否則模型可能無法從內(nèi)存中釋放)

      if(this == model.collection) {

        delete model.collection;

      }

      // 取消集合中監(jiān)聽的所有模型事件

      model.off('all', this._onModelEvent, this);

    },

    // 在向集合中添加模型時被自動調(diào)用

    // 用于監(jiān)聽集合中模型的事件, 當模型在觸發(fā)事件(add, remove, destroy, change事件)時集合進行相關處理

    _onModelEvent : function(event, model, collection, options) {

      // 添加和移除模型的事件, 必須確保模型所屬的集合為當前集合對象

      if((event == 'add' || event == 'remove') && collection != this)

        return;

      // 模型觸發(fā)銷毀事件時, 從集合中移除

      if(event == 'destroy') {

        this.remove(model, options);

      }

      // 當模型的id被修改時, 集合修改_byId中存儲對模型的引用, 保持與模型id的同步, 便于使用get()方法獲取模型對象

      if(model && event === 'change:' + model.idAttribute) {

        // 獲取模型在改變之前的id, 并根據(jù)此id從集合的_byId列表中移除

        delete this._byId[model.previous(model.idAttribute)];

        // 以模型新的id作為key, 在_byId列表中存放對模型的引用

        this._byId[model.id] = model;

      }

      // 在集合中觸發(fā)模型對應的事件, 無論模型觸發(fā)任何事件, 集合都會觸發(fā)對應的事件

      // (例如當模型被添加到集合中時, 會觸發(fā)模型的"add"事件, 同時也會在此方法中觸發(fā)集合的"add"事件)

      // 這對于監(jiān)聽并處理集合中模型狀態(tài)的變化非常有效

      // 在監(jiān)聽的集合事件中, 觸發(fā)對應事件的模型會被作為參數(shù)傳遞給集合的監(jiān)聽函數(shù)

      this.trigger.apply(this, arguments);

    }

  });

 

  // 定義Underscore中的集合操作的相關方法

  // 將Underscore中一系列集合操作方法復制到Collection集合類的原型對象中

  // 這樣就可以直接通過集合對象調(diào)用Underscore相關的集合方法

  // 這些方法在調(diào)用時所操作的集合數(shù)據(jù)是當前Collection對象的models數(shù)據(jù)

  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy'];

 

  // 遍歷已經(jīng)定義的方法列表

  _.each(methods, function(method) {

    // 將方法復制到Collection集合類的原型對象

    Collection.prototype[method] = function() {

      // 調(diào)用時直接使用Underscore的方法, 上下文對象保持為Underscore對象

      // 需要注意的是這里傳遞給Underscore方法的集合參數(shù)是 this.models, 因此在使用這些方法時, 所操作的集合對象是當前Collection對象的models數(shù)據(jù)

      return _[method].apply(_, [this.models].concat(_.toArray(arguments)));

    };

  });

  // Backbone.Router URL路由器

  // -------------------

 

  // 通過繼承Backbone.Router類實現(xiàn)自定義的路由器

  // 路由器允許定義路由規(guī)則, 通過URL片段進行導航, 并將每一個規(guī)則對應到一個方法, 當URL匹配某個規(guī)則時會自動執(zhí)行該方法

  // 路由器通過URL進行導航, 導航方式分為pushState, Hash, 和監(jiān)聽方式(詳細可參考Backbone.History類)

  // 在創(chuàng)建Router實例時, 通過options.routes來設置某個路由規(guī)則對應的監(jiān)聽方法

  // options.routes中的路由規(guī)則按照 {規(guī)則名稱: 方法名稱}進行組織, 每一個路由規(guī)則所對應的方法, 都必須是在Router實例中的已經(jīng)聲明的方法

  // options.routes定義的路由規(guī)則按照先后順序進行匹配, 如果當前URL能被多個規(guī)則匹配, 則只會執(zhí)行第一個匹配的事件方法

  var Router = Backbone.Router = function(options) {

    // options默認是一個空對象

    options || ( options = {});

    // 如果在options中設置了routes對象(路由規(guī)則), 則賦給當前實例的routes屬性

    // routes屬性記錄了路由規(guī)則與事件方法的綁定關系, 當URL與某一個規(guī)則匹配時, 會自動調(diào)用關聯(lián)的事件方法

    if(options.routes)

      this.routes = options.routes;

    // 解析和綁定路由規(guī)則

    this._bindRoutes();

    // 調(diào)用自定義的初始化方法

    this.initialize.apply(this, arguments);

  };

  // 定義用于將字符串形式的路由規(guī)則, 轉換為可執(zhí)行的正則表達式規(guī)則時的查找條件

  // (字符串形式的路由規(guī)則, 通過\w+進行匹配, 因此只支持字母數(shù)字和下劃線組成的字符串)

  // 匹配一個URL片段中(以/"斜線"為分隔)的動態(tài)路由規(guī)則

  // 如: (topic/:id) 匹配 (topic/1228), 監(jiān)聽事件function(id) { // id為1228 }

  var namedParam = /:\w+/g;

  // 匹配整個URL片段中的動態(tài)路由規(guī)則

  // 如: (topic*id) 匹配 (url#/topic1228), 監(jiān)聽事件function(id) { // id為1228 }

  var splatParam = /\*\w+/g;

  // 匹配URL片段中的特殊字符, 并在字符前加上轉義符, 防止特殊字符在被轉換為正則表達式后變成元字符

  // 如: (abc)^[,.] 將被轉換為 \(abc\)\^\[\,\.\]

  var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;

 

  // 向Router類的原型對象中擴展屬性和方法

  _.extend(Router.prototype, Events, {

 

    // 自定義初始化方法, 在路由器Router實例化后被自動調(diào)用

    initialize : function() {

    },

    // 將一個路由規(guī)則綁定給一個監(jiān)聽事件, 當URL片段匹配該規(guī)則時, 會自動調(diào)用觸發(fā)該事件

    route : function(route, name, callback) {

      // 創(chuàng)建history實例, Backbone.history是一個單例對象, 只在第一次創(chuàng)建路由器對象時被實例化

      Backbone.history || (Backbone.history = new History);

      // 檢查route規(guī)則名稱是否為一個字符串(當手動調(diào)用route方法創(chuàng)建路由規(guī)則時, 允許傳遞一個正則表達式或字符串作為規(guī)則)

      // 在構造Router實例時傳入options.routes中的規(guī)則, 都應該是一個字符串(因為在_bindRoutes方法中將routes配置中的key作為路由規(guī)則)

      // 如果傳入的是字符串類型的路由規(guī)則, 通過_routeToRegExp方法將其轉換為一個正則表達式, 用于匹配URL片段

      if(!_.isRegExp(route))

        route = this._routeToRegExp(route);

      // 如果沒有設置callback(事件方法), 則根據(jù)name從當前Router實例中獲取與name同名的方法

      // 這是因為在手動調(diào)用route方法時可能不會傳遞callback方法, 但必須傳遞name事件名稱, 并在Router實例中已經(jīng)定義了該方法

      if(!callback)

        callback = this[name];

      // 調(diào)用history實例的route方法, 該方法會將轉換后的正則表達式規(guī)則, 和監(jiān)聽事件方法綁定到history.handlers列表中, 以便history進行路由和控制

      // 當history實例匹配到對應的路由規(guī)則而調(diào)用該事件時, 會將URL片段作為字符串(即fragment參數(shù))傳遞給該事件方法

      // 這里并沒有直接將監(jiān)聽事件傳遞給history的route方法, 而是使用bind方法封裝了另一個函數(shù), 該函數(shù)的執(zhí)行上下文為當前Router對象

      Backbone.history.route(route, _.bind(function(fragment) {

        // 調(diào)用_extractParameters方法獲取匹配到的規(guī)則中的參數(shù)

        var args = this._extractParameters(route, fragment);

        // 調(diào)用callback路由監(jiān)聽事件, 并將參數(shù)傳遞給監(jiān)聽事件

        callback && callback.apply(this, args);

        // 觸發(fā)route:name事件, name為調(diào)用route時傳遞的事件名稱

        // 如果對當前Router實例使用on方法綁定了route:name事件, 則會收到該事件的觸發(fā)通知

        this.trigger.apply(this, ['route:' + name].concat(args));

        // 觸發(fā)history實例中綁定的route事件, 當路由器匹配到任何規(guī)則時, 均會觸發(fā)該事件

        Backbone.history.trigger('route', this, name, args);

        /**

         * 事件綁定如:

         * var router = new MyRouter();

         * router.on('route:routename', function(param) {

         *   // 綁定到Router實例中某個規(guī)則的事件, 當匹配到該規(guī)則時觸發(fā)

         * });

         * Backbone.history.on('route', function(router, name, args) {

         *   // 綁定到history實例中的事件, 當匹配到任何規(guī)則時觸發(fā)

         * });

         * Backbone.history.start();

         */

      }, this));

      return this;

    },

    // 通過調(diào)用history.navigate方法, 手動設置跳轉到URL

    navigate : function(fragment, options) {

      // 代理到history實例的navigate方法

      Backbone.history.navigate(fragment, options);

    },

    // 解析當前實例定義的路由(this.routes)規(guī)則, 并調(diào)用route方法將每一個規(guī)則綁定到對應的方法

    _bindRoutes : function() {

      // 如果在創(chuàng)建對象時沒有設置routes規(guī)則, 則不進行解析和綁定

      if(!this.routes)

        return;

      // routes變量以二維數(shù)組的形式存儲倒序排列的路由規(guī)則

      // 如[['', 'homepage'], ['controller:name', 'toController']]

      var routes = [];

      // 遍歷routes配置

      for(var route in this.routes) {

        // 將路由規(guī)則放入一個新的數(shù)組, 按照[規(guī)則名稱, 綁定方法]組織

        // 將該數(shù)組通過unshift方法放置到routes頂部, 實現(xiàn)倒序排列

        // 這里將routes中的規(guī)則倒序排列, 在后面調(diào)用route方法時會再次調(diào)用unshift將順序倒過來, 以保證最終的順序是按照routes配置中定義的順序來執(zhí)行的

        // 倒換兩次順序后, 會重新恢復最初調(diào)用前的順序, 之所以這樣做, 是因為用戶可以手動調(diào)用route方法動態(tài)添加路由規(guī)則, 而手動添加的路由規(guī)則會被添加到列表的第一個, 因此要在route方法中使用unshift來插入規(guī)則

        // 而構造Router實例時自動添加的規(guī)則, 為了保持定義順序, 因此在此處將定義的規(guī)則倒序排列

        routes.unshift([route, this.routes[route]]);

      }

      // 循環(huán)完畢, 此時routes中存儲了倒序排列的路由規(guī)則

 

      // 循環(huán)路由規(guī)則, 并依次調(diào)用route方法, 將規(guī)則名稱綁定到具體的事件函數(shù)

      for(var i = 0, l = routes.length; i < l; i++) {

        // 調(diào)用route方法, 并分別傳遞(規(guī)則名稱, 事件函數(shù)名, 事件函數(shù)對象)

        this.route(routes[i][0], routes[i][1], this[routes[i][1]]);

      }

    },

    // 將字符串形式的路由規(guī)則轉換為正則表達式對象

    // (在route方法中檢查到字符串類型的路由規(guī)則后, 會自動調(diào)用該方法進行轉換)

    _routeToRegExp : function(route) {

      // 為字符串中特殊字符添加轉義符, 防止特殊字符在被轉換為正則表達式后變成元字符(這些特殊字符包括-[\]{}()+?.,\\^$|#\s)

      // 將字符串中以/"斜線"為分隔的動態(tài)路由規(guī)則轉換為([^\/]+), 在正則中表示以/"斜線"開頭的多個字符

      // 將字符串中的*"星號"動態(tài)路由規(guī)則轉換為(.*?), 在正則中表示0或多個任意字符(這里使用了非貪婪模式, 因此你可以使用例如這樣的組合路由規(guī)則: *list/:id, 將匹配 orderlist/123 , 同時會將"order"和"123"作為參數(shù)傳遞給事件方法 )

      // 請注意namedParam和splatParam替換后的正則表達式都是用()括號將匹配的內(nèi)容包含起來, 這是為了方便取出匹配的內(nèi)容作為參數(shù)傳遞給事件方法

      // 請注意namedParam和splatParam匹配的字符串 :str, *str中的str字符串是無意義的, 它們會在下面替換后被忽略, 但一般寫作和監(jiān)聽事件方法的參數(shù)同名, 以便進行標識

      route = route.replace(escapeRegExp, '\\$&').replace(namedParam, '([^\/]+)').replace(splatParam, '(.*?)');

      // 將轉換后的字符串創(chuàng)建為正則表達式對象并返回

      // 這個正則表達式將根據(jù)route字符串中的規(guī)則, 用于匹配URL片段

      return new RegExp('^' + route + '$');

    },

    // 傳入一個路由規(guī)則(正則表達式)和URL片段(字符串)進行匹配, 并返回從匹配的字符串中獲取參數(shù)

    /**

     * 例如路由規(guī)則為 'teams/:type/:id', 對應的正則表達式會被轉換為/^teams/([^/]+)/([^/]+)$/ , (對路由規(guī)則轉換為正則表達式的過程可參考_routeToRegExp方法)

     * URL片段為 'teams/35/1228'

     * 則通過exec執(zhí)行后的結果為 ["teams/35/1228", "35", "1228"]

     * 數(shù)組中的一個元素是URL片段字符串本身, 從第二個開始則依次為路由規(guī)則表達式中的參數(shù)

     */

    _extractParameters : function(route, fragment) {

      return route.exec(fragment).slice(1);

    }

  });

 

  // Backbone.History 路由器管理

  // ----------------

 

  // History類提供路由管理相關操作, 包括監(jiān)聽URL的變化, (通過popstate和onhashchange事件進行監(jiān)聽, 對于不支持事件的瀏覽器通過setInterval心跳監(jiān)控)

  // 提供路由規(guī)則與當前URL的匹配驗證, 和觸發(fā)相關的監(jiān)聽事件

  // History一般不會被直接調(diào)用, 在第一次實例化Router對象時, 將自動創(chuàng)建一個History的單例(通過Backbone.history訪問)

  var History = Backbone.History = function() {

    // handlers屬性記錄了當前所有路由對象中已經(jīng)設置的規(guī)則和監(jiān)聽列表

    // 形式如: [{route: route, callback: callback}], route記錄了正則表達式規(guī)則, callback記錄了匹配規(guī)則時的監(jiān)聽事件

    // 當history對象監(jiān)聽到URL發(fā)生變化時, 會自動與handlers中定義的規(guī)則進行匹配, 并調(diào)用監(jiān)聽事件

    this.handlers = [];

    // 將checkUrl方法的上下文對象綁定到history對象, 因為checkUrl方法被作為popstate和onhashchange事件或setInterval的回調(diào)函數(shù), 在執(zhí)行回調(diào)時, 上下文對象會被改變

    // checkUrl方法用于在監(jiān)聽到URL發(fā)生變化時檢查并調(diào)用loadUrl方法

    _.bindAll(this, 'checkUrl');

  };

  // 定義用于匹配URL片段中首字符是否為"#"或"/"的正則

  var routeStripper = /^[#\/]/;

 

  // 定義用于匹配從userAgent中獲取的字符串是否包含IE瀏覽器的標識, 用于判斷當前瀏覽器是否為IE

  var isExplorer = /msie [\w.]+/;

 

  // 記錄當前history單例對象是否已經(jīng)被初始化過(調(diào)用start方法)

  History.started = false;

 

  // 向History類的原型對象中添加方法, 這些方法可以通過History的實例調(diào)用(即Backbone.history對象)

  _.extend(History.prototype, Events, {

 

    // 當用戶使用低版本的IE瀏覽器(不支持onhashchange事件)時, 通過心跳監(jiān)聽路由狀態(tài)的變化

    // interval屬性設置心跳頻率(毫秒), 該頻率如果太低可能會導致延遲, 如果太高可能會消耗CPU資源(需要考慮用戶使用低端瀏覽器時的設備配置)

    interval : 50,

 

    // 獲取location中Hash字符串(錨點#后的片段)

    getHash : function(windowOverride) {

      // 如果傳入了一個window對象, 則從該對象中獲取, 否則默認從當前window對象中獲取

      var loc = windowOverride ? windowOverride.location : window.location;

      // 將錨點(#)后的字符串提取出來并返回

      var match = loc.href.match(/#(.*)$/);

      // 如果沒有找到匹配的內(nèi)容, 則返回空字符串

      return match ? match[1] : '';

    },

    // 根據(jù)當前設置的路由方式, 處理并返回當前URL中的路由片段

    getFragment : function(fragment, forcePushState) {

      // fragment是通過getHash或從URL中已經(jīng)提取的待處理路由片段(如 #/id/1288)

      if(fragment == null) {// 如果沒有傳遞fragment, 則根據(jù)當前路由方式進行提取

        if(this._hasPushState || forcePushState) {

          // 使用了pushState方式進行路由

          // fragment記錄當前域名后的URL路徑

          fragment = window.location.pathname;

          // search記錄當前頁面后的參數(shù)內(nèi)容

          var search = window.location.search;

          // 將路徑和參數(shù)合并在一起, 作為待處理的路由片段

          if(search)

            fragment += search;

        } else {

          // 使用了hash方式進行路由

          // 通過getHash方法獲取當前錨點(#)后的字符串作為路由片段

          fragment = this.getHash();

        }

      }

      // 根據(jù)配置項中設置的root參數(shù), 則從路由片段取出root路徑之后的內(nèi)容

      if(!fragment.indexOf(this.options.root))

        fragment = fragment.substr(this.options.root.length);

      // 如果URL片段首字母為"#"或"/", 則去除該字符

      // 返回處理之后的URL片段

      return fragment.replace(routeStripper, '');

    },

    // 初始化History實例, 該方法只會被調(diào)用一次, 應該在創(chuàng)建并初始化Router對象之后被自動調(diào)用

    // 該方法作為整個路由的調(diào)度器, 它將針對不同瀏覽器監(jiān)聽URL片段的變化, 負責驗證并通知到監(jiān)聽函數(shù)

    start : function(options) {

      // 如果history對象已經(jīng)被初始化過, 則拋出錯誤

      if(History.started)

        throw new Error("Backbone.history has already been started");

      // 設置history對象的初始化狀態(tài)

      History.started = true;

 

      // 設置配置項, 使用調(diào)用start方法時傳遞的options配置項覆蓋默認配置

      this.options = _.extend({}, {

        // root屬性設置URL導航中的路由根目錄

        // 如果使用pushState方式進行路由, 則root目錄之后的地址會根據(jù)不同的路由產(chǎn)生不同的地址(這可能會定位到不同的頁面, 因此需要確保服務器支持)

        // 如果使用Hash錨點的方式進行路由, 則root表示URL后錨點(#)的位置

        root : '/'

      }, this.options, options);

      /**

       * history針對不同瀏覽器特性, 實現(xiàn)了3種方式的監(jiān)聽:

       * - 對于支持HTML5中popstate事件的瀏覽器, 通過popstate事件進行監(jiān)聽

       * - 對于不支持popstate的瀏覽器, 使用onhashchange事件進行監(jiān)聽(通過改變hash(錨點)設置的URL在被載入時會觸發(fā)onhashchange事件)

       * - 對于不支持popstate和onhashchange事件的瀏覽器, 通過保持心跳監(jiān)聽

       *

       * 關于HTML5中popstate事件的相關方法:

       * - pushState可以將指定的URL添加一個新的history實體到瀏覽器歷史里

       * - replaceState方法可以將當前的history實體替換為指定的URL

       * 使用pushState和replaceState方法時僅替換當前URL, 而并不會真正轉到這個URL(當使用后退或前進按鈕時, 也不會跳轉到該URL)

       * (這兩個方法可以解決在AJAX單頁應用中瀏覽器前進, 后退操作的問題)

       * 當使用pushState或replaceState方法替換的URL, 在被載入時會觸發(fā)onpopstate事件

       * 瀏覽器支持情況:

       * Chrome 5, Firefox 4.0, IE 10, Opera 11.5, Safari 5.0

       *

       * 注意:

       * - history.start方法默認使用Hash方式進行導航

       * - 如果需要啟用pushState方式進行導航, 需要在調(diào)用start方法時, 手動傳入配置options.pushState

       *  (設置前請確保瀏覽器支持pushState特性, 否則將默認轉換為Hash方式)

       * - 當使用pushState方式進行導航時, URL可能會從options.root指定的根目錄后發(fā)生變化, 這可能會導航到不同頁面, 因此請確保服務器已經(jīng)支持pushState方式的導航

       */

      // _wantsHashChange屬性記錄是否希望使用hash(錨點)的方式來記錄和導航路由器

      // 除非在options配置項中手動設置hashChange為false, 否則默認將使用hash錨點的方式

      // (如果手動設置了options.pushState為true, 且瀏覽器支持pushState特性, 則會使用pushState方式)

      this._wantsHashChange = this.options.hashChange !== false;

      // _wantsPushState屬性記錄是否希望使用pushState方式來記錄和導航路由器

      // pushState是HTML5中為window.history添加的新特性, 如果沒有手動聲明options.pushState為true, 則默認將使用hash方式

      this._wantsPushState = !!this.options.pushState;

      // _hasPushState屬性記錄瀏覽器是否支持pushState特性

      // 如果在options中設置了pushState(即希望使用pushState方式), 則檢查瀏覽器是否支持該特性

      this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState);

      // 獲取當前URL中的路由字符串

      var fragment = this.getFragment();

      // documentMode是IE瀏覽器的獨有屬性, 用于標識當前瀏覽器使用的渲染模式

      var docMode = document.documentMode;

      // oldIE用于檢查當前瀏覽器是否為低版本的IE瀏覽器(即IE 7.0以下版本)

      // 這句代碼可理解為: 當前瀏覽器為IE, 但不支持documentMode屬性, 或documentMode屬性返回的渲染模式為IE7.0以下

      var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));

 

      if(oldIE) {

        // 如果用戶使用低版本的IE瀏覽器, 不支持popstate和onhashchange事件

        // 向DOM中插入一個隱藏的iframe, 并通過改變和心跳監(jiān)聽該iframe的URL實現(xiàn)路由

        this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;

        // 通過navigate將iframe設置到當前的URL片段, 這并不會真正加載到一個頁面, 因為fragment并非一個完整的URL

        this.navigate(fragment);

      }

 

      // 開始監(jiān)聽路由狀態(tài)變化

      if(this._hasPushState) {

        // 如果使用了pushState方式路由, 且瀏覽器支持該特性, 則將popstate事件監(jiān)聽到checkUrl方法

        $(window).bind('popstate', this.checkUrl);

      } else if(this._wantsHashChange && ('onhashchange' in window) && !oldIE) {

        // 如果使用Hash方式進行路由, 且瀏覽器支持onhashchange事件, 則將hashchange事件監(jiān)聽到checkUrl方法

        $(window).bind('hashchange', this.checkUrl);

      } else if(this._wantsHashChange) {

        // 對于低版本的瀏覽器, 通過setInterval方法心跳監(jiān)聽checkUrl方法, interval屬性標識心跳頻率

        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);

      }

 

      // 記錄當前的URL片段

      this.fragment = fragment;

      // 驗證當前是否處于根路徑(即options.root中所配置的路徑)

      var loc = window.location;

      var atRoot = loc.pathname == this.options.root;

 

      // 如果用戶通過pushState方式的URL訪問到當前地址, 但用戶此時所使用的瀏覽器并不支持pushState特性

      // (這可能是某個用戶通過pushState方式訪問該應用, 然后將地址分享給其他用戶, 而其他用戶的瀏覽器并不支持該特性)

      if(this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {

        // 獲取當前pushState方式中的URL片段, 并通過Hash方式重新打開頁面

        this.fragment = this.getFragment(null, true);

        // 例如hashState方式的URL為 /root/topic/12001, 重新打開的Hash方式的URL則為 /root#topic/12001

        window.location.replace(this.options.root + '#' + this.fragment);

        return true;

 

        // 如果用戶通過Hash方式的URL訪問到當前地址, 但調(diào)用Backbone.history.start方法時設置了pushState(希望通過pushState方式進行路由)

        // 且用戶瀏覽器支持pushState特性, 則將當前URL替換為pushState方式(注意, 這里使用replaceState方式進行替換URL, 而頁面不會被刷新)

        // 以下分支條件可理解為: 如果我們希望使用pushState方式進行路由, 且瀏覽器支持該特性, 同時用戶還使用了Hash方式打開當前頁面

        // (這可能是某個用戶使用Hash方式瀏覽到一個URL, 并將URL分享給另一個瀏覽器支持pushState特性的用戶, 當該用戶訪問時會執(zhí)行此分支)

      } else if(this._wantsPushState && this._hasPushState && atRoot && loc.hash) {

        // 獲取URL中的Hash片段, 并清除字符串首個"#"或"/"

        this.fragment = this.getHash().replace(routeStripper, '');

        // 使用replaceState方法將當前瀏覽器的URL替換為pushState支持的方式, 即: 協(xié)議//主機地址/URL路徑/Hash參數(shù), 例如:

        // 當用戶訪問Hash方式的URL為 /root/#topic/12001, 將被替換為 /root/topic/12001

        // 注:

        // pushState和replaceState方法的參數(shù)有3個, 分別是state, title, url

        // -state: 用于存儲插入或修改的history實體信息

        // -title: 用于設置瀏覽器標題(屬于保留參數(shù), 目前瀏覽器還沒有實現(xiàn)該特性)

        // -url: 設置history實體的URL地址(可以是絕對或相對路徑, 但無法設置跨域URL)

        window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);

      }

 

      // 一般調(diào)用start方法時會自動調(diào)用loadUrl, 匹配當前URL片段對應的路由規(guī)則, 調(diào)用該規(guī)則的方法

      // 如果設置了silent屬性為true, 則loadUrl方法不會被調(diào)用

      // 這種情況一般出現(xiàn)在調(diào)用了stop方法重置history對象狀態(tài)后, 再次調(diào)用start方法啟動(實際上此時并非為頁面初始化, 因此會設置silent屬性)

      if(!this.options.silent) {

        return this.loadUrl();

      }

    },

    // 停止history對路由的監(jiān)控, 并將狀態(tài)恢復為未監(jiān)聽狀態(tài)

    // 調(diào)用stop方法之后, 可重新調(diào)用start方法開始監(jiān)聽, stop方法一般用戶在調(diào)用start方法之后, 需要重新設置start方法的參數(shù), 或用于單元測試

    stop : function() {

      // 解除對瀏覽器路由的onpopstate和onhashchange事件的監(jiān)聽

      $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);

      // 停止對于低版本的IE瀏覽器的心跳監(jiān)控

      clearInterval(this._checkUrlInterval);

      // 恢復started狀態(tài), 便于下次重新調(diào)用start方法

      History.started = false;

    },

    // 向handlers中綁定一個路由規(guī)則(參數(shù)route, 類型為正則表達式)與事件(參數(shù)callback)的映射關系(該方法由Router的實例自動調(diào)用)

    route : function(route, callback) {

      // 將route和callback插入到handlers列表的第一個位置

      // 這是為了確保最后調(diào)用route時傳入的規(guī)則被優(yōu)先進行匹配

      this.handlers.unshift({

        // 路由規(guī)則(正則)

        route : route,

        // 匹配規(guī)則時執(zhí)行的方法

        callback : callback

      });

    },

    // 檢查當前的URL相對上一次的狀態(tài)是否發(fā)生了變化

    // 如果發(fā)生變化, 則記錄新的URL狀態(tài), 并調(diào)用loadUrl方法觸發(fā)新URL與匹配路由規(guī)則的方法

    // 該方法在onpopstate和onhashchange事件被觸發(fā)后自動調(diào)用, 或者在低版本的IE瀏覽器中由setInterval心跳定時調(diào)用

    checkUrl : function(e) {

      // 獲取當前的URL片段

      var current = this.getFragment();

      // 對低版本的IE瀏覽器, 將從iframe中獲取最新的URL片段并賦給current變量

      if(current == this.fragment && this.iframe)

        current = this.getFragment(this.getHash(this.iframe));

      // 如果當前URL與上一次的狀態(tài)沒有發(fā)生任何變化, 則停止執(zhí)行

      if(current == this.fragment)

        return false;

      // 執(zhí)行到這里, URL已經(jīng)發(fā)生改變, 調(diào)用navigate方法將URL設置為當前URL

      // 這里在自動調(diào)用navigate方法時, 并沒有傳遞options參數(shù), 因此不會觸發(fā)navigate方法中的loadUrl方法

      if(this.iframe)

        this.navigate(current);

      // 調(diào)用loadUrl方法, 檢查匹配的規(guī)則, 并執(zhí)行規(guī)則綁定的方法

      // 如果調(diào)用this.loadUrl方法沒有成功, 則試圖在調(diào)用loadUrl方法時, 將重新獲取的當前Hash傳遞給該方法

      this.loadUrl() || this.loadUrl(this.getHash());

    },

    // 根據(jù)當前URL, 與handler路由列表中的規(guī)則進行匹配

    // 如果URL符合某一個規(guī)則, 則執(zhí)行這個規(guī)則所對應的方法, 函數(shù)將返回true

    // 如果沒有找到合適的規(guī)則, 將返回false

    // loadUrl方法一般在頁面初始化時調(diào)用start方法會被自動調(diào)用(除非設置了silent參數(shù)為true)

    // - 或當用戶改變URL后, 由checkUrl監(jiān)聽到URL發(fā)生變化時被調(diào)用

    // - 或當調(diào)用navigate方法手動導航到某個URL時被調(diào)用

    loadUrl : function(fragmentOverride) {

      // 獲取當前URL片段

      var fragment = this.fragment = this.getFragment(fragmentOverride);

      // 調(diào)用Undersocre的any方法, 將URL片段與handlers中的所有規(guī)則依次進行匹配

      var matched = _.any(this.handlers, function(handler) {

        // 如果handlers中的規(guī)則與當前URL片段匹配, 則執(zhí)行該歸額對應的方法, 并返回true

        if(handler.route.test(fragment)) {

          handler.callback(fragment);

          return true;

        }

      });

      // matched是any方法的返回值, 如果匹配到規(guī)則則返回true, 沒有匹配到返回false

      return matched;

    },

    // 導航到指定的URL

    // 如果在options中設置了trigger, 將觸發(fā)導航的URL與對應路由規(guī)則的事件

    // 如果在options中設置了replace, 將使用需要導航的URL替換當前的URL在history中的位置

    navigate : function(fragment, options) {

      // 如果沒有調(diào)用start方法, 或已經(jīng)調(diào)用stop方法, 則無法導航

      if(!History.started)

        return false;

      // 如果options參數(shù)不是一個對象, 而是true值, 則默認trigger配置項為true(即觸發(fā)導航的URL與對應路由規(guī)則的事件)

      if(!options || options === true)

        options = {

          trigger : options

        };

      // 將傳遞的fragment(URL片段)去掉首字符的"#"或"/"

      var frag = (fragment || '').replace(routeStripper, '');

      // 如果當前URL與需要導航的URL沒有變化, 則不繼續(xù)執(zhí)行

      if(this.fragment == frag)

        return;

 

      // 如果當前支持并使用了pushState方式進行導航

      if(this._hasPushState) {

        // 構造一個完整的URL, 如果當前URL片段中沒有包含根路徑, 則使用根路徑連接URL片段

        if(frag.indexOf(this.options.root) != 0)

          frag = this.options.root + frag;

        // 設置新的URL

        this.fragment = frag;

        // 如果在options選項中設置了replace屬性, 則將新的URL替換到history中的當前URL, 否則默認將新的URL追加到history中

        window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag);

 

        // 如果使用hash方式進行導航

      } else if(this._wantsHashChange) {

        // 設置新的hash

        this.fragment = frag;

        // 調(diào)用_updateHash方法更新當前URL為新的hash, 并將options中的replace配置傳遞給_updateHash方法(在該方法中實現(xiàn)替換或追加新的hash)

        this._updateHash(window.location, frag, options.replace);

        // 對于低版本的IE瀏覽器, 當Hash發(fā)生變化時, 更新iframe URL中的Hash

        if(this.iframe && (frag != this.getFragment(this.getHash(this.iframe)))) {

          // 如果使用了replace參數(shù)替換當前URL, 則直接將iframe替換為新的文檔

          // 調(diào)用document.open打開一個新的文檔, 以擦除當前文檔中的內(nèi)容(這里調(diào)用close方法是為了關閉文檔的狀態(tài))

          // open和close方法之間沒有使用write或writeln方法輸出內(nèi)容, 因此這是一個空文檔

          if(!options.replace)

            this.iframe.document.open().close();

          // 調(diào)用_updateHash方法更新iframe中的URL

          this._updateHash(this.iframe.location, frag, options.replace);

        }

 

      } else {

        // 如果在調(diào)用start方法時, 手動設置hashChange參數(shù)為true, 不希望使用pushState和hash方式導航

        // 則直接將頁面跳轉到新的URL

        window.location.assign(this.options.root + fragment);

      }

      // 如果在options配置項中設置了trigger屬性, 則調(diào)用loadUrl方法查找路由規(guī)則, 并執(zhí)行規(guī)則對應的事件

      // 在URL發(fā)生變化時, 通過checkUrl方法監(jiān)聽到的狀態(tài), 會在checkUrl方法中自動調(diào)用loadUrl方法

      // 在手動調(diào)用navigate方法時, 如果需要觸發(fā)路由事件, 則需要傳遞trigger參數(shù)

      if(options.trigger)

        this.loadUrl(fragment);

    },

    // 更新或設置當前URL中的Has串, _updateHash方法在使用hash方式導航時被自動調(diào)用(navigate方法中)

    // location是需要更新hash的window.location對象

    // fragment是需要更新的hash串

    // 如果需要將新的hash替換到當前URL, 可以設置replace為true

    _updateHash : function(location, fragment, replace) {

      // 如果設置了replace為true, 則使用location.replace方法替換當前的URL

      // 使用replace方法替換URL后, 新的URL將占有原有URL在history歷史中的位置

      if(replace) {

        // 將當前URL與hash組合為一個完整的URL并替換

        location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment);

      } else {

        // 沒有使用替換方式, 直接設置location.hash為新的hash串

        location.hash = fragment;

      }

    }

  });

 

  // Backbone.View 視圖相關

  // -------------

 

  // 視圖類用于創(chuàng)建與數(shù)據(jù)低耦合的界面控制對象, 通過將視圖的渲染方法綁定到數(shù)據(jù)模型的change事件, 當數(shù)據(jù)發(fā)生變化時會通知視圖進行渲染

  // 視圖對象中的el用于存儲當前視圖所需要操作的DOM最父層元素, 這主要是為了提高元素的查找和操作效率, 其優(yōu)點包括:

  // - 查找或操作元素時, 將操作的范圍限定在el元素內(nèi), 不需要再整個文檔樹中搜索

  // - 在為元素綁定事件時, 可以方便地將事件綁定到el元素(默認也會綁定到el元素)或者是其子元素

  // - 在設計模式中, 將一個視圖相關的元素, 事件, 和邏輯限定在該視圖的范圍中, 降低視圖與視圖間的耦合(至少在邏輯上是這樣)

  var View = Backbone.View = function(options) {

    // 為每一個視圖對象創(chuàng)建一個唯一標識, 前綴為"view"

    this.cid = _.uniqueId('view');

    // 設置初始化配置

    this._configure(options || {});

    // 設置或創(chuàng)建視圖中的元素

    this._ensureElement();

    // 調(diào)用自定義的初始化方法

    this.initialize.apply(this, arguments);

    // 解析options中設置的events事件列表, 并將事件綁定到視圖中的元素

    this.delegateEvents();

  };

  // 定義用于解析events參數(shù)中事件名稱和元素的正則

  var delegateEventSplitter = /^(\S+)\s*(.*)$/;

 

  // viewOptions列表記錄一些列屬性名, 在構造視圖對象時, 如果傳遞的配置項中包含這些名稱, 則將屬性復制到對象本身

  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];

 

  // 向視圖類的原型對象中添加一些方法

  _.extend(View.prototype, Events, {

 

    // 如果在創(chuàng)建視圖對象時, 沒有設置指定的el元素, 則會通過make方法創(chuàng)建一個元素, tagName為創(chuàng)建元素的默認標簽

    // 也可以通過在options中自定義tagName來覆蓋默認的"div"標簽

    tagName : 'div',

 

    // 每個視圖中都具有一個$選擇器方法, 該方法與jQuery或Zepto類似, 通過傳遞一個表達式來獲取元素

    // 但該方法只會在視圖對象的$el元素范圍內(nèi)進行查找, 因此會提高匹配效率

    $ : function(selector) {

      return this.$el.find(selector);

    },

    // 初始化方法, 在對象被實例化后自動調(diào)用

    initialize : function() {

    },

    // render方法與initialize方法類似, 默認沒有實現(xiàn)任何邏輯

    // 一般會重載該方法, 以實現(xiàn)對視圖中元素的渲染

    render : function() {

      // 返回當前視圖對象, 以支持方法的鏈式操作

      // 因此如果重載了該方法, 建議在方法最后也返回視圖對象(this)

      return this;

    },

    // 移除當前視圖的$el元素

    remove : function() {

      // 通過調(diào)用jQuery或Zepto的remove方法, 因此在第三方庫中會同時移除該元素綁定的所有事件和數(shù)據(jù)

      this.$el.remove();

      return this;

    },

    // 根據(jù)傳入的標簽名稱, 屬性和內(nèi)容, 創(chuàng)建并返回一個DOM元素

    // 該方法用于在內(nèi)部創(chuàng)建this.el時自動調(diào)用

    make : function(tagName, attributes, content) {

      // 根據(jù)tagName創(chuàng)建元素

      var el = document.createElement(tagName);

      // 設置元素屬性

      if(attributes)

        $(el).attr(attributes);

      // 設置元素內(nèi)容

      if(content)

        $(el).html(content);

      // 返回元素

      
return el;

    },

    // 為視圖對象設置標準的$el及el屬性, 該方法在對象創(chuàng)建時被自動調(diào)用

    // $el是通過jQuery或Zepto創(chuàng)建的對象, el是標準的DOM對象

    setElement : function(element, delegate) {

      // 如果已經(jīng)存在了$el屬性(可能是手動調(diào)用了setElement方法切換視圖的元素), 則取消之前對$el綁定的events事件(詳細參考undelegateEvents方法)

      if(this.$el)

        this.undelegateEvents();

      // 將元素創(chuàng)建為jQuery或Zepto對象, 并存放在$el屬性中

      this.$el = ( element instanceof $) ? element : $(element);

      // this.el存放標準的DOM對象

      this.el = this.$el[0];

      // 如果設置了delegate參數(shù), 則為元素綁定視圖中events參數(shù)設置的事件

      // 在視圖類的構造函數(shù)中, 已經(jīng)調(diào)用了delegateEvents方法進行綁定, 因此在初始化的_ensureElement方法中調(diào)用setElement方法時沒有傳遞delegate參數(shù)

      // 在手動調(diào)用setElemen方法設置視圖元素時, 允許傳遞delegate綁定事件

      if(delegate !== false)

        this.delegateEvents();

      return this;

    },

    // 為視圖元素綁定事件

    // events參數(shù)配置了需要綁定事件的集合, 格式如('事件名稱 元素選擇表達式' : '事件方法名稱/或事件函數(shù)'):

    // {

    //   'click #title': 'edit',

    //   'click .save': 'save'

    //   'click span': function() {}

    // }

    // 該方法在視圖對象初始化時會被自動調(diào)用, 并將對象中的events屬性作為events參數(shù)(事件集合)

    delegateEvents : function(events) {

      // 如果沒有手動傳遞events參數(shù), 則從視圖對象獲取events屬性作為事件集合

      if(!(events || ( events = getValue(this, 'events'))))

        return;

      // 取消當前已經(jīng)綁定過的events事件

      this.undelegateEvents();

      // 遍歷需要綁定的事件列表

      for(var key in events) {

        // 獲取需要綁定的方法(允許是方法名稱或函數(shù))

        var method = events[key];

        // 如果是方法名稱, 則從對象中獲取該函數(shù)對象, 因此該方法名稱必須是視圖對象中已定義的方法

        if(!_.isFunction(method))

          method = this[events[key]];

        // 對無效的方法拋出一個錯誤

        if(!method)

          throw new Error('Method "' + events[key] + '" does not exist');

        // 解析事件表達式(key), 從表達式中解析出事件的名字和需要操作的元素

        // 例如 'click #title'將被解析為 'click' 和 '#title' 兩部分, 均存放在match數(shù)組中

        var match = key.match(delegateEventSplitter);

        // eventName為解析后的事件名稱

        // selector為解析后的事件元素選擇器表達式

        var eventName = match[1], selector = match[2];

        // bind方法是Underscore中用于綁定函數(shù)上下文的方法

        // 這里將method事件方法的上下文綁定到當前視圖對象, 因此在事件被觸發(fā)后, 事件方法中的this始終指向視圖對象本身

        method = _.bind(method, this);

        // 設置事件名稱, 在事件名稱后追加標識, 用于傳遞給jQuery或Zepto的事件綁定方法

        eventName += '.delegateEvents' + this.cid;

        // 通過jQuery或Zepto綁定事件

        if(selector === '') {

          // 如果沒有設置子元素選擇器, 則通過bind方法將事件和方法綁定到當前$el元素本身

          this.$el.bind(eventName, method);

        } else {

          // 如果當前設置了子元素選擇器表達式, 則通過delegate方式綁定

          // 該方法將查找當前$el元素下的子元素, 并將于selector表達式匹配的元素進行事件綁定

          // 如果該選擇器的元素不屬于當前$el的子元素, 則事件綁定無效

          this.$el.delegate(selector, eventName, method);

        }

      }

    },

    // 取消視圖中當前元素綁定的events事件, 該方法一般不會被使用

    // 除非調(diào)用delegateEvents方法重新為視圖中的元素綁定事件, 在重新綁定之前會清除當前的事件

    // 或通過setElement方法重新設置試圖的el元素, 也會清除當前元素的事件

    undelegateEvents : function() {

      this.$el.unbind('.delegateEvents' + this.cid);

    },

    // 在實例化視圖對象時設置初始配置

    // 將傳遞的配置覆蓋到對象的options中

    // 將配置中與viewOptions列表相同的配置復制到對象本身, 作為對象的屬性

    _configure : function(options) {

      // 如果對象本身設置了默認配置, 則使用傳遞的配置進行合并

      if(this.options)

        options = _.extend({}, this.options, options);

      // 遍歷viewOptions列表

      for(var i = 0, l = viewOptions.length; i < l; i++) {

        // attr依次為viewOptions中的屬性名

        var attr = viewOptions[i];

        // 將options配置中與viewOptions相同的配置復制到對象本身, 作為對象的屬性

        if(options[attr])

          this[attr] = options[attr];

      }

      // 設置對象的options配置

      this.options = options;

    },

    // 每一個視圖對象都應該有一個el元素, 作為渲染的元素

    // 在構造視圖時, 可以設置對象的el屬性來指定一個元素

    // 如果設置的el是一個字符串或DOM對象, 則通過$方法將其創(chuàng)建為一個jQuery或Zepto對象

    // 如果沒有設置el屬性, 則根據(jù)傳遞的tagName, id和className, 調(diào)用mak方法創(chuàng)建一個元素

    // (新創(chuàng)建的元素不會被添加到文檔樹中, 而始終存儲在內(nèi)存, 當處理完畢需要渲染到頁面時, 一般會在重寫的render方法, 或自定義方法中, 訪問this.el將其追加到文檔)

    // (如果我們需要向頁面添加一個目前還沒有的元素, 并且需要為其添加一些子元素, 屬性, 樣式或事件時, 可以通過該方式先將元素創(chuàng)建到內(nèi)存, 在完成所有操作之后再手動渲染到文檔, 可以提高渲染效率)

    _ensureElement : function() {

      // 如果沒有設置el屬性, 則創(chuàng)建默認元素

      if(!this.el) {

        // 從對象獲取attributes屬性, 作為新創(chuàng)建元素的默認屬性列表

        var attrs = getValue(this, 'attributes') || {};

        // 設置新元素的id

        if(this.id)

          attrs.id = this.id;

        // 設置新元素的class

        if(this.className)

          attrs['class'] = this.className;

        // 通過make方法創(chuàng)建元素, 并調(diào)用setElement方法將元素設置為視圖所使用的標準元素

        this.setElement(this.make(this.tagName, attrs), false);

      } else {

        // 如果設置了el屬性, 則直接調(diào)用setElement方法將el元素設置為視圖的標準元素

        this.setElement(this.el, false);

      }

    }

  });

 

  // 實現(xiàn)對象繼承的函數(shù), 該函數(shù)內(nèi)部使用inherits實現(xiàn)繼承, 請參考inherits函數(shù)

  var extend = function(protoProps, classProps) {

    // child存儲已經(jīng)實現(xiàn)繼承自當前類的子類(Function)

    // protoProps設置子類原型鏈中的屬性

    // classProps設置子類的靜態(tài)屬性

    var child = inherits(this, protoProps, classProps);

    // 將extend函數(shù)添加到子類, 因此調(diào)用子類的extend方法便可實現(xiàn)對子類的繼承

    child.extend = this.extend;

    // 返回實現(xiàn)繼承的子類

    return child;

  };

  // 為Model, Collection, Router和View類實現(xiàn)繼承機制

  Model.extend = Collection.extend = Router.extend = View.extend = extend;

 

  // Backbone.sync 與服務器異步交互相關

  // -------------

 

  // 定義Backbone中與服務器交互方法和請求type的對應關系

  var methodMap = {

    'create' : 'POST',

    'update' : 'PUT',

    'delete' : 'DELETE',

    'read' : 'GET'

  };

 

  // sync用于在Backbone中操作數(shù)據(jù)時, 向服務器發(fā)送請求同步數(shù)據(jù)狀態(tài), 以建立與服務器之間的無縫連接

  // sync發(fā)送默認通過第三方庫(jQuery, Zepto等) $.ajax方法發(fā)送請求, 因此如果要調(diào)用狀態(tài)同步相關的方法, 需要第三方庫支持

  // Backbone默認定義了一套與服務器交互的數(shù)據(jù)格式(JSON)和結構, 服務器響應的數(shù)據(jù)應該遵循該約定

  // 如果數(shù)據(jù)不需要保存在服務器, 或與服務器交互方法, 數(shù)據(jù)格式結構與約定不一致, 可以通過重載sync方法實現(xiàn)

  // @param {String} method 在Backbone中執(zhí)行的CRUD操作名稱

  // @param {Model Obejct} model 需要與服務器同步狀態(tài)的模型對象

  // @param {Object} options

  Backbone.sync = function(method, model, options) {

    // 根據(jù)CRUD方法名定義與服務器交互的方法(POST, GET, PUT, DELETE)

    var type = methodMap[method];

 

    // options默認為一個空對象

    options || ( options = {});

 

    // params將作為請求參數(shù)對象傳遞給第三方庫的$.ajax方法

    var params = {

      // 請求類型

      type : type,

      // 數(shù)據(jù)格式默認為json

      dataType : 'json'

    };

 

    // 如果在發(fā)送請求時沒有在options中設置url地址, 將會通過模型對象的url屬性或方法來獲取url

    // 模型所獲取url的方式可參考模型的url方法

    if(!options.url) {

      // 獲取請求地址失敗時會調(diào)用urlError方法拋出一個錯誤

      params.url = getValue(model, 'url') || urlError();

    }

 

    // 如果調(diào)用create和update方法, 且沒有在options中定義請求數(shù)據(jù), 將序列化模型中的數(shù)據(jù)對象傳遞給服務器

    if(!options.data && model && (method == 'create' || method == 'update')) {

      // 定義請求的Content-Type頭, 默認為application/json

      params.contentType = 'application/json';

      // 序列化模型中的數(shù)據(jù), 并作為請求數(shù)據(jù)傳遞給服務器

      params.data = JSON.stringify(model.toJSON());

    }

 

    // 對于不支持application/json編碼的瀏覽器, 可以通過設置Backbone.emulateJSON參數(shù)為true實現(xiàn)兼容

    if(Backbone.emulateJSON) {

      // 不支持Backbone.emulateJSON編碼的瀏覽器, 將類型設置為application/x-www-form-urlencoded

      params.contentType = 'application/x-www-form-urlencoded';

      // 將需要同步的數(shù)據(jù)存放在key為"model"參數(shù)中發(fā)送到服務器

      params.data = params.data ? {

        model : params.data

      } : {};

    }

 

    // 對于不支持REST方式的瀏覽器, 可以設置Backbone.emulateHTTP參數(shù)為true, 以POST方式發(fā)送數(shù)據(jù), 并在數(shù)據(jù)中加入_method參數(shù)標識操作名稱

    // 同時也將發(fā)送X-HTTP-Method-Override頭信息

    if(Backbone.emulateHTTP) {

      // 如果操作類型為PUT或DELETE

      if(type === 'PUT' || type === 'DELETE') {

        // 將操作名稱存放到_method參數(shù)發(fā)送到服務器

        if(Backbone.emulateJSON)

          params.data._method = type;

        // 實際以POST方式進行提交, 并發(fā)送X-HTTP-Method-Override頭信息

        params.type = 'POST';

        params.beforeSend = function(xhr) {

          xhr.setRequestHeader('X-HTTP-Method-Override', type);

        };

      }

    }

 

    // 對非GET方式的請求, 將不對數(shù)據(jù)進行轉換, 因為傳遞的數(shù)據(jù)可能是一個JSON映射

    if(params.type !== 'GET' && !Backbone.emulateJSON) {

      // 通過設置processData為false來關閉數(shù)據(jù)轉換

      // processData參數(shù)是$.ajax方法中的配置參數(shù), 詳細信息可參考jQuery或Zepto相關文檔

      params.processData = false;

    }

 

    // 通過第三方庫的$.ajax方法向服務器發(fā)送請求同步數(shù)據(jù)狀態(tài)

    // 傳遞給$.ajax方法的參數(shù)使用extend方法將options對象中的參數(shù)覆蓋到了params對象, 因此在調(diào)用sync方法時設置了與params同名的options參數(shù), 將以options為準

    return $.ajax(_.extend(params, options));

  };

  // 包裝一個統(tǒng)一的模型錯誤處理方法, 會在模型與服務器交互發(fā)生錯誤時被調(diào)用

  // onError是在調(diào)用與服務器的交互方法時(如fetch, destory等), options中指定的自定義錯誤處理函數(shù)

  // originalModel是發(fā)生錯誤的模型或集合對象

  Backbone.wrapError = function(onError, originalModel, options) {

    return function(model, resp) {

      resp = model === originalModel ? resp : model;

 

      if(onError) {

        // 如果設置了自定義錯誤處理方法, 則調(diào)用自定義方法

        onError(originalModel, resp, options);

      } else {

        // 默認將觸發(fā)發(fā)生錯誤的模型或集合的error事件

        originalModel.trigger('error', originalModel, resp, options);

      }

    };

  };

  // Helpers 定義一些供Backbone內(nèi)部使用的幫助函數(shù)

  // -------

 

  // ctor是一個共享的空函數(shù), 用于在調(diào)用inherits方法實現(xiàn)繼承時, 承載父類的原型鏈以便設置到子類原型中

  var ctor = function() {

  };

  // 實現(xiàn)OOP繼承特性

  // @param {Function} parent 被繼承的父類Function

  // @param {Object} protoProps 擴展子類原型中的屬性(或方法)對象

  // @param {Object} staticProps 擴展子類的靜態(tài)屬性(或方法)對象

  var inherits = function(parent, protoProps, staticProps) {

    var child;

 

    // 如果在protoProps中指定了"constructor"屬性, 則"constructor"屬性被作為子類的構造函數(shù)

    // 如果沒有指定構造子類構造函數(shù), 則默認調(diào)用父類的構造函數(shù)

    if(protoProps && protoProps.hasOwnProperty('constructor')) {

      // 使用"constructor"屬性指定的子類構造函數(shù)

      child = protoProps.constructor;

    } else {

      // 使用父類的構造函數(shù)

      child = function() {

        parent.apply(this, arguments);

      };

    }

 

    // 將父類中的靜態(tài)屬性復制為子類靜態(tài)屬性

    _.extend(child, parent);

 

    // 將父類原型鏈設置到子類的原型對象中, 子類以此繼承父類原型鏈中的所有屬性

    ctor.prototype = parent.prototype;

    child.prototype = new ctor();

 

    // 將protoProps對象中的屬性復制到子類的原型對象, 子類以此擁有protoProps中的屬性

    if(protoProps)

      _.extend(child.prototype, protoProps);

 

    // 將staticProps對象中的屬性復制到子類的構造函數(shù)本身, 將staticProps中的屬性作為子類的靜態(tài)屬性

    if(staticProps)

      _.extend(child, staticProps);

 

    // 在復制父類原型鏈到子類原型時, 子類原型鏈中的構造函數(shù)已經(jīng)被覆蓋, 因此此處重新設置子類的構造函數(shù)

    child.prototype.constructor = child;

 

    // 如果子類設置了constructor屬性, 則子類構造函數(shù)為constructor指定的函數(shù)

    // 如果需要在子類構造函數(shù)中調(diào)用父類構造函數(shù), 則需要在子類構造函數(shù)中手動調(diào)用父類的構造函數(shù)

    // 此處將子類的__super__屬性指向父類的構造函數(shù), 方便在子類中調(diào)用: 子類.__super__.constructor.call(this);

    child.__super__ = parent.prototype;

 

    // 返回子類

    return child;

  };

  // 獲取對象prop屬性的值, 如果prop屬性是一個函數(shù), 則執(zhí)行并返回該函數(shù)的返回值

  var getValue = function(object, prop) {

    // 如果object為空或object不存在prop屬性, 則返回null

    if(!(object && object[prop]))

      return null;

    // 返回prop屬性值, 如果prop是一個函數(shù), 則執(zhí)行并返回該函數(shù)的返回值

    return _.isFunction(object[prop]) ? object[prop]() : object[prop];

  };

  // 拋出一個Error異常, 在Backbone內(nèi)部會頻繁執(zhí)行, 因此獨立為一個公共函數(shù)

  var urlError = function() {

    throw new Error('A "url" property or function must be specified');

  };

}).call(this);

相關文章

最新評論

亚洲中文精品字幕在线观看| 欧美老妇精品另类不卡片| 十八禁在线观看地址免费| 91精品高清一区二区三区| 少妇一区二区三区久久久| 日本女人一级免费片| 无码国产精品一区二区高潮久久4| 91九色国产porny蝌蚪| 老师让我插进去69AV| 国产一线二线三线的区别在哪| 一区二区麻豆传媒黄片| 亚洲欧美精品综合图片小说| 天天干天天操天天爽天天摸| 啊啊啊视频试看人妻| 精品一区二区三区三区色爱| 成年女人免费播放视频| 亚洲高清国产自产av| 丝袜美腿视频诱惑亚洲无 | 国产亚洲视频在线二区| 久久久久久久久久久久久97| 中国黄片视频一区91| 色天天天天射天天舔| 不卡日韩av在线观看| 欧美 亚洲 另类综合| 亚洲另类在线免费观看| 日韩少妇人妻精品无码专区| 成人伊人精品色xxxx视频| 在线国产精品一区二区三区| 天天干天天操天天爽天天摸| 亚洲精品高清自拍av| 日本少妇人妻xxxxxhd| 好男人视频在线免费观看网站| 91人妻精品一区二区在线看| 五十路老熟女码av| 国产精品自拍在线视频| 亚洲av无女神免非久久| 护士小嫩嫩又紧又爽20p| 欧美日韩情色在线观看| 97人人妻人人澡人人爽人人精品| 亚洲欧美另类手机在线| 久久热这里这里只有精品| 一区二区三区日本伦理| 2021久久免费视频| 91精品国产91久久自产久强| 亚洲另类伦春色综合小| 91麻豆精品91久久久久同性| 中文字幕 人妻精品| 最新91精品视频在线| 欧美韩国日本国产亚洲| 日韩欧美一级aa大片| 日韩人妻xxxxx| 国产麻豆剧传媒精品国产av蜜桃| 中文字幕乱码av资源| 88成人免费av网站| 激情国产小视频在线| 欧美黑人性猛交xxxxⅹooo| 国产精品国产精品一区二区| 粉嫩欧美美人妻小视频| 久久久精品999精品日本| 男女之间激情网午夜在线| 99热久久极品热亚洲| 日韩激情文学在线视频| wwwxxx一级黄色片| 成年人免费看在线视频| 性感美女诱惑福利视频| 日本免费一级黄色录像| 亚洲 欧美 精品 激情 偷拍| 黄色资源视频网站日韩| 亚洲激情,偷拍视频| 久草极品美女视频在线观看| aⅴ精产国品一二三产品| 亚洲高清自偷揄拍自拍| 国产揄拍高清国内精品对白| 青草亚洲视频在线观看| 天堂va蜜桃一区入口| 亚洲 人妻 激情 中文| 欧美另类一区二区视频| 日韩欧美一级aa大片| 国产精品黄色的av| 亚洲高清国产自产av| 视频在线亚洲一区二区| 午夜激情精品福利视频| 沙月文乃人妻侵犯中文字幕在线| 精品suv一区二区69| 中文 成人 在线 视频| 亚洲av男人天堂久久| av手机在线观播放网站| 免费高清自慰一区二区三区网站| 国产乱弄免费视频观看| 免费在线看的黄片视频| 93视频一区二区三区| 一区二区三区的久久的蜜桃的视频| 欧美女同性恋免费a| 亚洲国产香蕉视频在线播放| 亚洲成人国产av在线| 日本啪啪啪啪啪啪啪| 国产又粗又猛又爽又黄的视频在线| 操操网操操伊剧情片中文字幕网| 亚洲视频在线观看高清| 不卡精品视频在线观看| 日韩成人性色生活片| 在线亚洲天堂色播av电影| 天堂资源网av中文字幕| 免费岛国喷水视频在线观看 | 国产午夜亚洲精品麻豆| 大香蕉伊人国产在线| 91极品新人『兔兔』精品新作| 亚洲1区2区3区精华液| 91国内精品自线在拍白富美| 亚洲男人在线天堂网| 沈阳熟妇28厘米大战黑人| 红桃av成人在线观看| 国内资源最丰富的网站| 女人精品内射国产99| 啊慢点鸡巴太大了啊舒服视频| aiss午夜免费视频| 在线免费观看日本片| 天天色天天舔天天射天天爽| 国产V亚洲V天堂无码欠欠| 视频一区 视频二区 视频| 国产1区,2区,3区| 黑人巨大精品欧美视频| 色婷婷精品大在线观看| 午夜毛片不卡免费观看视频| 最新中文字幕免费视频| 国产福利小视频大全| 欧美熟妇一区二区三区仙踪林| 亚洲区欧美区另类最新章节| 久久免费看少妇高潮完整版| 11久久久久久久久久久| 人妻少妇一区二区三区蜜桃| 在线免费观看靠比视频的网站| 国产亚洲视频在线二区| av中文字幕网址在线| 丰满少妇翘臀后进式| 国产精品黄片免费在线观看| 亚洲天堂第一页中文字幕| 黄色黄色黄片78在线| 精品国产乱码一区二区三区乱| 亚洲一级 片内射视正片| 小穴多水久久精品免费看| 韩国男女黄色在线观看| 99精品国自产在线人| av在线shipin| 国产成人午夜精品福利| 日韩中文字幕精品淫| 日本18禁久久久久久| asmr福利视频在线观看| 最新国产精品网址在线观看| 噜噜色噜噜噜久色超碰| 国产欧美精品一区二区高清| 中国黄色av一级片| 91试看福利一分钟| 青青青青操在线观看免费| 国产精品久久综合久久| 在线观看日韩激情视频| 精品老妇女久久9g国产| 亚洲av男人的天堂你懂的| 国产卡一卡二卡三乱码手机| 亚洲国产精品久久久久蜜桃| 99国内精品永久免费视频| 欧美区一区二区三视频| 在线观看av2025| 亚洲偷自拍高清视频| 51精品视频免费在线观看| 日韩精品中文字幕播放| aⅴ精产国品一二三产品| 日本人妻少妇18—xx| 国产精品女邻居小骚货| 熟女妇女老妇一二三区| 精品国产在线手机在线| 欧美3p在线观看一区二区三区| 国产丰满熟女成人视频| 青青青国产片免费观看视频| 亚洲午夜福利中文乱码字幕| 国产极品美女久久久久久| 国产成人综合一区2区| 91久久综合男人天堂| 成人av电影免费版| 美味人妻2在线播放| 亚洲卡1卡2卡三卡四老狼| 91免费观看国产免费| www日韩毛片av| 久久这里有免费精品| 美女骚逼日出水来了| 精品美女福利在线观看| 午夜精品亚洲精品五月色| 天天操天天爽天天干| 中文字幕日韩91人妻在线| rct470中文字幕在线| 在线不卡成人黄色精品| 日本熟女精品一区二区三区| 免费费一级特黄真人片 | 超碰97人人做人人爱| 一区二区三区综合视频| 天天干天天操天天扣| 中文字幕人妻被公上司喝醉在线 | 天天日天天干天天搡| 和邻居少妇愉情中文字幕| 在线视频自拍第三页| 亚洲日本一区二区三区| 视频 一区二区在线观看| 欧美一区二区三区激情啪啪啪| 中文字幕高清免费在线人妻| 晚上一个人看操B片| 九九视频在线精品播放| 成年人午夜黄片视频资源| 国产夫妻视频在线观看免费| 4个黑人操素人视频网站精品91| 亚洲欧美久久久久久久久| 中文字幕乱码人妻电影| 中文字幕在线欧美精品| 亚洲护士一区二区三区| 91精品国产91久久自产久强| 97资源人妻免费在线视频| 中字幕人妻熟女人妻a62v网| 99精品视频之69精品视频 | 国产白嫩美女一区二区| av高潮迭起在线观看| aiss午夜免费视频| 黄色男人的天堂视频| 亚洲精品亚洲人成在线导航| 亚洲精品一区二区三区老狼| 精品久久久久久久久久久99| 在线免费观看靠比视频的网站| mm131美女午夜爽爽爽| 丝袜美腿欧美另类 中文字幕| 女人精品内射国产99| av一本二本在线观看| 亚洲成人国产综合一区| 亚洲午夜伦理视频在线| 亚洲区欧美区另类最新章节| 成人精品视频99第一页| 欧美视频中文一区二区三区| 黄色片一级美女黄色片| 夜鲁夜鲁狠鲁天天在线| 91色秘乱一区二区三区| 麻豆性色视频在线观看| 极品性荡少妇一区二区色欲| 亚洲 欧美 精品 激情 偷拍| 扒开让我视频在线观看| 亚洲午夜电影之麻豆| 蜜臀av久久久久蜜臀av麻豆| av视屏免费在线播放| 绝顶痉挛大潮喷高潮无码 | 亚洲天天干 夜夜操| 欧美亚洲国产成人免费在线 | 国产精品一区二区久久久av| 中文字幕在线一区精品| 韩国男女黄色在线观看| 黑人性生活视频免费看| 日韩美av高清在线| 国产普通话插插视频| 成人国产激情自拍三区| 好吊视频—区二区三区| 98视频精品在线观看| 夜夜嗨av蜜臀av| 91精品视频在线观看免费| 人人妻人人澡欧美91精品| 亚洲精品无码久久久久不卡| 日韩三级电影华丽的外出| 日韩伦理短片在线观看| 亚洲国际青青操综合网站| 三级等保密码要求条款| 国产福利小视频大全| 美女在线观看日本亚洲一区| 性感美女高潮视频久久久| 亚洲精品麻豆免费在线观看 | 国产精品久久久久久久女人18| 日本午夜福利免费视频| 亚洲欧美另类手机在线| 欧美亚洲中文字幕一区二区三区| 一区二区三区在线视频福利| 中文字幕一区二区三区蜜月| 女同互舔一区二区三区| 在线免费观看av日韩| 男女之间激情网午夜在线| av高潮迭起在线观看| 久久艹在线观看视频| 摧残蹂躏av一二三区| 大香蕉福利在线观看| 久久亚洲天堂中文对白| 大香蕉伊人国产在线| 免费在线黄色观看网站| 国产成人小视频在线观看无遮挡| 97黄网站在线观看| 中文字幕人妻熟女在线电影| 2020av天堂网在线观看| 免费黄色成人午夜在线网站| 天天日天天天天天天天天天天 | 精品亚洲中文字幕av| 2020韩国午夜女主播在线| 精品成人啪啪18免费蜜臀| ka0ri在线视频| av网址在线播放大全| 懂色av蜜桃a v| 香蕉av影视在线观看| 日韩视频一区二区免费观看| 日本丰满熟妇大屁股久久| 中文字幕人妻av在线观看| 最近中文2019年在线看| 91精品国产91久久自产久强| 亚洲自拍偷拍精品网| 一区二区三区在线视频福利| 日本中文字幕一二区视频| 免费无毒热热热热热热久| 欧美日本在线视频一区| 美日韩在线视频免费看| 亚洲av色图18p| 亚洲一区二区三区在线高清| 欧美第一页在线免费观看视频| 男人的天堂一区二区在线观看| 好吊操视频这里只有精品| 青青热久免费精品视频在线观看| 99视频精品全部15| 国产在线观看免费人成短视频| 日日摸夜夜添夜夜添毛片性色av| 国产九色91在线视频| 亚洲欧美精品综合图片小说| 啊啊啊视频试看人妻| 日韩激情文学在线视频| 亚洲一区二区三区精品乱码| yy96视频在线观看| 国产精品一二三不卡带免费视频| 天堂中文字幕翔田av| xxx日本hd高清| 福利一二三在线视频观看| 亚洲另类在线免费观看| 亚洲1区2区3区精华液| 夜夜骑夜夜操夜夜奸| 少妇与子乱在线观看| 2018最新中文字幕在线观看| 888欧美视频在线| 亚洲推理片免费看网站| 中英文字幕av一区| 区一区二区三国产中文字幕| 国产精品成人xxxx| 91在线免费观看成人| 女生被男生插的视频网站| 国产变态另类在线观看| 国产在线观看免费人成短视频| 欧美黑人性猛交xxxxⅹooo| av黄色成人在线观看| 在线观看av观看av| 欧美日本在线观看一区二区| 中文字幕高清在线免费播放| 日韩欧美国产精品91| 日韩精品二区一区久久| 91免费观看在线网站| 亚洲综合图片20p| 亚洲成人激情视频免费观看了 | 99热这里只有精品中文| 激情色图一区二区三区| 国产又粗又黄又硬又爽| 一区二区三区欧美日韩高清播放| 国产欧美精品免费观看视频| 久久热久久视频在线观看| 亚洲欧美激情国产综合久久久| 亚洲免费va在线播放| 中国无遮挡白丝袜二区精品| 国产极品美女久久久久久| 日韩欧美高清免费在线| 搡老妇人老女人老熟女| 日本黄色特一级视频| 岛国黄色大片在线观看| 亚洲一区二区三区偷拍女厕91| 日本午夜福利免费视频| 北条麻妃av在线免费观看| 91久久精品色伊人6882| 中文字幕在线视频一区二区三区 | 日本五十路熟新垣里子| 香蕉aⅴ一区二区三区| 天天日天天干天天干天天日| 香港三日本三韩国三欧美三级| 在线免费观看日本片| 亚洲高清国产拍青青草原| 日本午夜爽爽爽爽爽视频在线观看| 亚洲国产香蕉视频在线播放| 免费看国产又粗又猛又爽又黄视频| 日本免费视频午夜福利视频| 亚洲精品成人网久久久久久小说| 亚洲成人免费看电影| 国产卡一卡二卡三乱码手机| 91社福利《在线观看| 4个黑人操素人视频网站精品91| 亚洲熟女女同志女同| 黄色片黄色片wyaa| 自拍偷拍日韩欧美一区二区| 色综合久久无码中文字幕波多| 亚洲欧美一卡二卡三卡| 成人免费做爰高潮视频| 麻豆精品成人免费视频| 日本熟女精品一区二区三区| 粉嫩av懂色av蜜臀av | 亚洲在线免费h观看网站| 国产在线拍揄自揄视频网站| 亚洲最大黄了色网站| 中文字幕在线第一页成人| 在线免费观看日本伦理| 亚洲精品精品国产综合| 91国语爽死我了不卡| 成人性爱在线看四区| 国产女人叫床高潮大片视频| 岳太深了紧紧的中文字幕| 亚洲av日韩精品久久久久久hd| av线天堂在线观看| 亚洲公开视频在线观看| 亚洲最大黄了色网站| 亚洲成人黄色一区二区三区| 久青青草视频手机在线免费观看| 国产精品sm调教视频| 伊人综合免费在线视频| 2022天天干天天操| 97人人妻人人澡人人爽人人精品| 红桃av成人在线观看| 在线国产精品一区二区三区| 性色蜜臀av一区二区三区| 欧美精品国产综合久久| 青青草国内在线视频精选| 国产免费av一区二区凹凸四季| sspd152中文字幕在线| 久久久久久性虐视频| 亚洲国产美女一区二区三区软件| 97瑟瑟超碰在线香蕉| 中文字幕AV在线免费看 | 欧美一区二区三区四区性视频| 精品成人啪啪18免费蜜臀| av中文字幕在线观看第三页| 亚洲精品国产在线电影| 欧洲精品第一页欧洲精品亚洲| 老司机福利精品免费视频一区二区 | 天天日天天干天天爱| 香蕉片在线观看av| 天天干天天操天天插天天日| 日本美女成人在线视频| www日韩a级s片av| 天天日夜夜操天天摸| 青青青艹视频在线观看| 男人的网址你懂的亚洲欧洲av| 中文字幕亚洲久久久| 国产黄色大片在线免费播放| 国产午夜男女爽爽爽爽爽视频| 伊人综合免费在线视频| 国产麻豆91在线视频| 9色在线视频免费观看| 精品国产污污免费网站入口自| 日韩欧美在线观看不卡一区二区| 2018最新中文字幕在线观看| 国产亚洲成人免费在线观看| 日日夜夜狠狠干视频| 91久久人澡人人添人人爽乱| 男女啪啪视频免费在线观看| 3344免费偷拍视频| 扒开让我视频在线观看| 黄色成年网站午夜在线观看| 精品一线二线三线日本| 爆乳骚货内射骚货内射在线| 日本三极片中文字幕| 免费高清自慰一区二区三区网站 | AV天堂一区二区免费试看| 护士特殊服务久久久久久久| 成年人该看的视频黄免费| 深田咏美亚洲一区二区| 日韩av熟妇在线观看| 日比视频老公慢点好舒服啊| 亚洲一区二区三区久久午夜| 亚洲av日韩精品久久久| 日韩欧美一级aa大片| 在线观看免费视频色97| 国产亚洲成人免费在线观看| 黄色男人的天堂视频| 久久永久免费精品人妻专区| 精品国产成人亚洲午夜| 99热这里只有国产精品6| 香蕉91一区二区三区| 亚洲欧美色一区二区| 国产精品久久久久久久久福交| 国产精品久久久久久美女校花| 91久久国产成人免费网站| 日本熟妇色熟妇在线观看| 777奇米久久精品一区| 日本性感美女写真视频| 99热99这里精品6国产| 免费一级黄色av网站| 视频久久久久久久人妻| 神马午夜在线观看视频| 欧美成人小视频在线免费看| 自拍偷拍一区二区三区图片| 国产一区二区火爆视频 | 青青青青青免费视频| 午夜精品一区二区三区更新| 欧美亚洲少妇福利视频| 天天射夜夜操综合网| 揄拍成人国产精品免费看视频| 91快播视频在线观看| 日本在线不卡免费视频| 少妇高潮无套内谢麻豆| 国产亚洲天堂天天一区| 2020国产在线不卡视频| 18禁网站一区二区三区四区| 亚洲视频在线观看高清| 久草福利电影在线观看| 色伦色伦777国产精品| 99热这里只有国产精品6| 99热久久极品热亚洲| 久久久久只精品国产三级| 五月天中文字幕内射| 亚洲一区二区三区久久受 | 免费在线看的黄网站| v888av在线观看视频| 青青青青操在线观看免费| 伊人情人综合成人久久网小说| 亚洲国产精品免费在线观看| 欧美精品伦理三区四区 | 精品日产卡一卡二卡国色天香 | 国产精品自拍偷拍a| av在线资源中文字幕| 青青青国产免费视频| 国产午夜激情福利小视频在线| 国产福利小视频大全| 2018最新中文字幕在线观看| 99久久99一区二区三区| 国产麻豆91在线视频| 亚洲精品ww久久久久久| 大鸡吧插入女阴道黄色片| 亚洲一级美女啪啪啪| 99热99这里精品6国产| 黑人变态深video特大巨大| 少妇人妻久久久久视频黄片| 欧美在线一二三视频| 大香蕉大香蕉大香蕉大香蕉大香蕉| 天天干天天爱天天色| 亚洲一区二区人妻av| 晚上一个人看操B片| 欧美日本在线视频一区| 亚洲中文字字幕乱码| ka0ri在线视频| 青草久久视频在线观看| 欧美精品亚洲精品日韩在线| av手机在线观播放网站| 亚洲国产成人无码麻豆艾秋| 成人精品在线观看视频| 人妻少妇精品久久久久久| 粉嫩av蜜乳av蜜臀| av在线播放国产不卡| 日本xx片在线观看| 欧美第一页在线免费观看视频| 天天日天天干天天插舔舔| 四虎永久在线精品免费区二区| 国产视频精品资源网站| 巨乳人妻日下部加奈被邻居中出| 天天日天天爽天天爽| 久久久久久久久久久久久97| 亚洲超碰97人人做人人爱| 99精品国产aⅴ在线观看| 人妻久久无码中文成人| 国产成人自拍视频播放| 精品久久久久久久久久中文蒉| 久久午夜夜伦痒痒想咳嗽P| 日日日日日日日日夜夜夜夜夜夜| 狠狠地躁夜夜躁日日躁| 亚洲激情,偷拍视频| av俺也去在线播放| 久久久久久性虐视频| 色婷婷久久久久swag精品| 日本午夜爽爽爽爽爽视频在线观看| 国产91久久精品一区二区字幕 | 欧亚日韩一区二区三区观看视频| 免费一级特黄特色大片在线观看| 100%美女蜜桃视频| 亚洲 中文 自拍 另类 欧美| 动漫黑丝美女的鸡巴| 嫩草aⅴ一区二区三区| 人妻丝袜榨强中文字幕| 国产日韩欧美视频在线导航| aⅴ精产国品一二三产品| 丝袜国产专区在线观看| 快插进小逼里大鸡吧视频| 男大肉棒猛烈插女免费视频 | 国产麻豆剧果冻传媒app| 成人蜜臀午夜久久一区| 在线观看911精品国产 | 毛茸茸的大外阴中国视频| 日韩午夜福利精品试看| 男大肉棒猛烈插女免费视频| 果冻传媒av一区二区三区 | 男大肉棒猛烈插女免费视频| 91色秘乱一区二区三区| 色天天天天射天天舔| 国产精品一二三不卡带免费视频| 5528327男人天堂| 日韩三级电影华丽的外出| 久精品人妻一区二区三区| 天天干天天日天天谢综合156| 不卡一不卡二不卡三| 国产激情av网站在线观看| 亚洲人妻国产精品综合| heyzo蜜桃熟女人妻| 大陆胖女人与丈夫操b国语高清| 日本午夜福利免费视频| 国产妇女自拍区在线观看| 亚洲欧洲一区二区在线观看| 激情啪啪啪啪一区二区三区| 成年人午夜黄片视频资源| 色婷婷精品大在线观看| 国产真实乱子伦a视频| 日本啪啪啪啪啪啪啪| 欧美性受xx黑人性猛交| 日本一道二三区视频久久| 第一福利视频在线观看| 18禁美女无遮挡免费| 欧美精品激情在线最新观看视频| 亚洲一区二区三区av网站| 欧美一区二区三区高清不卡tv | 亚洲国产美女一区二区三区软件| 国产91久久精品一区二区字幕| 大陆胖女人与丈夫操b国语高清| 天天日天天鲁天天操| 大陆胖女人与丈夫操b国语高清| 亚洲国产最大av综合| 国产精品成人xxxx| 最新的中文字幕 亚洲| 91精品国产观看免费| 高清成人av一区三区| 日本韩国在线观看一区二区| av在线免费观看亚洲天堂| 欧美在线一二三视频| 国产1区,2区,3区| 不卡一区一区三区在线| 狠狠地躁夜夜躁日日躁| 免费av岛国天堂网站| 亚洲中文精品字幕在线观看| 色天天天天射天天舔| 亚洲精品ww久久久久久| 五月天中文字幕内射| 天天日天天玩天天摸| 国产精品国色综合久久| 欧美成人一二三在线网| 天天插天天狠天天操| 天天操天天弄天天射| 免费在线播放a级片| 四川五十路熟女av| 日本成人不卡一区二区| 天天干天天日天天谢综合156| 精品av国产一区二区三区四区 | 亚洲一区二区三区五区| 精品国产污污免费网站入口自| 欧美80老妇人性视频| 日韩精品一区二区三区在线播放| 成人影片高清在线观看| 2018在线福利视频| 操人妻嗷嗷叫视频一区二区| 狍和女人的王色毛片| 小穴多水久久精品免费看| 日本少妇高清视频xxxxx| 国产精品福利小视频a| 亚洲欧美一区二区三区电影| 中国产一级黄片免费视频播放| 天天通天天透天天插| 中文字幕在线观看极品视频| 黄色片一级美女黄色片| 成人乱码一区二区三区av| 沙月文乃人妻侵犯中文字幕在线 | 日噜噜噜夜夜噜噜噜天天噜噜噜| mm131美女午夜爽爽爽| 欧美激情电影免费在线| 日韩少妇人妻精品无码专区| 欧美精品黑人性xxxx| 天码人妻一区二区三区在线看| 亚洲综合一区成人在线| chinese国产盗摄一区二区| 免费费一级特黄真人片 | 中文字幕人妻三级在线观看| 精品人人人妻人人玩日产欧| 欧美综合婷婷欧美综合| 99re6热在线精品| 免费岛国喷水视频在线观看 | 久久99久久99精品影院| 中文字幕第1页av一天堂网| 91老熟女连续高潮对白| 揄拍成人国产精品免费看视频 | 99人妻视频免费在线| 97人人妻人人澡人人爽人人精品| free性日本少妇| 中文字幕成人日韩欧美| 欧美3p在线观看一区二区三区| 国产高清女主播在线| 插小穴高清无码中文字幕| 伊人情人综合成人久久网小说| 国产精品亚洲在线观看| 中文字幕,亚洲人妻| 亚洲熟女久久久36d| 天天躁日日躁狠狠躁av麻豆| 丝袜肉丝一区二区三区四区在线| 成年午夜免费无码区| 啊用力插好舒服视频| 日本午夜爽爽爽爽爽视频在线观看 | 黄色黄色黄片78在线| 日本乱人一区二区三区| 操操网操操伊剧情片中文字幕网| 在线免费观看日本片| 99久久99久国产黄毛片| 2018在线福利视频| 伊人成人在线综合网| 亚洲日产av一区二区在线| 五十路人妻熟女av一区二区| 成人av电影免费版| 日韩加勒比东京热二区| 欧美一区二区三区高清不卡tv| 国产精品久久久久久久女人18| 欧美在线偷拍视频免费看| 免费费一级特黄真人片| 日韩北条麻妃一区在线| av天堂中文字幕最新| 国产之丝袜脚在线一区二区三区| 亚洲av日韩高清hd| 久久久久91精品推荐99| 黄色在线观看免费观看在线| 快插进小逼里大鸡吧视频| 色婷婷六月亚洲综合香蕉| 日韩一区二区电国产精品| 日韩欧美一级精品在线观看| 人妻少妇av在线观看| 成人蜜桃美臀九一一区二区三区| 97国产在线观看高清| 亚洲一区二区三区偷拍女厕91 | 成人动漫大肉棒插进去视频| 午夜91一区二区三区| 91国语爽死我了不卡| 97国产福利小视频合集| 巨乳人妻日下部加奈被邻居中出| 超碰中文字幕免费观看| 日本福利午夜电影在线观看| 亚洲粉嫩av一区二区三区| 女人精品内射国产99| 亚洲一级美女啪啪啪| 成人动漫大肉棒插进去视频| 国产女人叫床高潮大片视频| 亚洲欧美一卡二卡三卡| 激情啪啪啪啪一区二区三区 | 亚洲高清视频在线不卡| 大香蕉大香蕉在线看| 92福利视频午夜1000看 | 免费看高清av的网站| 亚洲av成人免费网站| 91国内视频在线观看| 亚洲国产在线精品国偷产拍| 蜜臀av久久久久蜜臀av麻豆| 哥哥姐姐综合激情小说| 一区二区三区日本伦理| 日本熟妇喷水xxx| 免费在线观看污污视频网站| 韩国爱爱视频中文字幕| 涩爱综合久久五月蜜臀| 久久99久久99精品影院| 亚洲国产欧美一区二区三区久久 | 日韩精品一区二区三区在线播放 | 玩弄人妻熟妇性色av少妇| 不卡日韩av在线观看| 在线观看av2025| 日韩欧美国产精品91| 91传媒一区二区三区| 丝袜国产专区在线观看| 久久久久久久精品成人热| 欧美精品资源在线观看| 99视频精品全部15| 一区二区在线视频中文字幕 | 在线新三级黄伊人网| 久久香蕉国产免费天天| 欧美精品资源在线观看| 国产熟妇一区二区三区av| 国产高清97在线观看视频| av在线免费资源站| 91免费放福利在线观看| 中文字幕综合一区二区| 91九色porny蝌蚪国产成人| 亚洲一区二区久久久人妻| 国产乱子伦一二三区| 日本啪啪啪啪啪啪啪| 日本在线不卡免费视频| 亚洲精品国偷自产在线观看蜜桃| 久久这里只有精品热视频| 在线播放 日韩 av| 亚洲欧美激情国产综合久久久 | 日本性感美女写真视频| 亚洲高清自偷揄拍自拍| 久久久久久久亚洲午夜综合福利| 欧美视频一区免费在线| 黑人性生活视频免费看| 狠狠躁夜夜躁人人爽天天久天啪| 欧美性受xx黑人性猛交| 天天日天天玩天天摸| 欧美天堂av无线av欧美| 又粗又长 明星操逼小视频| 天天干天天操天天插天天日| www久久久久久久久久久| 欧美专区第八页一区在线播放| 新婚人妻聚会被中出| 国产97视频在线精品| 天天日天天做天天日天天做| 都市家庭人妻激情自拍视频| 老司机免费视频网站在线看| sspd152中文字幕在线| 亚洲精品欧美日韩在线播放| 亚洲美女自偷自拍11页| 亚洲男人的天堂a在线| 98精产国品一二三产区区别| 搞黄色在线免费观看| 一区二区三区四区视频在线播放| 欧美精品中文字幕久久二区| 2018在线福利视频| 亚洲欧美激情人妻偷拍| 亚洲偷自拍高清视频| 国产欧美日韩在线观看不卡| 国产福利小视频二区| 晚上一个人看操B片| 国产白袜脚足J棉袜在线观看| 999热精品视频在线| 青青草原网站在线观看| 新婚人妻聚会被中出| 精品黑人巨大在线一区| 75国产综合在线视频| 九色porny九色9l自拍视频| 国产av欧美精品高潮网站| 国产成人自拍视频播放| 大白屁股精品视频国产| chinese国产盗摄一区二区| 欧美偷拍亚洲一区二区| 香港三日本三韩国三欧美三级| 亚洲av黄色在线网站| 大香蕉福利在线观看| 青青草在观免费国产精品| 久久久久久cao我的性感人妻| 天天做天天干天天操天天射| 欲乱人妻少妇在线视频裸| 国产91久久精品一区二区字幕| 又色又爽又黄的美女裸体| 亚洲国产欧美一区二区三区久久| av一区二区三区人妻| 含骚鸡巴玩逼逼视频| 天天日天天爽天天爽| 老司机免费福利视频网| 91麻豆精品传媒国产黄色片| 九九热99视频在线观看97| 国产熟妇一区二区三区av| 少妇系列一区二区三区视频| 国产成人精品午夜福利训2021| 国产亚洲视频在线观看| 免费看国产又粗又猛又爽又黄视频| 晚上一个人看操B片| 91中文字幕免费在线观看| 天天色天天爱天天爽| 欧美特色aaa大片| 久久精品亚洲成在人线a| 久久99久久99精品影院| 欧美日韩一区二区电影在线观看 | 色呦呦视频在线观看视频| 精品国产污污免费网站入口自| 亚洲欧美成人综合视频| 中文字幕人妻熟女在线电影| 男生舔女生逼逼的视频| 欧美精品黑人性xxxx| 亚洲公开视频在线观看| 国产成人午夜精品福利| 国产精品久久久久久久女人18| 国产精品sm调教视频| 中文字幕,亚洲人妻| 国产熟妇乱妇熟色T区| 激情人妻校园春色亚洲欧美| 在线新三级黄伊人网| 日本三极片视频网站观看| 91久久人澡人人添人人爽乱| 黑人性生活视频免费看| 午夜久久久久久久99| 91大神福利视频网| 欧美香蕉人妻精品一区二区| 久久热久久视频在线观看| 国产一区av澳门在线观看| 精品国产成人亚洲午夜| 亚洲综合另类精品小说| 18禁美女羞羞免费网站| 亚洲欧美综合在线探花| 深夜男人福利在线观看| 亚洲高清国产自产av| 一区二区熟女人妻视频| 天天日天天摸天天爱| 男大肉棒猛烈插女免费视频| 日韩av中文在线免费观看| 岛国毛片视频免费在线观看| 国产九色91在线观看精品| 亚洲国产美女一区二区三区软件 | 国产 在线 免费 精品| 偷拍自拍亚洲美腿丝袜| 视频一区二区三区高清在线| 精品久久久久久久久久中文蒉| 一区二区三区四区视频| 欧美一级片免费在线成人观看 | 天天色天天舔天天射天天爽| 2020久久躁狠狠躁夜夜躁| 亚洲午夜电影之麻豆| 午夜在线一区二区免费| 欧美日韩一级黄片免费观看| 色哟哟国产精品入口| 一区二区三区麻豆福利视频| 国产亚洲精品品视频在线| 欧美精品中文字幕久久二区| 人妻少妇中文有码精品| 91色网站免费在线观看| 97成人免费在线观看网站| 93人妻人人揉人人澡人人| 超pen在线观看视频公开97 | 亚洲专区激情在线观看视频| 久久久久久久久久性潮| 欧美熟妇一区二区三区仙踪林| 在线视频国产欧美日韩| 亚洲一区二区三区在线高清| 国产av国片精品一区二区| 欧洲精品第一页欧洲精品亚洲| 视频在线免费观看你懂得| 久草视频在线看免费| 青青草在观免费国产精品| 亚洲av在线观看尤物| 日本一二三中文字幕| 日韩精品中文字幕福利| 青青青青视频在线播放| 日本在线一区二区不卡视频| 国产va精品免费观看| 91免费观看国产免费| 亚洲精品国产综合久久久久久久久| 久久精品国产999| 欧美亚洲偷拍自拍色图| 在线观看的黄色免费网站| 日韩中文字幕福利av| 老熟妇xxxhd老熟女| 亚洲自拍偷拍综合色| 一区二区久久成人网| aaa久久久久久久久| 天天日天天干天天爱| www,久久久,com| 福利片区一区二体验区| 涩爱综合久久五月蜜臀| 亚洲卡1卡2卡三卡四老狼| 中文字母永久播放1区2区3区| 97超碰最新免费在线观看| 欧美视频一区免费在线| 三级等保密码要求条款| av一区二区三区人妻| 97小视频人妻一区二区| 欧美男同性恋69视频| 色av色婷婷人妻久久久精品高清| 欧美亚洲中文字幕一区二区三区| 青青青青视频在线播放| 国产精品人妻66p| 一区二区三区另类在线| 伊人综合免费在线视频| 国产麻豆剧传媒精品国产av蜜桃| 亚洲中文字幕乱码区| 日本黄在免费看视频| 一个色综合男人天堂| 免费十精品十国产网站| 黄色成年网站午夜在线观看| 2019av在线视频| 沈阳熟妇28厘米大战黑人| 91亚洲国产成人精品性色| 久草视频 久草视频2| 91传媒一区二区三区| 最近中文字幕国产在线| 欲乱人妻少妇在线视频裸| 天天日天天做天天日天天做| 中文字幕在线第一页成人| 无码中文字幕波多野不卡| 女警官打开双腿沦为性奴| 全国亚洲男人的天堂| 天天色天天操天天舔| 性色蜜臀av一区二区三区| 五月激情婷婷久久综合网| 久久久极品久久蜜桃| 日本啪啪啪啪啪啪啪| 免费看国产又粗又猛又爽又黄视频| 亚洲午夜福利中文乱码字幕| 亚洲精品国偷自产在线观看蜜桃| 天天日天天干天天要| 18禁美女羞羞免费网站| 中文字幕人妻一区二区视频| 午夜免费体验区在线观看| 少妇高潮无套内谢麻豆| 成人影片高清在线观看| 人妻久久无码中文成人| 日韩国产乱码中文字幕| 精品人妻每日一部精品| 国产一级麻豆精品免费| 秋霞午夜av福利经典影视| 美味人妻2在线播放| 免费福利av在线一区二区三区| 久久午夜夜伦痒痒想咳嗽P| 亚洲av可乐操首页| 亚洲激情偷拍一区二区| 91九色porny蝌蚪国产成人| av在线资源中文字幕| 女生被男生插的视频网站| 日本丰满熟妇BBXBBXHD| 日本熟妇色熟妇在线观看| 91国产资源在线视频| 亚洲欧美自拍另类图片| 亚洲av男人天堂久久| 91精品激情五月婷婷在线| 自拍偷拍亚洲精品第2页| 亚洲精品中文字幕下载| 青青青青在线视频免费观看| 999久久久久999| 天天日天天干天天搡| weyvv5国产成人精品的视频| 国产成人无码精品久久久电影| 国产女孩喷水在线观看| 亚洲欧美在线视频第一页| 中英文字幕av一区| 日韩成人免费电影二区| 国产黄色大片在线免费播放| 天天躁日日躁狠狠躁躁欧美av| 久精品人妻一区二区三区 | 久久久久久9999久久久久| 自拍偷拍日韩欧美亚洲| 美女少妇亚洲精选av| 成人亚洲精品国产精品| 岛国av高清在线成人在线| 人妻少妇av在线观看| 孕妇奶水仑乱A级毛片免费看| 免费无码人妻日韩精品一区二区| 日韩欧美亚洲熟女人妻| 日本人妻欲求不满中文字幕| 天堂av在线官网中文| 亚洲麻豆一区二区三区| 五十路av熟女松本翔子| 国产普通话插插视频| 自拍偷拍亚洲精品第2页| 日韩美女精品视频在线观看网站| 美日韩在线视频免费看| 日本少妇精品免费视频| 国产品国产三级国产普通话三级| 啊啊啊想要被插进去视频| 第一福利视频在线观看| 黑人变态深video特大巨大| 欧美色婷婷综合在线| 激情色图一区二区三区| 婷婷久久久综合中文字幕| 曰本无码人妻丰满熟妇啪啪| 天天操天天弄天天射| 福利在线视频网址导航| 一区二区三区四区五区性感视频 | 88成人免费av网站| 国产精品人妻66p| 91破解版永久免费| 搡老熟女一区二区在线观看| 麻豆精品成人免费视频| 国产日韩av一区二区在线| 日本精品一区二区三区在线视频。| 日韩精品中文字幕福利| 中文字幕+中文字幕| 国产美女午夜福利久久| 18禁美女无遮挡免费| 亚洲 人妻 激情 中文| 青青操免费日综合视频观看| 精品区一区二区三区四区人妻 | 成年人该看的视频黄免费| 福利在线视频网址导航| 美女福利视频导航网站| 538精品在线观看视频| 精品黑人巨大在线一区| 欧美亚洲中文字幕一区二区三区| 国产av福利网址大全| 免费国产性生活视频| 加勒比视频在线免费观看| 男人插女人视频网站| 久久久久只精品国产三级| 亚洲人妻30pwc| 91chinese在线视频| 亚洲天堂精品久久久| 中文字幕高清资源站| 久久尻中国美女视频| 久草视频在线一区二区三区资源站| 欧美中国日韩久久精品| 91快播视频在线观看| 亚洲自拍偷拍综合色| 最新91精品视频在线| 91人妻精品久久久久久久网站| 孕妇奶水仑乱A级毛片免费看| 国产av自拍偷拍盛宴| 国产福利小视频二区| 黄色大片男人操女人逼| 一区二区三区综合视频| 顶级尤物粉嫩小尤物网站| 亚洲最大黄了色网站| 亚洲视频在线观看高清| 国产高清女主播在线| 少妇人妻久久久久视频黄片| 亚洲国产欧美一区二区三区…| 免费在线播放a级片| 成人免费做爰高潮视频| 岛国av高清在线成人在线| 粉嫩欧美美人妻小视频| 玩弄人妻熟妇性色av少妇| 一区二区三区美女毛片| 久久精品美女免费视频| 中文字幕网站你懂的| 欧美另类z0z变态| 欧美一级片免费在线成人观看 | 欧美老妇精品另类不卡片| 国产卡一卡二卡三乱码手机| 97少妇精品在线观看| 国产真实灌醉下药美女av福利| 国产在线91观看免费观看| 中文亚洲欧美日韩无线码| 中文字幕免费福利视频6| 久久久久91精品推荐99| 精品视频一区二区三区四区五区| 97精品视频在线观看| 午夜久久久久久久99| 国产成人精品久久二区91| 亚洲天堂精品福利成人av| 超级福利视频在线观看| 欧亚乱色一区二区三区| 小穴多水久久精品免费看| av俺也去在线播放| 天天日天天干天天插舔舔| 亚洲精品亚洲人成在线导航| 丝袜亚洲另类欧美变态| 中文字幕之无码色多多| 啪啪啪啪啪啪啪啪av| 福利片区一区二体验区| 一区二区视频在线观看免费观看 | 日本一二三中文字幕| 日本乱人一区二区三区| 国产亚洲成人免费在线观看| 成人H精品动漫在线无码播放| 日韩人妻xxxxx| 日韩一区二区电国产精品| 青青青青青操视频在线观看| 成人蜜桃美臀九一一区二区三区| 性感美女高潮视频久久久| 91色九色porny| 亚洲综合色在线免费观看| av天堂资源最新版在线看| 91av精品视频在线| 一区二区视频视频视频| 亚洲熟女久久久36d| 韩国黄色一级二级三级| 中文字幕av一区在线观看 | 伊人网中文字幕在线视频| 视频 一区二区在线观看| 乱亲女秽乱长久久久| 天堂v男人视频在线观看| 男生舔女生逼逼视频| 欧美一区二区三区在线资源 | 美女张开腿让男生操在线看| 年轻的人妻被夫上司侵犯| 国产激情av网站在线观看| japanese五十路熟女熟妇| 国产av欧美精品高潮网站| 国产精品探花熟女在线观看| 888欧美视频在线| 内射久久久久综合网| 男人操女人的逼免费视频| 2021国产一区二区| 免费无毒热热热热热热久| 青青草视频手机免费在线观看| 91九色国产熟女一区二区| 色哟哟在线网站入口| 男生用鸡操女生视频动漫| 伊人精品福利综合导航| 黄色在线观看免费观看在线 | 18禁精品网站久久| 好吊操视频这里只有精品| 98视频精品在线观看| 性感美女福利视频网站| av天堂资源最新版在线看| 亚洲成人三级在线播放| 亚洲欧美一区二区三区电影| 欧美一区二区三区乱码在线播放| 鸡巴操逼一级黄色气| weyvv5国产成人精品的视频| 日本18禁久久久久久| 国产欧美精品一区二区高清| 国产V亚洲V天堂无码欠欠| 国产午夜无码福利在线看| av视网站在线观看| 888欧美视频在线| 91精品国产黑色丝袜| 无码中文字幕波多野不卡| 国产午夜无码福利在线看| 国产一区二区火爆视频 | 欧美女同性恋免费a| 福利在线视频网址导航| 狍和女人的王色毛片| av乱码一区二区三区| 黑人乱偷人妻中文字幕| 日韩欧美国产精品91| 日本女人一级免费片| 大屁股熟女一区二区三区| 韩国黄色一级二级三级| 午夜在线观看一区视频| 天天日天天鲁天天操| 欧美专区第八页一区在线播放| 日韩欧美在线观看不卡一区二区| 中文字幕在线视频一区二区三区| 欧美国品一二三产区区别| 精品国产在线手机在线| 久久这里只有精品热视频| 国产在线91观看免费观看| 视频久久久久久久人妻| 婷婷久久久综合中文字幕| 欧美成人精品欧美一级黄色| 成人av久久精品一区二区| 国产性感美女福利视频| 精品乱子伦一区二区三区免费播 | av天堂中文字幕最新| 日韩av中文在线免费观看| 阴茎插到阴道里面的视频| 国产视频一区二区午夜| 亚洲 中文 自拍 另类 欧美| 日日摸夜夜添夜夜添毛片性色av| 一区二区视频在线观看视频在线| 成人H精品动漫在线无码播放| 天天做天天干天天操天天射| 999九九久久久精品| av天堂加勒比在线| 午夜福利资源综合激情午夜福利资 | 国产妇女自拍区在线观看| 久久这里只有精品热视频| 换爱交换乱高清大片| 国产成人自拍视频播放| 人妻熟女中文字幕aⅴ在线 | 蝴蝶伊人久久中文娱乐网| 中文字母永久播放1区2区3区| 狠狠操狠狠操免费视频| 91九色国产porny蝌蚪| 人妻少妇av在线观看| 激情五月婷婷综合色啪| 人妻熟女中文字幕aⅴ在线 | 一级黄片大鸡巴插入美女| 精品亚洲国产中文自在线| 欲满人妻中文字幕在线| 日本18禁久久久久久| 99re国产在线精品| av中文字幕网址在线| 插小穴高清无码中文字幕 | 日本一二三中文字幕| 中文字幕AV在线免费看 | 亚洲2021av天堂| 国产视频一区二区午夜| 亚洲色偷偷综合亚洲AV伊人| 天堂av狠狠操蜜桃| 夜夜骑夜夜操夜夜奸| 制丝袜业一区二区三区| 精品乱子伦一区二区三区免费播 | 99精品国产免费久久| 在线亚洲天堂色播av电影| 日本熟女精品一区二区三区| 888欧美视频在线| 真实国模和老外性视频| sspd152中文字幕在线| 无码国产精品一区二区高潮久久4| 日韩亚洲高清在线观看| 最新中文字幕乱码在线| 97青青青手机在线视频| 亚洲在线观看中文字幕av| 久久精品亚洲国产av香蕉| 青青伊人一精品视频| 中国把吊插入阴蒂的视频| 日本18禁久久久久久| 青娱乐最新视频在线| 国产黄色大片在线免费播放| 韩国爱爱视频中文字幕| 一区二区三区蜜臀在线| 久久美欧人妻少妇一区二区三区 | 成年人该看的视频黄免费| 亚洲卡1卡2卡三卡四老狼| 91av中文视频在线| 精品少妇一二三视频在线| 天堂av在线播放免费| 美日韩在线视频免费看| 亚洲欧美一区二区三区爱爱动图 | 国内精品在线播放第一页| 午夜精品一区二区三区4| av在线shipin| 三级黄色亚洲成人av| 久久久超爽一二三av| 午夜精品亚洲精品五月色| 风流唐伯虎电视剧在线观看 | 啪啪啪啪啪啪啪啪啪啪黄色| 99精品一区二区三区的区| 亚洲国产成人av在线一区| 久草视频在线一区二区三区资源站| 午夜精品亚洲精品五月色| 在线新三级黄伊人网| 激情国产小视频在线| 中文字幕在线第一页成人| 日韩美av高清在线| 日本成人不卡一区二区| av老司机精品在线观看| 亚洲va国产va欧美va在线| 国产欧美精品不卡在线| 久久这里只有精品热视频| 日本熟女50视频免费| 日韩人妻xxxxx| 亚洲一区二区三区在线高清| av老司机亚洲一区二区| 姐姐的朋友2在线观看中文字幕| 中文字幕日韩91人妻在线| 国产日韩精品一二三区久久久| 亚洲国产成人最新资源| 色av色婷婷人妻久久久精品高清| 99热久久极品热亚洲| 精品一区二区亚洲欧美| 天天操天天干天天艹| 久久艹在线观看视频| 午夜精品福利一区二区三区p| 国产清纯美女al在线| 日韩中文字幕福利av| 欧美精品一二三视频| v888av在线观看视频| 欧美乱妇无乱码一区二区| av在线免费中文字幕| 激情图片日韩欧美人妻| 最新97国产在线视频| 女生被男生插的视频网站| 国产性色生活片毛片春晓精品| 日日操夜夜撸天天干| 久久久噜噜噜久久熟女av| 绯色av蜜臀vs少妇| 亚洲综合一区成人在线| 啪啪啪啪啪啪啪啪啪啪黄色| 男女之间激情网午夜在线| 五色婷婷综合狠狠爱| 99久久99一区二区三区| 粉嫩av蜜乳av蜜臀| 亚洲精品无码久久久久不卡| 香蕉91一区二区三区| 唐人色亚洲av嫩草| 亚洲精品国产综合久久久久久久久| 亚洲1区2区3区精华液| 欧美视频不卡一区四区| 五十路老熟女码av| 五十路人妻熟女av一区二区| 强行扒开双腿猛烈进入免费版| 国产成人精品久久二区91| 男生用鸡操女生视频动漫| 亚洲av色香蕉一区二区三区| 国产一区成人在线观看视频| 国产一区二区久久久裸臀| 国产精品入口麻豆啊啊啊| 18禁无翼鸟成人在线| 人妻丰满熟妇综合网| 宅男噜噜噜666免费观看| 一区二区三区毛片国产一区| 日本啪啪啪啪啪啪啪| 38av一区二区三区| 大香蕉大香蕉大香蕉大香蕉大香蕉| 国产精品一区二区av国| 国产 在线 免费 精品| 鸡巴操逼一级黄色气| 日本一区美女福利视频| 人人人妻人人澡人人| 久久三久久三久久三久久| 日日日日日日日日夜夜夜夜夜夜| 非洲黑人一级特黄片| av一本二本在线观看| 亚洲国产成人无码麻豆艾秋| 蜜臀av久久久久久久| 欧美日本aⅴ免费视频| 日韩亚国产欧美三级涩爱| 另类av十亚洲av| 狠狠躁夜夜躁人人爽天天久天啪| 在线不卡日韩视频播放| 国产极品精品免费视频| 人妻丝袜精品中文字幕| 成人国产激情自拍三区| asmr福利视频在线观看| 黄色无码鸡吧操逼视频| 亚洲综合一区成人在线| 91久久综合男人天堂| 亚洲欧美激情中文字幕| 动漫精品视频在线观看| 中文字幕乱码人妻电影| 日本丰满熟妇大屁股久久| 在线免费观看亚洲精品电影| 欧美一区二区三区激情啪啪啪| 五十路av熟女松本翔子| 国产污污污污网站在线| aaa久久久久久久久| 亚洲天堂精品久久久| 99精品视频之69精品视频| 午夜蜜桃一区二区三区| 好了av中文字幕在线| 视频久久久久久久人妻| 99re6热在线精品| 久久久久国产成人精品亚洲午夜| 欧美精品伦理三区四区| 伊人情人综合成人久久网小说| 亚洲熟色妇av日韩熟色妇在线| 日本一道二三区视频久久| 2018在线福利视频| 在线免费视频 自拍| 宅男噜噜噜666国产| 91人妻精品一区二区在线看| 精品一区二区亚洲欧美| 日韩人妻xxxxx| 天堂av在线官网中文| 天天爽夜夜爽人人爽QC| av高潮迭起在线观看| 精品人妻伦一二三区久| 欧美专区日韩专区国产专区| 亚洲码av无色中文| 91麻豆精品91久久久久同性| 少妇与子乱在线观看| 亚洲推理片免费看网站| 国产精品精品精品999| 最新中文字幕免费视频| 一级A一级a爰片免费免会员| 直接能看的国产av| 亚洲在线观看中文字幕av| 人人人妻人人澡人人| 天天日天天操天天摸天天舔| 日本黄色特一级视频| 秋霞午夜av福利经典影视| 日本精品视频不卡一二三| 亚洲成人激情av在线| 国产精品自拍偷拍a| 午夜场射精嗯嗯啊啊视频| 黄色黄色黄片78在线| 成人免费公开视频无毒| 日韩欧美制服诱惑一区在线| av在线观看网址av| 四川五十路熟女av| av天堂中文免费在线| av一区二区三区人妻| 在线观看免费视频色97| 99精品视频在线观看婷婷| 天天插天天狠天天操| 日韩欧美高清免费在线| 国产亚洲欧美视频网站| 亚洲男人的天堂a在线| 99热这里只有国产精品6| 亚洲欧美国产综合777| 欧美精品 日韩国产| 色婷婷六月亚洲综合香蕉| 亚洲av无女神免非久久| 抽查舔水白紧大视频| 亚洲老熟妇日本老妇| 美女被肏内射视频网站| 国产之丝袜脚在线一区二区三区 | 精彩视频99免费在线| 在线观看亚洲人成免费网址| 老司机午夜精品视频资源| av中文字幕在线观看第三页| 人妻丰满熟妇综合网| 亚洲va天堂va国产va久| 9国产精品久久久久老师| 天天干天天日天天干天天操| 女人精品内射国产99| 天天干天天操天天玩天天射| 偷拍3456eee| 天天日天天日天天射天天干| 黑人3p华裔熟女普通话| 婷婷久久久综合中文字幕| 91免费放福利在线观看| 日韩人妻丝袜中文字幕| 午夜国产免费福利av| 黑人3p华裔熟女普通话| 岛国黄色大片在线观看| 国产黄色片在线收看| 中文字幕免费福利视频6| 婷婷六月天中文字幕| 欧美久久久久久三级网| 一级a看免费观看网站| 狠狠嗨日韩综合久久| 色97视频在线播放| 亚洲精品三级av在线免费观看| 孕妇奶水仑乱A级毛片免费看| 日本午夜久久女同精女女| 国产成人自拍视频播放| 欧美黄片精彩在线免费观看| 大陆胖女人与丈夫操b国语高清| 日本免费一级黄色录像| 熟女少妇激情五十路| 成人av天堂丝袜在线观看| 1000小视频在线| 在线观看的黄色免费网站| 77久久久久国产精产品| 九色精品视频在线播放| 亚洲av日韩精品久久久| 水蜜桃国产一区二区三区| 蜜桃专区一区二区在线观看| 国产使劲操在线播放| 天天射,天天操,天天说| 久久久麻豆精亚洲av麻花| 边摸边做超爽毛片18禁色戒 | 老司机你懂得福利视频| 宅男噜噜噜666国产| 二区中出在线观看老师| 日本成人一区二区不卡免费在线| av天堂中文字幕最新| 精品一线二线三线日本| 99精品免费观看视频| 揄拍成人国产精品免费看视频| 亚洲一级 片内射视正片| 青青青激情在线观看视频| 国产 在线 免费 精品| 熟女人妻一区二区精品视频| 免费人成黄页网站在线观看国产| 天天色天天操天天透| 天堂av狠狠操蜜桃| 国产刺激激情美女网站| 日本少妇在线视频大香蕉在线观看| 色天天天天射天天舔| 九色精品视频在线播放| 国产又色又刺激在线视频| 亚洲精品欧美日韩在线播放| 2022国产精品视频| 国产黑丝高跟鞋视频在线播放| 国产三级精品三级在线不卡| 亚洲欧美综合另类13p| aⅴ精产国品一二三产品| 国产久久久精品毛片| 开心 色 六月 婷婷| 99热99这里精品6国产| 一色桃子久久精品亚洲| 91欧美在线免费观看| 日韩av中文在线免费观看| 亚洲免费福利一区二区三区| 欧美专区日韩专区国产专区| 午夜精品久久久久麻豆影视| 人妻少妇av在线观看| 一区二区三区四区五区性感视频| 在线视频精品你懂的| 欧美女同性恋免费a| 亚洲免费福利一区二区三区| 女同互舔一区二区三区| 中文字幕 亚洲av| 欧美日韩精品永久免费网址| av乱码一区二区三区| 97精品综合久久在线| 亚洲精品国品乱码久久久久| 亚洲午夜电影之麻豆| 久久香蕉国产免费天天| 久久久极品久久蜜桃| 婷婷色中文亚洲网68| 精品人妻伦一二三区久| 最新中文字幕乱码在线| 1024久久国产精品| 日韩人妻在线视频免费| 操日韩美女视频在线免费看| 天天日天天干天天搡| 11久久久久久久久久久| 五色婷婷综合狠狠爱| 51国产成人精品视频| 亚洲欧美国产麻豆综合| 免费一级特黄特色大片在线观看| 激情伦理欧美日韩中文字幕| 久久永久免费精品人妻专区| 亚洲最大黄了色网站| 欧美天堂av无线av欧美| 成年女人免费播放视频| 中文字幕人妻三级在线观看| 国产精品自偷自拍啪啪啪| 国产一区二区视频观看| 欧美国产亚洲中英文字幕| 国产成人精品福利短视频| 在线视频这里只有精品自拍| 一区二区三区视频,福利一区二区| 精品人人人妻人人玩日产欧| 熟女人妻在线中出观看完整版| 色天天天天射天天舔| japanese日本熟妇另类| 中文字幕1卡1区2区3区| 国产精品久久久久久久久福交| 成人精品在线观看视频| 国产成人精品午夜福利训2021| 又色又爽又黄又刺激av网站| 性欧美激情久久久久久久| 亚洲一区二区激情在线| 国产中文精品在线观看| 精品黑人一区二区三区久久国产| 在线视频这里只有精品自拍| 日韩欧美中文国产在线| 一级黄片久久久久久久久| 亚洲一区二区激情在线| aⅴ精产国品一二三产品| 午夜蜜桃一区二区三区| 最后99天全集在线观看| 国产又粗又硬又大视频| 国产在线观看免费人成短视频| 亚洲午夜电影之麻豆| 青青热久免费精品视频在线观看| 好吊操视频这里只有精品| 偷拍自拍亚洲视频在线观看| 在线免费观看欧美小视频| 婷婷久久一区二区字幕网址你懂得 | 沈阳熟妇28厘米大战黑人| 中国黄片视频一区91| 亚洲国产免费av一区二区三区 | 精品人人人妻人人玩日产欧| 美女福利写真在线观看视频| 91在线视频在线精品3| 中文字幕午夜免费福利视频| 黄色录像鸡巴插进去| 国产精品福利小视频a| 91免费放福利在线观看| 亚洲国产中文字幕啊啊啊不行了| 国产精品成久久久久三级蜜臀av | 青草亚洲视频在线观看| 中英文字幕av一区| 日韩av大胆在线观看| 亚洲伊人色一综合网| 经典亚洲伊人第一页| 在线免费观看国产精品黄色| 国产97在线视频观看| 午夜大尺度无码福利视频| 亚洲第一黄色在线观看| 人妻久久无码中文成人| 日韩美av高清在线| 在线观看视频一区麻豆| 被大鸡吧操的好舒服视频免费| 国产综合高清在线观看| 国产精品久久综合久久| 国产免费av一区二区凹凸四季| 中文字幕一区二区人妻电影冢本| 在线观看成人国产电影| 91国内视频在线观看| 色呦呦视频在线观看视频| 中文字幕之无码色多多| 在线视频自拍第三页| 家庭女教师中文字幕在线播放| 亚洲av男人天堂久久| 最新国产精品拍在线观看| 日韩二区视频一线天婷婷五| 国产性色生活片毛片春晓精品| 亚洲国产精品黑丝美女| 综合激情网激情五月五月婷婷| 五十路老熟女码av| 大鸡巴操b视频在线| 操日韩美女视频在线免费看| www日韩a级s片av| 欧美日韩国产一区二区三区三州 | 免费岛国喷水视频在线观看 | 亚洲 色图 偷拍 欧美| 五十路息与子猛烈交尾视频| 亚洲熟妇无码一区二区三区| 操的小逼流水的文章| 五月色婷婷综合开心网4438| 国产日韩精品免费在线| 一级a看免费观看网站| 人人妻人人澡人人爽人人dvl| 99精品视频之69精品视频| 喷水视频在线观看这里只有精品| 色狠狠av线不卡香蕉一区二区| 天天日夜夜操天天摸| 在线免费观看黄页视频| 亚洲av天堂在线播放| lutube在线成人免费看| 成年午夜影片国产片| 97超碰国语国产97超碰| 午夜av一区二区三区| 中文字幕日韩91人妻在线| 久草视频在线一区二区三区资源站| 青青在线视频性感少妇和隔壁黑丝| 97国产在线av精品| gogo国模私拍视频| 天天摸天天日天天操| 丝袜长腿第一页在线| 中文字幕av一区在线观看 | 又大又湿又爽又紧A视频| 亚洲欧美人精品高清| 欧美成一区二区三区四区| 亚洲超碰97人人做人人爱| 又黄又刺激的午夜小视频| 在线成人日韩av电影| 亚洲天堂有码中文字幕视频 | 老鸭窝日韩精品视频观看| 日本少妇人妻xxxxxhd| lutube在线成人免费看| 福利视频广场一区二区| 黄色成人在线中文字幕| 人妻最新视频在线免费观看| 亚洲成人av一区久久| av视网站在线观看| 人妻少妇亚洲精品中文字幕| 国产密臀av一区二区三| 午夜久久久久久久99| 国产av国片精品一区二区| 欧美精产国品一二三产品价格| 一区国内二区日韩三区欧美| 亚洲欧美激情人妻偷拍| 成人av久久精品一区二区| 青青草精品在线视频观看| 中文字幕人妻被公上司喝醉在线| 91在线免费观看成人| 91亚洲手机在线视频播放| 中文字幕av熟女人妻| 免费福利av在线一区二区三区| 日本阿v视频在线免费观看| 欧美中国日韩久久精品| 日本人竟这样玩学生妹| 欧美日韩一级黄片免费观看| 午夜精品久久久久麻豆影视| 亚洲视频乱码在线观看| 日本午夜久久女同精女女| 亚洲蜜臀av一区二区三区九色| 精品av久久久久久久| 免费福利av在线一区二区三区| 午夜极品美女福利视频| 午夜久久香蕉电影网| 日本午夜久久女同精女女| 国产精品久久久久网| 大香蕉大香蕉在线看| 国产日韩欧美美利坚蜜臀懂色| 成年午夜影片国产片| 中国黄片视频一区91| 超鹏97历史在线观看| 年轻的人妻被夫上司侵犯| 懂色av之国产精品| 色婷婷精品大在线观看| 免费费一级特黄真人片| 77久久久久国产精产品| 久草极品美女视频在线观看| 欧美美女人体视频一区| 成人在线欧美日韩国产| 又黄又刺激的午夜小视频| 天天操天天污天天射| 精品首页在线观看视频| 桃色视频在线观看一区二区| 3D动漫精品啪啪一区二区下载| 日本午夜福利免费视频| 午夜精品福利一区二区三区p| 亚洲国产精品久久久久久6| 99热久久极品热亚洲| 五十路av熟女松本翔子| nagger可以指黑人吗| 日本一二三区不卡无| 中出中文字幕在线观看| 五月天中文字幕内射| 日韩av大胆在线观看| 午夜蜜桃一区二区三区| 把腿张开让我插进去视频| 中文字幕av熟女人妻| 亚洲精品成人网久久久久久小说| 特大黑人巨大xxxx| 天天干夜夜操啊啊啊| 欧美精品欧美极品欧美视频| 在线不卡成人黄色精品| 美女少妇亚洲精选av| 人人人妻人人澡人人| 最新91九色国产在线观看| 日本男女操逼视频免费看| 亚洲国产精品久久久久久6| 插小穴高清无码中文字幕| 青草亚洲视频在线观看| 熟女视频一区,二区,三区| 人妻av无码专区久久绿巨人| 久草免费人妻视频在线| 老司机福利精品免费视频一区二区| 精品国产在线手机在线| 人妻激情图片视频小说| 极品性荡少妇一区二区色欲| 国产福利小视频免费观看| 999热精品视频在线| 精品亚洲中文字幕av| 亚洲精品国产综合久久久久久久久| 国产成人精品一区在线观看| 国产男女视频在线播放| 久久99久久99精品影院| 天天射夜夜操综合网| 精品高跟鞋丝袜一区二区| 欧美美女人体视频一区| 青青青aaaa免费| 少妇露脸深喉口爆吞精| 免费岛国喷水视频在线观看| 大香蕉福利在线观看| 一区二区免费高清黄色视频| 93精品视频在线观看| 亚洲av在线观看尤物| 91免费福利网91麻豆国产精品| 91精品一区二区三区站长推荐| 夜夜操,天天操,狠狠操| 国产视频精品资源网站| 亚洲 国产 成人 在线| 国产成人一区二区三区电影网站| 啪啪啪18禁一区二区三区| 人妻少妇精品久久久久久| 人妻少妇性色欲欧美日韩| 天天操天天爽天天干| 自拍偷拍亚洲欧美在线视频| 天天插天天狠天天操| 精品成人午夜免费看| 青青草在观免费国产精品| 精品少妇一二三视频在线| 亚洲欧美成人综合在线观看| 端庄人妻堕落挣扎沉沦| 欧美成人小视频在线免费看| 97a片免费在线观看| 黄色在线观看免费观看在线 | 精品国产污污免费网站入口自| 97人妻色免费视频| 激情啪啪啪啪一区二区三区| 亚洲粉嫩av一区二区三区| 777奇米久久精品一区| yellow在线播放av啊啊啊 | 久久久91蜜桃精品ad| 欧美精品资源在线观看| 亚洲狠狠婷婷综合久久app| 国产精品久久久久久美女校花| 二区中出在线观看老师| 午夜在线观看岛国av,com| 40道精品招牌菜特色| 日本av高清免费网站| 日本啪啪啪啪啪啪啪| 国产精品系列在线观看一区二区| 国产中文字幕四区在线观看| 一区二区三区精品日本| 天堂av在线官网中文| 亚洲福利午夜久久久精品电影网| 欧美成人猛片aaaaaaa| 夏目彩春在线中文字幕| 亚洲天堂精品久久久| 自拍偷拍亚洲欧美在线视频| 国产91嫩草久久成人在线视频| 天天干狠狠干天天操| 人妻另类专区欧美制服| 93人妻人人揉人人澡人人| 日本xx片在线观看| 黄片三级三级三级在线观看| 人妻少妇亚洲精品中文字幕| 99精品一区二区三区的区| 日韩加勒比东京热二区| 老司机午夜精品视频资源| 成年美女黄网站18禁久久| 蜜桃专区一区二区在线观看| 精品久久久久久久久久久99| 日韩精品啪啪视频一道免费| 爆乳骚货内射骚货内射在线| 欧美伊人久久大香线蕉综合| 91精品国产高清自在线看香蕉网| 欧美日韩中文字幕欧美| 国产大学生援交正在播放| 91在线免费观看成人| 一区二区麻豆传媒黄片 | 80电影天堂网官网| 国产精品亚洲а∨天堂免| 特一级特级黄色网片| 不卡一不卡二不卡三| 一区二区三区蜜臀在线| 日韩av熟妇在线观看| 午夜福利人人妻人人澡人人爽| 少妇被强干到高潮视频在线观看| 九九视频在线精品播放| 一区二区三区四区视频| 手机看片福利盒子日韩在线播放| 亚洲1区2区3区精华液| 精品亚洲国产中文自在线| 免费观看污视频网站| 天天做天天干天天操天天射| 亚洲码av无色中文| 年轻的人妻被夫上司侵犯| 五十路熟女人妻一区二| 福利午夜视频在线合集| 在线免费观看99视频| 黄片三级三级三级在线观看| 大黑人性xxxxbbbb| 亚洲高清自偷揄拍自拍| av一区二区三区人妻| 被大鸡吧操的好舒服视频免费| 搡老熟女一区二区在线观看| 曰本无码人妻丰满熟妇啪啪| 熟女俱乐部一二三区| 精品久久久久久久久久久99| 亚洲国产成人无码麻豆艾秋| 国产综合高清在线观看| 成年人啪啪视频在线观看| 专门看国产熟妇的网站| 丝袜国产专区在线观看| 国产一区二区欧美三区| 日韩精品中文字幕播放| 一个人免费在线观看ww视频| 91大屁股国产一区二区| 亚洲一区二区三区精品视频在线 | 国产不卡av在线免费| 国产黄色高清资源在线免费观看| 欧美日韩亚洲国产无线码| 久草视频在线看免费| 大鸡吧插入女阴道黄色片| 姐姐的朋友2在线观看中文字幕| 欧美精产国品一二三产品价格 | 日韩亚国产欧美三级涩爱| 精品91自产拍在线观看一区| 五十路av熟女松本翔子| 福利视频一区二区三区筱慧 | xxx日本hd高清| 久草视频在线一区二区三区资源站| 欧美日本国产自视大全| 国产又色又刺激在线视频| 国产精品熟女久久久久浪潮| 一区二区免费高清黄色视频| 午夜久久香蕉电影网| 婷婷久久一区二区字幕网址你懂得| 国产亚洲精品欧洲在线观看| 又黄又刺激的午夜小视频| 国产精品人久久久久久| 国产品国产三级国产普通话三级| 自拍 日韩 欧美激情| 亚洲成人线上免费视频观看| 五月激情婷婷久久综合网| 欧美日韩v中文在线| 亚洲精品无码色午夜福利理论片| 早川濑里奈av黑人番号| 岛国一区二区三区视频在线| 亚洲1069综合男同| 国产自拍在线观看成人| 国产九色91在线观看精品| 蜜桃视频入口久久久| 99的爱精品免费视频| 中文字幕 亚洲av| 国产精品欧美日韩区二区| 免费无毒热热热热热热久| 端庄人妻堕落挣扎沉沦| 中文字幕日韩精品就在这里| 狠狠的往里顶撞h百合| 2018最新中文字幕在线观看| 欧美另类一区二区视频| 91精品国产91青青碰| 性感美女高潮视频久久久| 91麻豆精品传媒国产黄色片| 欧美精品黑人性xxxx| 色天天天天射天天舔| 亚洲 色图 偷拍 欧美| 91p0rny九色露脸熟女| 在线观看欧美黄片一区二区三区| free性日本少妇| 亚洲蜜臀av一区二区三区九色| 伊人精品福利综合导航| 91在线免费观看成人| 视频久久久久久久人妻| 成人伊人精品色xxxx视频| 玩弄人妻熟妇性色av少妇| 80电影天堂网官网| 免费看高清av的网站| 在线免费观看日本伦理| 人妻丝袜榨强中文字幕| 都市激情校园春色狠狠| 国产精品视频资源在线播放| 青青青青青青草国产| 绝顶痉挛大潮喷高潮无码 | 日韩不卡中文在线视频网站| 一区二区三区综合视频| 97香蕉碰碰人妻国产樱花| 91九色国产熟女一区二区| 岛国黄色大片在线观看| 51精品视频免费在线观看| 久久久精品999精品日本 | 国产av国片精品一区二区| 亚洲午夜福利中文乱码字幕| 喷水视频在线观看这里只有精品| 最新日韩av传媒在线| 99国内小视频在现欢看| 啊啊啊视频试看人妻| 亚洲高清视频在线不卡| 在线免费观看国产精品黄色| 激情国产小视频在线| 日本熟女50视频免费| 999九九久久久精品| 人人爱人人妻人人澡39| 激情图片日韩欧美人妻| 欧洲国产成人精品91铁牛tv| 888亚洲欧美国产va在线播放| 天天操天天污天天射| 日噜噜噜夜夜噜噜噜天天噜噜噜| 午夜蜜桃一区二区三区| 国产熟妇乱妇熟色T区| 在线观看国产网站资源| 不戴胸罩引我诱的隔壁的人妻| 九九热99视频在线观看97| 99国产精品窥熟女精品| 视频一区 二区 三区 综合| 水蜜桃一区二区三区在线观看视频 | 欧美一级片免费在线成人观看 | 成人乱码一区二区三区av| 毛片av在线免费看| 天天躁夜夜躁日日躁a麻豆| 亚洲第17页国产精品| 亚洲激情,偷拍视频| 久久三久久三久久三久久| 日韩熟女系列一区二区三区| sw137 中文字幕 在线| 日本性感美女写真视频| av天堂中文免费在线| 97人妻夜夜爽二区欧美极品| 最新国产精品网址在线观看| 欧美3p在线观看一区二区三区| 精品首页在线观看视频| 制服丝袜在线人妻中文字幕| 亚洲第一黄色在线观看| 国产大学生援交正在播放| 亚洲伊人久久精品影院一美女洗澡| 家庭女教师中文字幕在线播放| 国产成人无码精品久久久电影| 欧美亚洲偷拍自拍色图| 亚洲欧美另类手机在线| 亚洲在线观看中文字幕av| 狍和女人的王色毛片| 日美女屁股黄邑视频| 亚洲av成人免费网站| 中文字幕高清在线免费播放 | 国产精品成人xxxx| 97人人模人人爽人人喊| 偷拍自拍亚洲美腿丝袜| 成人网18免费视频版国产| 天天操天天射天天操天天天| 欧美成人一二三在线网| 国产美女午夜福利久久| 欧美乱妇无乱码一区二区| 精品视频一区二区三区四区五区| 一色桃子人妻一区二区三区| AV天堂一区二区免费试看| 老师让我插进去69AV| 日本黄在免费看视频| av天堂中文字幕最新| 亚洲偷自拍高清视频| 激情内射在线免费观看| 99国内小视频在现欢看| 亚洲中文字幕综合小综合| 自拍 日韩 欧美激情| 色哟哟在线网站入口| 亚洲高清视频在线不卡| 北条麻妃高跟丝袜啪啪| 亚洲一区二区三区uij| gay gay男男瑟瑟在线网站| 国产丰满熟女成人视频| 91大神福利视频网| 国产成人午夜精品福利| 激情啪啪啪啪一区二区三区| 2021最新热播中文字幕| 在线观看911精品国产| 国产九色91在线观看精品| 国产日韩精品一二三区久久久| 熟女人妻在线观看视频| 蜜臀av久久久久久久| 天堂资源网av中文字幕| 91片黄在线观看喷潮| 人妻少妇精品久久久久久| 国产美女一区在线观看| 青青青青青手机视频| 国产亚洲天堂天天一区| aⅴ精产国品一二三产品| 免费男阳茎伸入女阳道视频 | 亚洲欧美综合另类13p| 欧美视频一区免费在线| 免费岛国喷水视频在线观看 | 日曰摸日日碰夜夜爽歪歪| 沈阳熟妇28厘米大战黑人| 青青青视频手机在线观看| 女同久久精品秋霞网| 影音先锋女人av噜噜色| 97成人免费在线观看网站| 老司机欧美视频在线看| 狠狠操操操操操操操操操| av中文字幕国产在线观看| 久草视频福利在线首页| 国产精彩对白一区二区三区| 日韩欧美中文国产在线| 一级黄色av在线观看| 久久精品国产999| 亚洲精品午夜aaa久久| 人妻丝袜av在线播放网址| 天天操天天干天天插| 人人妻人人澡欧美91精品| 国产精品中文av在线播放| 亚洲图库另类图片区| 久久综合老鸭窝色综合久久| 天天干天天操天天爽天天摸| 欧美伊人久久大香线蕉综合| 成年人啪啪视频在线观看| 亚洲人妻国产精品综合| 中文字幕在线观看极品视频| 亚洲另类在线免费观看| 伊人网中文字幕在线视频| 成人av天堂丝袜在线观看| 亚洲综合另类欧美久久| 青青青青青青青青青青草青青| 91色网站免费在线观看| av老司机亚洲一区二区| 国产精品中文av在线播放 | 色哟哟在线网站入口| 蜜臀av久久久久久久| 狠狠操操操操操操操操操| 天堂女人av一区二区| 天天日天天透天天操| 小泽玛利亚视频在线观看| 国产亚洲视频在线二区| 巨乳人妻日下部加奈被邻居中出| 97瑟瑟超碰在线香蕉| 91九色国产porny蝌蚪| 色爱av一区二区三区| 99热这里只有国产精品6| 国产麻豆剧传媒精品国产av蜜桃 | 国产在线91观看免费观看| 日本熟女50视频免费| 亚洲av自拍天堂网| 晚上一个人看操B片| 国产性感美女福利视频| 国产精品入口麻豆啊啊啊| 亚洲成人情色电影在线观看| 色综合色综合色综合色| 传媒在线播放国产精品一区| 国产精品久久久久久久久福交| 黄色资源视频网站日韩| 亚洲国产40页第21页| 成年人的在线免费视频| 国产综合精品久久久久蜜臀| 午夜91一区二区三区| 国产精品一区二区av国| 亚洲熟女久久久36d| 国产一区二区火爆视频| 97人妻色免费视频| 果冻传媒av一区二区三区| 少妇高潮无套内谢麻豆| 欧美成人小视频在线免费看| 黄色片年轻人在线观看| 天堂av在线播放免费| 五月色婷婷综合开心网4438| 欧美性感尤物人妻在线免费看| 欧美日韩熟女一区二区三区| 人人爽亚洲av人人爽av| 成人av电影免费版| 婷婷色中文亚洲网68| 天天日天天爽天天爽| 亚洲免费福利一区二区三区| 五月激情婷婷久久综合网| 日韩欧美中文国产在线 | 久草视频首页在线观看| aⅴ五十路av熟女中出| 91麻豆精品传媒国产黄色片| eeuss鲁片一区二区三区| 亚洲男人让女人爽的视频| 天天日夜夜操天天摸| 中字幕人妻熟女人妻a62v网| 久久久久久久精品老熟妇| 亚洲女人的天堂av| 婷婷色中文亚洲网68| 国产高潮无码喷水AV片在线观看| 青娱乐最新视频在线| 97精品人妻一区二区三区精品| 91国产资源在线视频| 狠狠躁狠狠爱网站视频| 欧美精品黑人性xxxx| 黑人性生活视频免费看| 影音先锋女人av噜噜色| 91精品国产高清自在线看香蕉网 | 中文字幕在线欧美精品| brazzers欧熟精品系列| 91老熟女连续高潮对白| 日本高清成人一区二区三区| 精品国产成人亚洲午夜| 中文字幕免费在线免费| 日本www中文字幕| 精品国产在线手机在线| 亚洲一区二区三区av网站| 黄色黄色黄片78在线| 亚洲综合乱码一区二区| 日本高清撒尿pissing| 亚洲欧美自拍另类图片| 大肉大捧一进一出好爽在线视频 | 一区二区三区综合视频| 日韩欧美中文国产在线| 欧美黑人性猛交xxxxⅹooo| 任你操视频免费在线观看| 天堂资源网av中文字幕| 欧美香蕉人妻精品一区二区| 欧亚乱色一区二区三区| 午夜精品久久久久久99热| 亚洲一区二区三区精品视频在线| 国产极品精品免费视频| 第一福利视频在线观看| 美女福利视频网址导航| 丝袜美腿欧美另类 中文字幕| www天堂在线久久| 日本高清在线不卡一区二区| 成人性黑人一级av| 揄拍成人国产精品免费看视频| 毛片av在线免费看| 中文人妻AV久久人妻水| 亚洲一区二区三区uij| 欧美色婷婷综合在线| 国产一级麻豆精品免费| 中文字幕一区二区三区蜜月| 亚洲成人激情视频免费观看了| 中文乱理伦片在线观看| 大陆精品一区二区三区久久| 国产不卡av在线免费| 2022国产精品视频| 五月婷婷在线观看视频免费 | av一本二本在线观看| 中文字幕av熟女人妻| 日本中文字幕一二区视频| 在线制服丝袜中文字幕| 青青社区2国产视频| 美女av色播在线播放| 亚洲天堂有码中文字幕视频| 国产大鸡巴大鸡巴操小骚逼小骚逼| 亚洲av天堂在线播放| 亚洲 人妻 激情 中文| 91精品资源免费观看| 中文字幕日韩无敌亚洲精品| 精品一区二区三区午夜| 日本免费视频午夜福利视频| 一区二区熟女人妻视频| 在线国产精品一区二区三区| 91精品资源免费观看| 免费观看成年人视频在线观看| 亚洲中文精品字幕在线观看| 最新的中文字幕 亚洲| 日本高清在线不卡一区二区 | 社区自拍揄拍尻屁你懂的| avjpm亚洲伊人久久| 亚洲国际青青操综合网站| 热99re69精品8在线播放| 在线观看av2025| 亚洲熟女女同志女同| 亚洲av自拍天堂网| 天天日天天敢天天干| 亚洲午夜在线视频福利| 国产白嫩美女一区二区| 亚洲综合一区二区精品久久| 国产黄色片在线收看| av手机免费在线观看高潮| 5528327男人天堂| 亚洲伊人色一综合网| 一区二区熟女人妻视频| 狠狠躁夜夜躁人人爽天天久天啪| 青青擦在线视频国产在线| 国产亚洲国产av网站在线| 99热这里只有精品中文| 亚洲精品高清自拍av| 亚洲另类图片蜜臀av| 免费观看理论片完整版| 黄色三级网站免费下载| 亚洲中文精品字幕在线观看| 人妻丝袜榨强中文字幕| 91片黄在线观看喷潮| 欧美香蕉人妻精品一区二区| 亚国产成人精品久久久| 超级福利视频在线观看| 日本人妻欲求不满中文字幕| 国产精品人妻熟女毛片av久| 91快播视频在线观看| 亚洲成人午夜电影在线观看| av天堂中文字幕最新| 老司机免费视频网站在线看| 免费在线黄色观看网站| 欧美地区一二三专区| 久久久久久99国产精品| 国产精品久久久久久久久福交| 人人爱人人妻人人澡39| 国产午夜激情福利小视频在线| 日本欧美视频在线观看三区| 国产又粗又猛又爽又黄的视频美国| 啊啊好大好爽啊啊操我啊啊视频| 中文字幕在线视频一区二区三区| 沈阳熟妇28厘米大战黑人| 久碰精品少妇中文字幕av| 日日夜夜狠狠干视频| aⅴ精产国品一二三产品| 专门看国产熟妇的网站| 强行扒开双腿猛烈进入免费版| 青青草原色片网站在线观看| 岛国一区二区三区视频在线| 初美沙希中文字幕在线| 午夜精品久久久久麻豆影视| 啊用力插好舒服视频| 亚洲福利精品视频在线免费观看| 天天草天天色天天干| 首之国产AV医生和护士小芳| 白白操白白色在线免费视频| 欧美激情电影免费在线| 青青青青青免费视频| 少妇人妻久久久久视频黄片| 黄色视频在线观看高清无码 | 91中文字幕最新合集| 粉嫩欧美美人妻小视频| 欧美精品激情在线最新观看视频 | 国产不卡av在线免费| 欧美精品一区二区三区xxxx| 免费av岛国天堂网站| 在线网站你懂得老司机| 国产精品污污污久久| 国产在线拍揄自揄视频网站| 国产成人精品久久二区91| 日日操综合成人av| 国产av国片精品一区二区| 亚洲欧美一区二区三区爱爱动图 | 国产极品精品免费视频| 日本少妇精品免费视频| 精品suv一区二区69| 男人天堂最新地址av| av无限看熟女人妻另类av | 自拍 日韩 欧美激情| 婷婷久久一区二区字幕网址你懂得| av高潮迭起在线观看| 不卡日韩av在线观看| 91中文字幕最新合集| 黄色三级网站免费下载| 亚洲一区自拍高清免费视频| 精品一线二线三线日本| 国产精品自偷自拍啪啪啪| 偷拍美女一区二区三区| 男人的天堂av日韩亚洲| 日韩欧美高清免费在线| 黄色三级网站免费下载| 在线免费91激情四射| 97成人免费在线观看网站| 国产精品三级三级三级| 亚洲熟色妇av日韩熟色妇在线| 青青社区2国产视频| 91欧美在线免费观看| 九色视频在线观看免费| 日韩视频一区二区免费观看| 亚洲精品ww久久久久久| 视频 国产 精品 熟女 | 亚洲福利精品福利精品福利| 玖玖一区二区在线观看| 在线观看视频一区麻豆| 国产在线免费观看成人| 99视频精品全部15| 在线观看视频网站麻豆| 天天日天天日天天擦| 97精品成人一区二区三区| 午夜精品亚洲精品五月色| 成年人免费看在线视频| 午夜久久久久久久99| avjpm亚洲伊人久久| 丝袜美腿视频诱惑亚洲无| eeuss鲁片一区二区三区| 国产免费av一区二区凹凸四季| 99一区二区在线观看| 强行扒开双腿猛烈进入免费版| 极品粉嫩小泬白浆20p主播| 五十路av熟女松本翔子| 一级黄片久久久久久久久| 最后99天全集在线观看| 国产一线二线三线的区别在哪| 亚洲麻豆一区二区三区| 青青青青视频在线播放| 超碰中文字幕免费观看| 亚洲福利午夜久久久精品电影网| 国产日韩精品一二三区久久久| 国产九色91在线视频| 亚洲天堂精品福利成人av| 在线免费观看日本伦理| 国产av福利网址大全| av森泽佳奈在线观看| 性色蜜臀av一区二区三区| 久久这里只有精彩视频免费| 在线观看视频网站麻豆| 日韩影片一区二区三区不卡免费| 班长撕开乳罩揉我胸好爽| 日本一区二区三区免费小视频| 亚洲天堂成人在线观看视频网站| 伊人网中文字幕在线视频| 快插进小逼里大鸡吧视频| 一级黄片久久久久久久久| 亚洲人妻视频在线网| 美女操逼免费短视频下载链接| 国产美女精品福利在线| 2020久久躁狠狠躁夜夜躁| 色婷婷综合激情五月免费观看| jiujiure精品视频在线| 国产视频精品资源网站| 婷婷六月天中文字幕| 欧美xxx成人在线| 日韩av有码中文字幕| 亚洲一级av无码一级久久精品| 天堂va蜜桃一区入口| av天堂资源最新版在线看| 影音先锋女人av噜噜色| 视频 一区二区在线观看| 成人高潮aa毛片免费| 无忧传媒在线观看视频| 中文字幕日韩人妻在线三区| 亚洲图库另类图片区| 一级黄片大鸡巴插入美女| 国内精品在线播放第一页| 一区二区三区日本伦理| 偷拍自拍 中文字幕| 成年人啪啪视频在线观看| 肏插流水妹子在线乐播下载| 亚洲激情,偷拍视频| 99国产精品窥熟女精品| 欧美成人精品欧美一级黄色| 亚洲欧美另类手机在线| 国产极品精品免费视频| av亚洲中文天堂字幕网| 91chinese在线视频| 成人资源在线观看免费官网| 99精品视频之69精品视频| 中国黄片视频一区91| 最新的中文字幕 亚洲| 亚洲男人的天堂a在线| 大胆亚洲av日韩av| 自拍偷拍亚洲精品第2页| 国产一级精品综合av| 老师让我插进去69AV| 蜜臀av久久久久久久| av资源中文字幕在线观看| 天天干狠狠干天天操 | 亚洲精品 日韩电影| 福利视频广场一区二区| 午夜成午夜成年片在线观看| 亚洲中文精品字幕在线观看| 免费在线黄色观看网站| 亚洲人人妻一区二区三区| 五月激情婷婷久久综合网| 五十路老熟女码av| 亚洲伊人av天堂有码在线| 青青青青青手机视频| 天天日天天干天天搡| 日本韩国免费一区二区三区视频 | 国产又粗又硬又大视频| 天天干天天插天天谢| 一色桃子久久精品亚洲| 欧美xxx成人在线| 在线网站你懂得老司机| 搞黄色在线免费观看|