精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

參透Node中exports的7種設計模式

開發 前端
這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的文章。但因為這篇文章有些時日了,部分示例已經不符合現況。故這是一篇加上小弟收集匯整而成的更新翻譯。

前言

這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的文章。

但因為這篇文章有些時日了,部分示例已經不符合現況。故這是一篇加上小弟收集匯整而成的更新翻譯。

旅程的開始

當你在Node中加載一個模塊,我們到底會取回什么?當我們撰寫一個模塊時我們又有哪些選擇可以用來設計程序的界面?

在我***次學習Node的時候,發現在Node中有太多的方式處理這個問題,由于Javascript本身非常彈性,加上在社群中的開發者們各自都有不同的實作風格,這讓當時的我感到有點挫折。

在原文作者的學習旅程中曾持續的觀察尋找好的方式以應用在其的工作上,在這篇文章中將會分享觀察到的Node模塊設計方式。

大略總結了7種設計模式(pattern)

  • 導出命名空間Namespace
  • 導出函式Function
  • 導出高階函式High-Order Function
  • 導出構造函數/構建函式Constructor
  • 導出單一實例物件Singleton
  • 擴展全局物件Extend Global Object
  • 套用猴子補丁Monkey Patch

require,exports和module.exports

首先我們需要先聊點基礎的知識

在Node官方文件中定義了匯入一個檔案就是匯入一個模塊。

In Node.js,files and modules are in one-to-one correspondence.- Node文件

也就是所有的模塊會參考指向(Reference)一個隱式模塊物件的module.exports。當我們使用require()時會取得的東西。同時我們也取得exports。

這個exports就是指向module.exports的參考。exports會收集該其屬性,如果module.exports沒有任何屬性就把這些數據交給module.exports,但如果module.exports已經具備屬性的話,那么exports的所有數據都會被忽略。

為了讓您更好理解關于exports與module.exports下面的示例提供了較詳細的說明 

  1. var a = { id: 1 } 
  2. var b = a 
  3. console.log(a)// {id: 1} 
  4. console.log(b)// {id: 1} 
  5.  
  6. // b參考指向a,意味著修改b的屬性a會跟著變動 
  7. b.id = 2 
  8. console.log(a)// {id: 2} 
  9. console.log(b)// {id: 2} 
  10.  
  11. //但如果將一個全新的物件賦予b那么參考的關系將會中斷 
  12. b = { id: 3 } 
  13. console.log(a)// {id: 2} 
  14. console.log(b)// {id: 3}  

另外比較具體的示例 

  1. /* person.js */ 
  2. exports.name = function(){ 
  3.  console.log('My name is andyyou.') 
  4. … 
  5. /* main.js */ 
  6. var person = require('./person.js') 
  7. person.name() 
  8. /* person.js */ 
  9. module.exports = 'Hey,andyyou' 
  10. exports.name = function(){ 
  11.  console.log('My name is andyyou') 
  12.  
  13. /* main.js */ 
  14. var person = require('./person.js') 
  15. // exports的屬性被忽略了 
  16. person.name()// TypeError: Object Hey,andyyou has no method 'name'  
  • exports只是指向module.exports的參考(Reference)
  • module.exports初始值為{}空物件,于是exports也會取得該空物件
  • require()回傳的是module.exports而不是exports
  • 所以您可以使用exports.property_name = something而不會使用exports = something
  • 一旦使用exports = something參考關系便會停止,也就是exports的數據都會被忽略。

本質上我們可以理解為所有模塊都隱含實作了下面這行代碼

  1. var exports = module.exports = {} 

現在我們知道了,當我們要導出一個function時我們得使用module.exports。

如果使用exports那個exports的內存位置(Reference/參考)將會被修改而module.exports就不會得到其內容。

另外,我們在許多項目看到下面的這行代碼

  1. exports = module.exports = something 

這行代碼作用就是確保exports在module.exports被我們復寫之后,仍可以指向相同的參考。

接著我們就可以透過module.exports來定義并導出一個function

  1. /* function.js */ 
  2. module.exports = function(){ 
  3.  return { name'andyyou' } 
  4.  

使用的方式則是

  1. var func = require('./function'

關于require一個很重要的行為就是它會緩存(Cache)module.exports的值,未來每一次require被調用時都會回傳相同的值。

它會根據匯入檔案的絕對路徑來緩存,所以當我們想要模塊能夠回傳不同得值時,我們就需要導出function,如此一來每次執行函式時就會回傳一個新值。

下面在Node REPL中簡易的示范 

  1. $ node 
  2. > f1 = require('/Users/andyyou/Projects/export_this/function') 
  3. [Function
  4. > f2 = require('./function')//相同路徑 
  5. [Function
  6. > f1 === f2 
  7. true 
  8. > f1()=== f2() 
  9. false  

您可以觀察到require回傳了同樣的函式物件實例,但每一次調用函式回傳的物件是不同的。

更詳細的介紹可以參考官方文件,值得一讀。

現在,我們可以開始探討界面的設計模式(pattern)了。

導出命名空間

一個簡單且常用的設計模式就是導出一個包含數個屬性的物件,這些屬性具體的內容主要是函式,但并不限于函式。

如此,我們就能夠透過匯入該模塊來取得這個命名空間下一系列相關的功能。

當您匯入一個命名空間類型的模塊時,我們通常會將模塊指定到某一個變數,然后透過它的成員(物件屬性)來存取使用這些功能。

甚至我們也可以將這些變數成員直接指定到區域變數。

  1. var fs = require('fs') 
  2. var readFile = fs.readFile 
  3. var ReadStream = fs.ReadStream 
  4.  
  5. readFile('./file.txt'function(err,data){ 
  6.  console.log('readFile contents: %s',data) 
  7. })  

這便是fs核心模塊的做法

  1. var fs = exports 

首先將隱式exports物件設定一個區域變數(即上面提過的exports)到fs,然后透過fs的屬性使用各個功能,例如:fs.Stats = binding.Stats。

由于fs參考exports并且它是一個物件,所以當我們require('fs')時,我們就能夠透過屬性使用那些功能。

  1. fs.readFile = function(path,options,callback_){ 
  2.  //… 
  3.  

其他東西也是一樣的作法,例如導出構造函數 

  1. fs.ReadStream = ReadStream 
  2. function ReadStream(path,options){ 
  3.  //… 
  4. ReadStream.prototype.open = function(){ 
  5.  //… 
  6.  

當導出命名空間時,您可以指定屬性到exports,就像fs的作法,又或者可以建立一個新的物件指派給module.exports 

  1. /* exports作法*/ 
  2. exports.verstion = '1.0' 
  3.  
  4. /*或者module.exports作法*/ 
  5. module.exports = { 
  6.  version: '1.0', 
  7.  doYourTasks: function(){ 
  8.  //… 
  9.  } 
  10.  

一個常見的作法就是透過一個根模塊(root)來匯整并導出其他模塊,如此一來只需要一個require便可以使用所有的模塊。

原文作者在Good Eggs工作時,會將數據模型(Model)拆分成個別的模塊,并使用導出構造函數的方式導出(請參考下文介紹),然后透過一個index檔案來集合該目錄下所有的數據模型并一起導出,如此一來在models命名空間下的所有數據模型都可以使用 

  1. var models = require('./models') 
  2. var User = models.User 
  3. var Product = models.Product  

在ES2015和CoffeeScript中我們甚至還可以使用解構指派來匯入我們需要的功能 

  1. /* CoffeeScript */ 
  2. {User,Product} = require './models' 
  3.  
  4. /* ES2015 */ 
  5. import {User,Product} from './models'  

而剛剛提到的index.js大概就如下

  1. exports.User = require('./User') 
  2. exports.Person = require('./person')  

實際上這樣分開的寫法還有更精簡的寫法,我們可以透過一個小小的函式庫來匯入在同一階層中所有檔案并搭配CamelCase的命名規則導出。

于是在我們的index.js中看起來就會如下 

  1. module.exports = require('../lib/require_siblings')(__filename) 

導出函式

另外一個設計模式是導出函式當作該模塊的界面。常見的作法是導出一個工廠函式(Factory function),然后呼叫并回傳一個物件。

在使用Express.js的時候便是這種作法

  1. var express = require('express') 
  2. var app = express() 
  3.  
  4. app.get('/hello'function(req,res,next){ 
  5.  res.send('Hi there!We are using Express v' + express.version) 
  6. })  

Express導出該函式,讓我們可以用來建立一個新的express應用程序。

在使用這種模式時,通常我們會使用factory function搭配參數讓我們可以設定并回傳初始化后的物件。

想要導出function,我們就一定要使用module.exports,Express便是這么做 

  1. exports = module.exports = createApplication  
  2. … 
  3. function createApplication(){ 
  4. … 
  5.  

上面指派了createApplication函式到module.exports然后再指給exports確保參考一致。

同時Express也使用下面這種方式將導出函式當作命名空間的作法使用。 

  1. exports.version = '3.1.1' 

這邊要大略解釋一下由于Javascript原生并沒有支持命名空間的機制,于是大部分在JS中提到的namespace指的就是透過物件封裝的方式來達到namespace的效果,也就是***種設計模式。

注意!并沒有任何方式可以阻止我們將導出的函式作為命名空間物件使用,我們可以用其來引用其他的function,構造函數,物件。

Express 3.3.2 / 2013-07-03之后已經將exports.version移除了

另外在導出函式的時候***為其命名,如此一來當出錯的時候我們比較容易從錯誤堆疊信息中找到問題點。

下面是兩個簡單的例子: 

  1. /* bomb1.js */ 
  2. module.exports = function(){ 
  3.  throw new Error('boom') 
  4. module.exports = function bomb(){ 
  5.  throw new Error('boom') 
  6. $ node 
  7. > bomb = require('./bomb1'); 
  8. [Function
  9. > bomb() 
  10. Error: boom 
  11.  at module.exports(/Users/andyyou/Projects/export_this/bomb1.js:2:9) 
  12.  at repl:1:2 
  13. … 
  14. > bomb = require('./bomb2'); 
  15. [Function: bomb] 
  16. > bomb() 
  17. Error: boom  
  18.  at bomb(/Users/andyyou/Projects/export_this/bomb2.js:2:9) 
  19.  at repl:1:2 
  20. …  

導出函式還有些比較特別的案例,值得用另外的名稱以區分它們的不同。

導出高階函式

一個高階函式或functor基本上就是一個函式可以接受一個或多個函式為其輸入或輸出。而這邊我們要談論的后者-一個函式回傳函式

當我們想要模塊能夠根據輸入控制回傳函式的行為時,導出一個高階函式就是一種非常實用的設計模式。

補充:functor & monad

舉例來說Connect就提供了許多可掛載的功能給網頁框架。

這里的middleware我們先理解成一個有三個參數(req,res,next)的function。

Express從v4.x版之后不再相依于connect

connect middleware慣例就是導出的function執行后,要回傳一個middleware function。

在處理request的過程中這個回傳的middleware function就可以接手使用剛剛提到的三個參數,用來在過程中做一些處理或設定。

同時因為閉包的特性這些設定在整個中間件的處理流程中都是有效的。

舉例來說,compression這個middleware就可以在處理responsive過程中協助壓縮 

  1. var connect = require('connect') 
  2. var app = connect() 
  3.  
  4. // gzip outgoing responses 
  5. var compression = require('compression') 
  6. app.use(compression())  

而它的原始碼看起來就如下 

  1. module.exports = compression 
  2. … 
  3. function compression(options){ 
  4. … 
  5.  return function compression(req,res,next){ 
  6. … 
  7.  next() 
  8.  } 
  9.  

于是每一個request都會經過compression middleware處理,而代入的options也因為閉包的關系會被保留下來

這是一種***彈性的模塊作法,也可能在您的開發項目上幫上許多忙。

middleware在這里您可以大略想成串連執行一系列的function,自然其Function Signature要一致

導出構造函數

在一般面向對象語言中,constructor構造函數指的是一小段代碼協助我們從類別Class建立一個物件。 

  1. // C# 
  2. class Car { 
  3.  // c#構造函數 
  4.  // constructor即class中用來初始化物件的method。 
  5.  public Car(name){ 
  6.  name = name; 
  7.  } 
  8. var car = new Car('BMW');  

由于在ES2015之前Javascript并不支持類別,某種程度上在Javascript之中我們可以把任何一個function當作類別,或者說一個function可以當作function執行或者搭配new關鍵字當作constructor來使用。如果想知道更詳細的介紹可以閱讀MDN教學。

欲導出構造函數,我們需要透過構造函式來定義類別,然后透過new來建立物件實例。 

  1. function Person(name){ 
  2.  this.name = name 
  3.  
  4. Person.prototype.greet = function(){ 
  5.  return 'Hi,I am ' + this.name 
  6.  
  7. var person = new Person('andyyou') 
  8. console.log(person.greet())// Hi,I am andyyou  

在這種設計模式底下,我們通常會將每個檔案設計成一個類別,然后導出構造函數。這使得我們的項目構架更加清楚。 

  1. var Person = require('./person') 
  2. var person = new Person()  

整個檔案看起來會如下 

  1. /* person.js */ 
  2. function Person(name){ 
  3.  this.name = name 
  4.  
  5. Person.prototype.greet = function(){ 
  6.  return 'Hi,I am ' + this.name 
  7.  
  8. exports = module.exports = Person  

導出單一物件實例Signleton

當我們需要所有的模塊使用者共享物件的狀態與行為時,就需要導出單一物件實例。

Mongoose是一個ODM(Object-Document Mapper)函式庫,讓我們可以使用程序中的Model物件去操作MongoDB。 

  1. var mongoose = require('mongoose') 
  2. mongoose.connect'mongodb://localhost/test') 
  3.  
  4. var Cat = mongoose.model('Cat',{name: String}) 
  5.  
  6. var kitty = new Cat({name'Zildjian'}) 
  7. kitty.save(function(err){ 
  8.  if(err) 
  9.  throw Error('save failed') 
  10.  console.log('meow') 
  11. }) 

 那我們require取得的mongoose物件是什么東西呢?事實上mongoose模塊的內部是這么處理的 

  1. function Mongoose(){ 
  2. … 
  3.  
  4. module.exports = exports = new Mongoose()  

因為require的緩存了module.exports的值,于是所有reqire('mongoose')將會回傳相同的物件實例,之后在整個應用程序之中使用的都會是同一個物件。

Mongoose使用面向對象的設計模式來封裝,解耦(分離功能之間的相依性),維護狀態使整體具備可讀性,同時透過導出一個Mongoose Class的物件給使用者,讓我們可以簡單的存取使用。

如果我們有需要,它也可以建立其他的物件實例來作為命名空間使用。實際上Mongoose內部提供了存取構造函數的方法 

  1. Mongoose.prototype.Mongoose = Mongoose 

因此我們可以這么做 

  1. var mongoose = require('mongoose'
  2.  
  3. var Mongoose = mongoose.Mongoose 
  4.  
  5. var anotherMongoose = new Mongoose() 
  6.  
  7. anotherMongoose.connect('mongodb://localhost/test' 

擴展全局物件

一個被匯入的模塊不只限于單純取得其導出的數據。它也可以用來修改全局物件或回傳全局物件,自然也能定義新的全局物件。而在這邊的全局物件(Global objects)或稱為標準內置物件像是Object,Function,Array指的是在全局能存取到的物件們,而不是當Javascript開始執行時所產生代表global scope的global object。

當我們需要擴增或修改全局物件預設行為時就需要使用這種設計模式。當然這樣的方式是有爭議,您必須謹慎使用,特別是在開放原始碼的項目上。

例如:Should.js是一個常被用在單元測試中用來判斷分析值是否正確的函式庫。 

  1. require('should') 
  2.  
  3. var user = { 
  4.  name'andyyou' 
  5.  
  6. user.name.should.equal('andyyou')  

這樣您是否比較清楚了,should.js增加了底層的Object的功能,加入了一個非列舉型的屬性 should,讓我們可以用簡潔的語法來撰寫單元測試。

而在內部should.js做了這樣的事情 

  1. var should = function(obj){ 
  2.  return new Assertion(util.isWrapperType(obj)?obj.valueOf():obj) 
  3. … 
  4. exports = module.exports = should 
  5.  
  6. Object.defineProperty(Object.prototype,'should',{ 
  7.  setfunction(){}, 
  8.  get: function(){ 
  9.  return should(this); 
  10.  }, 
  11.  configurable: true 
  12. });  

就算看到這邊您肯定跟我一樣有滿滿的疑惑,全局物件擴展定義跟exprots有啥關聯呢?

事實上 

  1. /* whoami.js */ 
  2. exports = module.exports = { 
  3.  name'andyyou' 
  4.  
  5. Object.defineProperty(Object.prototype,'whoami',{ 
  6.  setfunction(){}, 
  7.  get: function(){ 
  8.  return 'I am ' + this.name 
  9.  } 
  10. }) 
  11.  
  12. /* app.js */ 
  13. var whoami = require('whoami') 
  14. console.log(whoami)// { name'andyyou' } 
  15.  
  16. var obj = { name'lena' } 
  17. console.log(obj.whoami)// I am lena  

現在我們明白了上面說的修改全局物件的意思了。should.js導出了一個should函式但是它主要的使用方式則是把should加到Object屬性上,透過物件本身來呼叫。

套用猴子補丁(Monkey Patch)

在這邊所謂的猴子補丁特別指的是在執行時期動態修改一個類別或者模塊,通常會這么做是希望補強某的第三方套件的bug或功能。

假設某個模塊沒有提供您客制化功能的界面,而您又需要這個功能的時候,我們就會實作一個模塊來補強既有的模塊。

這個設計模式有點類似擴展全局物件,但并非修改全局物件,而是依靠Node模塊系統的緩存機制,當其他代碼匯入該模塊時去補強該模塊的實例物件。

預設來說Mongoose會使用小寫以及復數的慣例替數據模型命名。例如一個數據模型叫做CreditCard最終我們會得到collection的名稱是creditcards。假如我們希望可以換成credit_cards并且其他地方也遵循一樣的用法。

下面是我們試著使用猴子補丁的方式來替既有的模塊增加功能 

  1. var pluralize = require('pluralize')//處理復數單字的函式庫 
  2. var mongoose = require('mongoose') 
  3. var Mongoose = mongoose.Mongoose 
  4.  
  5. mongoose.Promise = global.Promise // v4.1+ http://mongoosejs.com/docs/promises.html 
  6. var model = Mongoose.prototype.model 
  7.  
  8. //補丁 
  9. var fn = functionnameschema,collection,skipInit){ 
  10.  collection = collection || pluralize.plural(name.replace(/([a-z\d])([A-Z])/g,'$1_$2').toLowerCase()) 
  11.  return model.call(this,nameschema,collection,skipInit) 
  12. Mongoose.prototype.model = fn 
  13.  
  14. /*實際測試*/ 
  15. mongoose.connect'mongodb://localhost/test') 
  16. var CreditCardSchema = new mongoose.Schema({number: String}) 
  17. var CreditCardModel = mongoose.model('CreditCard',CreditCardSchema); 
  18.  
  19. var card = new CreditCardModel({number: '5555444433332222'}); 
  20. card.save(function(err){ 
  21.  if(err){ 
  22.  console.log(err) 
  23.  } 
  24.  console.log('success') 
  25. })  

您不該輕易使用上面這種方式補丁,這邊只是為了說明猴子補丁這種方式,mongoose已經有提供官方的方式設定名稱 

  1. var schema = new Schema({..},{ collection: 'your_collection_name' }) 

當這個模塊***次被匯入的時候便會讓mongoose重新定義Mongoose.prototype.model并將其設回原本的model的實作。

如此一來所有Mongoose的實例物件都具備新的行為了。注意到這邊并沒有修改exports所以當我們require的時候得到的是預設的物件

另外如果您想使用上面這種補丁的方式時,記得閱讀原始碼并注意是否產生沖突。

請善用導出的功能

Node模塊系統提供了一個簡單的機制來封裝功能,使我們能夠建立了清楚的界面。希望掌握這七種設計模式提供不同的優缺點能對您有所幫助。

在這邊作者并沒有徹底的調查所有的方式,一定有其他選項可供選擇,這邊只有描述幾個最常見且不錯的方法。

小結

  • namespace:導出一個物件包含需要的功能

root module的方式,使用一個根模塊導出其他模塊

  • function:直接將module.exports設為function

Function物件也可以拿來當作命名空間使用

為其命名方便調試

exports = module.exports = something的作法是為了確保參考(Reference)一致

  • high-order function:可以透過代入參數控制并回傳function。

可協助實作middleware的設計模式

換句話說middleware即一系列相同signature的function串連。一個接一個執行

  • constructor:導出類別(function),使用時再new,具備OOP的優點
  • singleton:導出單一物件實例,重點在各個檔案可以共享物件狀態
  • global objects:在全局物件作的修改也會一起被導出
  • monkey patch:執行時期,利用Node緩存機制在instance加上補丁

筆記

  • 一個javascript檔案可視為一個模塊
  • 解決特定問題或需求,功能完整由單一或多個模塊組合而成的整體稱為套件(package)
  • require匯入的模塊具有自己的scope
  • exports只是module.exports的參考,exports會記錄收集屬性如果module.exports沒有任何屬性就把其數據交給module.exports,但如果module.exports已經具備屬性的話,那么exports的所有數據都會被忽略。
  • 就算exports置于后方仍會被忽略
  • Node初始化的順序

Native Module -> Module

StartNodeInstance()-> CreateEnvironment()-> LoadEnvironment()-> Cached

  • Native Module加載機制

檢查是否有緩存

->有;直接回傳this.exports

->沒有;new一個模塊物件

cache()

compile()-> NativeModule.wrap()將原始碼包進function字串->runInThisContext()建立函式

return NativeModule.exports

  • Node的require會cache,也就是說:如果希望模塊產生不同的instance時應使用function
責任編輯:龐桂玉 來源: segmentfault
相關推薦

2021-02-19 14:07:03

JavaScript編程開發

2009-06-29 18:11:40

JSP設計模式

2009-01-04 13:49:17

Java設計模式設計模式工廠模式

2023-09-22 11:58:49

2020-10-14 13:58:14

23種設計模式速記

2015-09-14 09:31:44

結對設計

2020-10-09 06:52:31

設計模式軟件

2022-05-27 11:33:02

前端代碼設計模式

2025-08-01 07:55:56

2009-06-15 14:15:07

Java設計模式Java

2023-05-15 15:29:13

設計模式JavaScript

2021-04-09 20:38:20

Vue模式.前端

2018-08-29 10:04:43

2012-08-30 09:07:33

設計模式

2024-07-31 08:12:33

2021-04-18 21:07:32

門面模式設計

2023-10-26 09:02:30

框架設計模式

2020-11-08 16:04:03

開發工具技術

2020-11-18 08:15:39

TypeScript設計模式

2012-05-28 09:16:12

Java設計模式
點贊
收藏

51CTO技術棧公眾號

26uuu亚洲电影在线观看| 亚洲最新av网站| 国产麻豆精品久久| 欧美日韩精品系列| 日本五级黄色片| 成年人在线观看视频| 韩国成人福利片在线播放| 97国产精品免费视频| 国产jizz18女人高潮| 精品久久ai| 欧美一区二区在线看| 亚洲精品无码久久久久久| 国产cdts系列另类在线观看| 久久一日本道色综合| 亚洲专区中文字幕| 久久人人爽人人爽人人片av免费| 欧美极品一区二区三区| 中文欧美在线视频| 免费的av网站| 久久综合偷偷噜噜噜色| 在线亚洲一区二区| a在线视频观看| 午夜伦理在线视频| 一区二区中文视频| 日韩欧美激情一区二区| 日韩中文字幕观看| 国产乱色国产精品免费视频| 国产精品自产拍高潮在线观看| 青青操免费在线视频| 韩国自拍一区| 欧美成人免费小视频| 少妇视频一区二区| 欧美伦理在线视频| 亚洲深夜福利在线| 中文字幕一区二区久久人妻网站 | 成人精品一区二区三区校园激情| 成人久久18免费网站麻豆| 91精品在线观看视频| 羞羞色院91蜜桃| 视频一区二区中文字幕| 欧美在线视频一二三| 国产一级久久久| 午夜视频一区| 免费不卡在线观看av| 国产探花在线视频| 天天av综合| 日韩一区二区三区xxxx| 激情高潮到大叫狂喷水| 欧美系列电影免费观看| 中文字幕精品在线视频| 黄色片在线观看免费| 欧美人与拘性视交免费看| 亚洲美女性视频| 一区二区黄色片| 牲欧美videos精品| 国产亚洲美女精品久久久| 丁香激情五月少妇| 欧美国产小视频| 精品国产一区二区三区久久久| 超碰人人人人人人人| 日韩欧美二区| 久久久成人av| 麻豆亚洲av熟女国产一区二| 亚洲第一区色| 欧美一级视频免费在线观看| 中文字幕一区在线播放| 日韩avvvv在线播放| 国产裸体写真av一区二区| 91在线精品入口| 国产精品一区二区黑丝| 国产成人精品一区二区三区福利 | 欧美24videosex性欧美| 三级久久三级久久| 国产成人一区二区在线| 中文字幕一区二区三区波野结 | 国产亚洲电影| 在线观看欧美www| 在线视频这里只有精品| 91精品啪在线观看国产81旧版 | 国产一区二区精彩视频| 欧美日韩国产免费观看| 91av国产在线| 中文字幕日韩国产| 国产超碰在线一区| 欧美日韩精品久久久免费观看| 成人免费高清在线播放| 一区二区三区精密机械公司| 国产3p露脸普通话对白| 91精品影视| 日韩免费视频一区二区| 少妇精品一区二区三区| 99久久www免费| 97人人做人人爱| 99成人精品视频| 高清成人免费视频| 日韩欧美在线一区二区| 1区2区在线观看| 欧美性猛交xxxx免费看漫画| 成年网站免费在线观看| 极品束缚调教一区二区网站| 中文字幕日韩av| 日韩毛片在线视频| 精品一区二区三区日韩| 精品亚洲第一| 26uuu亚洲电影在线观看| 色综合久久天天综合网| 中文字幕人妻无码系列第三区| 九九久久婷婷| 久久久久国产视频| 中文字幕一区二区在线视频| 成人av影院在线| 黄瓜视频免费观看在线观看www| 色多多在线观看| 欧美一区二区播放| 精品人妻无码一区二区三区| 黄色日韩在线| 国产日韩在线免费| 成人在线免费看| 亚洲成年人影院| 亚洲三级在线观看视频| 狠狠做深爱婷婷综合一区| 亚州欧美日韩中文视频| 精品人妻无码一区二区三区蜜桃一| 国产片一区二区| 能在线观看的av| 国产精品成人自拍| 欧美乱大交xxxxx另类电影| 免费av中文字幕| 91在线精品一区二区| 国产爆乳无码一区二区麻豆| 欧美三级电影网址| 综合网日日天干夜夜久久| 久草手机在线观看| av一本久道久久综合久久鬼色| 亚洲色婷婷久久精品av蜜桃| 欧美高清xxx| 色老头一区二区三区| 国产精品51麻豆cm传媒| 久久精品在这里| aa免费在线观看| 日韩a级大片| 高清欧美一区二区三区 | 国产婷婷97碰碰久久人人蜜臀| 久久久久人妻一区精品色欧美| 极品尤物av久久免费看| 亚洲欧美日本国产有色| 欧美v亚洲v综合v国产v仙踪林| 亚洲视频一区二区三区| 久操视频在线免费观看| 国产午夜亚洲精品不卡| 午夜激情福利在线| 欧美理论视频| 国产日韩综合一区二区性色av| 69av在线| 日韩一区二区三区精品视频| 中文字幕亚洲欧美日韩| 国产精品一区二区黑丝| 国产一线二线三线女| 久久91在线| 欧美亚洲日本网站| 久久久久国产精品嫩草影院| 91久久精品一区二区| 欧洲性xxxx| 经典三级在线一区| 精品视频在线观看一区二区| 成人性生交大片免费看中文视频| 高清欧美电影在线| 精品美女视频在线观看免费软件 | 亚洲国产精品国自产拍av| 国产又黄又猛又粗| 久久久久久久久久久妇女| 999热视频| 日韩大片免费观看| 伊人一区二区三区久久精品| 亚洲午夜激情视频| 亚洲一区视频在线观看视频| 亚洲av成人片色在线观看高潮 | 国产aⅴ精品一区二区三区久久| 国产精品久久久久久久久借妻| 免费高清完整在线观看| 精品久久五月天| 日本中文字幕第一页| 中文字幕一区二区三区在线不卡 | 久久的精品视频| 欧美 日韩 国产 在线| 色哟哟一区二区在线观看 | 婷婷久久一区| 国产精品一区二区免费| 亚洲电影有码| 久久久久久久av| aaa在线免费观看| 精品国产网站在线观看| 日韩欧美一级大片| 亚洲国产综合人成综合网站| 一级黄色性视频| 国产91精品入口| 免费看污污网站| 亚洲性色视频| 永久域名在线精品| 视频福利一区| 国产成人免费电影| 欧美aaaaaa| 日本中文字幕久久看| 久草在线资源站资源站| 日韩中文字幕在线看| 色视频精品视频在线观看| 日韩午夜在线影院| 中文字幕av影视| 狠狠躁夜夜躁人人爽天天天天97| 少妇被躁爽到高潮无码文| 亚洲国产岛国毛片在线| 久久人妻少妇嫩草av无码专区| 国产一区二区三区综合| 亚洲第一中文av| 香蕉视频成人在线观看| 日本黄色片一级片| 亚洲一本二本| 亚洲一二区在线| 久久最新网址| 精品国产一区二区三区四区精华 | 欧美二区三区91| 久久精品视频2| 欧美日韩免费区域视频在线观看| 波多野结衣亚洲色图| **网站欧美大片在线观看| 亚洲成人网在线播放| 成人性生交大片免费看中文网站 | 91视视频在线观看入口直接观看www| 女王人厕视频2ⅴk| 国产毛片精品视频| 日韩av卡一卡二| 奇米在线7777在线精品| 熟女人妇 成熟妇女系列视频| 性久久久久久| 2022亚洲天堂| 国产精品日韩| 日本日本19xxxⅹhd乱影响| 国内精品久久久久久久97牛牛| 欧美视频在线第一页| 欧美国产高潮xxxx1819| 日韩中文字幕在线不卡| 欧美特黄视频| 欧美国产日韩激情| 亚洲免费成人| 免费欧美一级视频| 久久精品123| 成人性生生活性生交12| 热久久免费视频| 日韩av在线中文| 国产一区二区精品久久| 超级砰砰砰97免费观看最新一期| 国产呦精品一区二区三区网站| 黑人性生活视频| 国产成人h网站| 亚洲啪av永久无码精品放毛片 | 制服丝袜中文字幕一区| 97在线视频人妻无码| 日韩亚洲欧美一区| 欧美视频久久久| 日韩久久精品成人| 成人激情电影在线看| 日韩中文字幕视频在线| av在线网址观看| 久久免费国产视频| 不卡福利视频| 国产主播喷水一区二区| 国产激情一区| 国产精品欧美久久| 久久不见久久见免费视频7| 亚州欧美一区三区三区在线| 久久久9色精品国产一区二区三区| 9色视频在线观看| 一本久道久久综合狠狠爱| 国产精品乱码久久久久| 国产在线不卡一区| 性久久久久久久久久久| 中文字幕免费在线观看视频一区| 中日韩一级黄色片| 亚洲成av人片| 中文字幕一区二区在线视频| 日韩欧美激情四射| 国产在线视频资源| 美女久久久久久久| 欧美一级大片| av资源站久久亚洲| 欧美日韩在线网站| 妺妺窝人体色www看人体| 日韩和欧美一区二区| 久久综合在线观看| 久久综合色鬼综合色| 国产一二三四区| 日韩欧美在线播放| 性一交一乱一伧老太| 亚洲色图狂野欧美| 男男gaygays亚洲| 国产精品视频专区| 美女呻吟一区| 日本福利视频在线观看| 久久婷婷久久| 美女久久久久久久久| 国产精品美女www爽爽爽| 婷婷激情五月网| 日韩一级片网站| av电影在线网| 欧美最猛性xxxxx亚洲精品| 亚洲国产aⅴ精品一区二区| 任我爽在线视频精品一| 在线看片成人| 国产美女视频免费看| 国产午夜久久久久| 久草国产精品视频| 欧美一级xxx| 免费av在线播放| 国产成人精品久久亚洲高清不卡| jizzjizzjizz欧美| 最近中文字幕免费mv| 日韩成人免费在线| 国产 欧美 在线| 天天操天天干天天综合网| 国产aⅴ爽av久久久久成人| 最近中文字幕2019免费| 成人va天堂| 欧美日韩精品一区| 久久aⅴ乱码一区二区三区| 丝袜熟女一区二区三区| 亚洲一区在线看| 亚洲va欧美va| 欧美成人午夜激情视频| 成人在线啊v| 一区二区欧美日韩| 秋霞成人午夜伦在线观看| 成人性生交大免费看| 欧美视频专区一二在线观看| 视频二区在线| 456国产精品| 久9久9色综合| 久久久久久久久久福利| 久久综合99re88久久爱| 一区二区三区福利视频| 日韩电影网在线| 久久电影tv| 色噜噜狠狠一区二区三区| 三级久久三级久久久| 亚洲а∨天堂久久精品2021| 欧美亚洲高清一区| 国产www.大片在线| 国产狼人综合免费视频| 99久久99热这里只有精品| 欧美性猛交xxxx乱大交91| 亚洲欧美另类小说视频| 国产黄a三级三级看三级| 欧美大片在线影院| 岛国精品一区| 欧美黄网站在线观看| 久久久久久久久久久电影| 国语对白做受69按摩| 色老头一区二区三区在线观看| 爱情电影网av一区二区| 国产乱人伦精品一区二区三区| 成人做爰69片免费看网站| 五月婷婷亚洲综合| 亚洲日韩中文字幕在线播放| 国产91亚洲精品久久久| 中文字幕一区综合| 国产91在线|亚洲| 欧美黑人一区二区| 中日韩美女免费视频网站在线观看| 欧美日韩va| 国产手机免费视频| 久久精品一区二区三区不卡牛牛 | 99在线视频影院| 奇米视频888战线精品播放| 男女男精品视频| 国产一区二区三区在线视频观看| 精品成人免费观看| 成人免费看黄| 一区二区三区观看| 成人丝袜18视频在线观看| 免费一级a毛片| 欧美成人激情视频| 性欧美lx╳lx╳| 亚洲精品免费一区亚洲精品免费精品一区 | 性生交大片免费全黄| 精品国产一区二区三区av性色| 亚洲同志男男gay1069网站| 综合操久久久| 99久久99久久精品免费观看| 国产成人精品亚洲| 欧美人与物videos| 国产剧情一区| www.久久com| 91豆麻精品91久久久久久| 91小视频xxxx网站在线| 免费日韩电影在线观看| 国产一区欧美一区| 欧美一级淫片免费视频黄| 欧美大成色www永久网站婷| 国产一区二区三区探花| 色悠悠在线视频| 在线播放欧美女士性生活|