gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区

站長資訊網
最全最豐富的資訊網站

聊聊JavaScript中實現繼承的6種方法

聊聊JavaScript中實現繼承的6種方法

前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調試工具:點擊使用

面試官:“你說說 JavaScript 中實現繼承有哪幾種方法?”

緊張的萌新:“額,class 中用 extends 實現繼承,然后…沒了…”

面試官:“…”

······

想必絕大部分人一說繼承就會想到類中的繼承吧,但其實繼承可不是 class 的專利,本文將總結,JavaScript 中關于繼承的幾種方案,其中包括原型鏈,盜用構造函數、組合式等等,助你力壓面試官。

注意:本文比較適合具備一定 JS 進階基礎的同學(不會也沒關系,收藏就會了?),涉及知識點有:原型、原型鏈、構造函數、this指向等。如果文中有不對、疑惑的地方,歡迎在評論區留言指正?

0. 繼承

繼承是面向對象編程中討論最多的話題。很多面向對象語言都支持兩種繼承:接口繼承和實現繼承。前者只繼承方法簽名,后者繼承實際的方法。 接口繼承在 ECMAScript中 是不可能的,因為函數沒有簽名。實現繼承是 ECMAScript 唯一支持的繼承方式,而這主要是通過原型鏈實現的。

1. 原型鏈繼承【方案一】

ECMA-262 把原型鏈定義為 ECMAScript 的主要繼承方式。其基本思想就是通過原型繼承多個引用類型的屬性和方法。重溫一下構造函數、原型和實例的關系:

  • 每個構造函數都有一個prototype屬性指向原型對象
  • 所有原型對象自動獲得一個名為 constructor 的屬性,指回與之關聯的構造函數

而實例有一個內部指針指向原型。如果原型是另一個類型的實例呢?那就意味著這個原型本身有一個內部指針指向另一個原型,相應的另一個原型也有一個指針指向另一個構造函數。這樣就在實例和原型之間構造了一條原型鏈。這就是原型鏈的基本構想。

實現原型鏈繼承涉及如下代碼模式

// 定義 Person 構造函數 function Person() {   this.name = 'CoderBin' }  // 給 Person 的原型上添加 getPersonValue 方法(原型方法) Person.prototype.getPersonValue = function() {   return this.name }  // 定義 Student 構造函數 function Student() {   this.sno = '001' }  // 繼承 Person — 將 Peson 的實例賦值給 Student 的原型 Student.prototype = new Person()  Student.prototype.getStudentValue = function() {   return this.sno }  // 實例化 Student let stu = new Student()  console.log(stu.getPersonValue()) // CoderBin
登錄后復制

1.1 代碼解讀

以上代碼定義了兩個構造函數:Person 和 Student。這兩個構造函數分別定義了一個屬性和一個方法。

這兩個類型的主要區別是 Student 通過創建 Person 的實例并將其賦值給自己的原型 Student.prototype 實現了對 Person 的繼承。

這個賦值重寫了 Student 最初的原型,將其替換為 Person 的實例。這意味著 Person 實例可以訪問的所有屬性和方法也會存在于Student.prototype。這樣實現繼承之后,代碼緊接著又給Student.prototype,也就是這個 Person 的實例添加了 一個新方法。最后又創建了 Student 的實例并調用了它繼承的getPersonValue()方法。

下圖展示了子類的實例與兩個構造函數及其對應的原型之間的關系:

聊聊JavaScript中實現繼承的6種方法

1.2 代碼核心解析

這個例子中實現繼承的關鍵,是 Student 沒有使用默認原型,而是將其替換成了一個新的對象。這個新的對象恰好是 Person 的實例。這樣一來,Student 的實例不僅能從 Person 的實例中繼承屬性和方法,而且還與 Person 的原型掛上了鉤。于是 stu(通過內部的 [[Prototype]] )指向Student.prototype,而Student.prototype(作為 Person 的實例又通過內部的 [[Prototype]] )指向Person.prototype。

注意1:getPersonValue() 方法還在Person.prototype對象上,而 name 屬性則在Student.prototype上。這是因為 getPersonValue() 是一個原型方法,而 name 是一個實例屬性。Student.prototype現在是 Person 的一個實例,因此 name 才會存儲在它上面。

注意2:由于 Student.prototype 的 constructor 屬性被重寫為指向 Person,所以 stu.constructor 也指向 Person 。

1.3 默認原型

實際上,原型鏈中還有一環。默認情況下,所有引用類型都繼承自 Object ,這也是通過原型鏈實現的。任何函數的默認原型都是一個 Object 的實例,這意味著這個實例有一個內部指針指向Object.prototype。這也是為什么自定義類型能夠繼承包括 toString() 、valueOf() 在內的所有默認方法的原因。因此前面的例子還有額外一層繼承關系。

下圖展示了完整的原型鏈。

聊聊JavaScript中實現繼承的6種方法

Student 繼承 Person ,而 Person 繼承 Object 。在調用 stu.toString() 時,實際上調用的是保存在 Object.prototype 上的方法。

1.4 原型與繼承的關系

原型與實例的關系可以通過兩種方式來確定。

1.4.1 instanceof

第一種方式是使用instanceof操作符,如果一個實例的原型鏈中出現過相應的構造函數,則instanceof返回 true 。如下例所示:

console.log(stu instanceof Object)    // true console.log(stu instanceof Person)    // true console.log(stu instanceof Student)   // true
登錄后復制

從技術上講,stu 是 Object、Person 和 Student 的實例,因為 stu 的原型鏈中包含這些構造函數的原型。結果就是 instanceof 對所有這些構造函數都返回 true 。

1.4.2 isPrototypeOf()

確定這種關系的第二種方式是使用isPrototypeOf()方法。原型鏈中的每個原型都可以調用這個方法,如下例所示,只要原型鏈中包含這個原型,這個方法就返回 true 。

console.log(Object.prototype.isPrototypeOf(stu))    // true console.log(Person.prototype.isPrototypeOf(stu))    // true console.log(Student.prototype.isPrototypeOf(stu))   // true
登錄后復制

1.5 關于方法

子類有時候需要覆蓋父類的方法,或者增加父類沒有的方法。為此, 這些方法必須在原型賦值之后再添加到原型上。來看下面的例子:

// 定義 Person 構造函數 function Person() {   this.name = 'CoderBin' }  // 給 Person 的原型上添加 getPersonValue 方法(原型方法) Person.prototype.getPersonValue = function() {   return this.name }  // 定義 Student 構造函數 function Student() {   this.sno = '001' }  // 繼承 Person Student.prototype = new Person()  // 新方法 —— 1 Student.prototype.getStudentValue = function() {   return this.sno }  // 覆蓋已有的方法 —— 2 Student.prototype.getPersonValue = function() {   return 'Bin' }  // 實例化 Student let stu = new Student()  console.log(stu.getPersonValue()) // Bin
登錄后復制

在上面的代碼中,注釋1、2的部分涉及兩個方法。

  • 第一個方法 getStudentValue() 是 Student 的新方法,
  • 第二個方法 getPersonValue() 是原型鏈上已經存在但在這里被遮蔽的方法。

后面在 Student 實例上調用 getPersonValue() 時調用的是2這個方法。而 Person 的實例仍然會調用最初的方法。

重點一:上述兩個方法都是在把原型賦值為 Person 的實例之后定義的。

重點二:另一個要理解的重點是,以對象字面量方式創建原型方法會破壞之前的原型鏈,因為這相當于重寫了原型鏈。下面是一個例子:

// 定義 Person 構造函數 function Person() {   this.name = 'CoderBin' }  // 給 Person 的原型上添加 getPersonValue 方法(原型方法) Person.prototype.getPersonValue = function() {   return this.name }  // 定義 Student 構造函數 function Student() {   this.sno = '001' }  // 繼承 Person Student.prototype = new Person()  // 通過對象字面量添加新方法,這會導致上一行無效!??! Student.prototype = {   getStudentValue() {     return this.sno   },   someOtherMethod() {     return 'something'   } }  // 實例化 Student let stu = new Student()  console.log(stu.getPersonValue())  // TypeError: stu.getPersonValue is not a function
登錄后復制

在這段代碼中,子類的原型在被賦值為 Person 的實例后,又被一個對象字面量覆蓋了。覆蓋后的原型是一個Object 的實例,而不再是 Person 的實例。因此之前的原型鏈就斷了。Student 和 Person 之間也沒有關系了。

1.6 原型鏈繼承的缺陷

原型鏈雖然是實現繼承的強大工具,但它也有問題。

主要問題出現在原型中包含引用值的時候。前面在談到原型的問題時也提到過,原型中包含的引用值會在所有實例間共享,這也是為什么屬性通常會在構造函數中定義而不會定義在原型上的原因。在使用原型實現繼承時,原型實際上變成了另一個類型的實例【1】。這意味著原先的實例屬性搖身一變成為了原型屬性。下面的例子揭示了這個問題:

// 定義 Person 構造函數 function Person() {   this.letters = ['a', 'b', 'c'] }  // 定義 Student 構造函數 function Student() {   this.sno = '001' }  // 繼承 Person Student.prototype = new Person()  let stu1 = new Student() let stu2 = new Student()  stu1.letters.push('d')  console.log(stu1.letters)  // ['a', 'b', 'c', 'd'] console.log(stu2.letters)  // ['a', 'b', 'c', 'd']
登錄后復制

代碼解析: 在這個例子中,Person 構造函數定義了一個 letters 屬性,其中包含一個數組(引用值)。每個 Person 的實例都會有自己的 letters 屬性,包含自己的數組。但是,當 Student 通過原型繼承 Person 后,Student.prototype變成了 Person 的一個實例,因而也獲得了自己的 letters 屬性。這類似于創建了Student.prototype.letters 屬性。最終結果是,Student 的所有實例都會共享這個 letters 屬性。這一點通過 stu1.letters 上的修改也能反映到 stu2.letters 上就可以看出來。

原型鏈的第二個問題是,子類型在實例化時不能給父類型的構造函數傳參【2】。事實上,我們無法在不影響所有對象實例的情況下把參數傳進父類的構造函數。再加上之前提到的原型中包含引用值的問題,就導致原型鏈基本不會被單獨使用。

2. 盜用構造函數繼承【方案二】

為了解決原型包含引用值導致的繼承問題,一種叫作“盜用構造函數” (constructor stealing)的技術在開發社區流行起來(這種技術有時也稱作“對象偽裝”或“經典繼承”)?;舅悸泛芎唵危?strong>在子類構造函數中調用父類構造函數。 因為畢竟函數就是在特定上下文中執行代碼的簡單對象,所以可以使用apply()call()方法以新創建的對象為上下文執 行構造函數。來看下面的例子:

// 定義 Person 構造函數 function Person() {   this.letters = ['a', 'b', 'c'] }  // 定義 Student 構造函數 function Student() {   // 繼承 Person — 使用 call() 方法調用 Person 構造函數   Person.call(this) }  let stu1 = new Student() let stu2 = new Student()  stu1.letters.push('d')  console.log(stu1.letters)  // ['a', 'b', 'c', 'd'] console.log(stu2.letters)  // ['a', 'b', 'c']
登錄后復制

代碼解析: 示例中繼承 Person 那一行代碼展示了盜用構造函數的調用。通過使用call() (或 apply() )方法,Person 構造函數在為 Student 的實例創建的新對象的上下文中執行了。這相當于新的 Student 對象上運行了 Person() 函數中的所有初始化代碼。結果就是每個實例都會有自己的 letters 屬性。

2.1 傳遞參數

相比于使用原型鏈,盜用構造函數的一個優點就是可以在子類構造函數中向父類構造函數傳參。來看下面的例子:

// 定義 Person 構造函數 function Person(name) {   this.name = name }  // 定義 Student 構造函數 function Student(name) {   // 繼承 Person   Person.call(this, name)   // 實例屬性   this.age = 18 }  let stu = new Student('CoderBin')  console.log(stu.name)   // CoderBin console.log(stu.age)     // 18
登錄后復制

代碼解析:在這個例子中,Person 構造函數接收一個參數 name ,然后將它賦值給一個屬性。在 Student 構造函數中調用 Person 構造函數時傳入這個參數,實際上會在 Student 的實例上定義 name 屬性。為確保 Person 構造函數不會覆蓋 Student 定義的屬性,可以在調用父類構造函數之后再給子類實例添加額外的屬性。

2.2 盜用構造函數繼承的缺陷

盜用構造函數的主要缺點,也是使用構造函數模式自定義類型的問題:必須在構造函數中定義方法,因此函數不能重用。此外,子類也不能訪問父類原型上定義的方法,因此所有類型只能使用構造函數模式。由于存在這些問題,盜用構造函數基本上也不能單獨使用。

3. 組合繼承【方案三】

組合繼承 (有時候也叫偽經典繼承)綜合了原型鏈和盜用構造函數,將兩者的優點集中了起來。基本的思路是:使用原型鏈繼承原型上的屬性和方法,而通過盜用構造函數繼承實例屬性。 這樣既可以把方法定義在原型上以實現重用,又可以讓每個實例都有自己的屬性。來看下面的例子:

// 定義 Person 構造函數 function Person(name) {   this.name = name   this.letters = ['a', 'b', 'c'] }  // 在 Person 的原型上添加 sayName 方法 Person.prototype.sayName = function() {   console.log(this.name + ' 你好~') }  // 定義 Student 構造函數 function Student(name, age) {   // 繼承屬性   Person.call(this, name)   this.age = age }  // 繼承方法 Student.prototype = new Person()  // 在 Student 的原型上添加 sayAge 方法 Student.prototype.sayAge = function() {   console.log(this.age) }  let stu1 = new Student('CoderBin', 18) let stu2 = new Student('Bin', 23)  stu1.letters.push('d')  // 輸出 stu1 的信息 console.log(stu1.letters)   // [ 'a', 'b', 'c', 'd' ] stu1.sayName()               // CoderBin 你好~ stu1.sayAge()                 // 18  // 輸出 stu2 的信息 console.log(stu2.letters)   // [ 'a', 'b', 'c'] stu2.sayName()               // Bin 你好~ stu2.sayAge()                 // 23
登錄后復制

代碼解析:在這個例子中,Person 構造函數定義了兩個屬性,name 和 letters ,而它的原型上也定義了一個方法叫 sayName() 。Student 構造函數調用了 Person 構造函數,傳入了 name 參數,然后又定義了自己的屬性 age 。

此外,Student.prototype 也被賦值為 Person 的實例。 原型賦值之后,又在這個原型上添加了新方法sayAge() 。這樣,就可以創建兩個 Student 實例,讓這兩個實例都有自己的屬性,包括 letters , 同時還共享相同的方法。

最后:組合繼承彌補了原型鏈和盜用構造函數的不足,是 JavaScript 中使用最多的繼承模式。而且組合繼承也保留了instanceof操作符和isPrototypeOf()方法識別合成對象的能力。

4. 原型式繼承【方案四】

2006年,Douglas Crockford(JSON之父) 寫了一篇文章:《JavaScript中的原型式繼承》(“Prototypal Inheritance in JavaScript”)。這篇文章介紹了 一種不涉及嚴格意義上構造函數的繼承方法。他的出發點是即使不自定義類型也可以通過原型實現對象之間的信息共享。文章最終給出了一個函數:

function object(o) {   function F() {}   F.prototype = o   return new F() }
登錄后復制

這個object() 函數會創建一個臨時構造函數,將傳入的對象賦值給這個構造函數的原型,然后返回這個臨時類型的一個實例。

4.1 方法一:object

本質上,object() 是對傳入的對象執行了一次淺復制。 來看下面的例子:

function object(o) {   function F() {}   F.prototype = o   return new F() }  let person = {   name: 'CoderBin',   letters: ['a', 'b', 'c'] }  let p1 = object(person) let p2 = object(person)  p1.name = 'p1' p1.letters.push('d')  p2.name = 'p2' p2.letters.push('e')  console.log(person.letters)   // [ 'a', 'b', 'c', 'd', 'e' ]
登錄后復制

代碼解析:在這個例子中,person 對象定義了另一個對象也應該共享的信息,把它傳給 object() 之后會返回一個新對象。這個新對象的原型是 person ,意味著它的原型上既有原始值屬性又有引用值屬性。這也意味著 person.letters 不僅是 person 的屬性,也會跟 p1 和 p2 共享。這里實際上克隆了兩個 person 。

Crockford推薦的原型式繼承適用于這種情況:你有一個對象,想在它的基礎上再創建一個新對象。你需要把這個對象先傳給 object() ,然后再對返回的對象進行適當修改。

4.2 方法二:Object.create()

ECMAScript5 通過增加Object.create()方法將原型式繼承的概念規范化了。這個方法接收兩個參數:作為新對象原型的對象,以及給新對象定義額外屬性的對象(第二個可選)。在只有一個參數時,Object.create() 與這里的object()方法效果相同:

let person = {   name: 'CoderBin',   letters: ['a', 'b', 'c'] }  let p1 = Object.create(person) let p2 = Object.create(person)  p1.name = 'p1' p1.letters.push('d')  p2.name = 'p2' p2.letters.push('e')  console.log(person.letters)   // [ 'a', 'b', 'c', 'd', 'e' ]
登錄后復制

Object.create()的第二個參數與Object.defineProperties()的第二個參數一樣:每個新增屬性都通過各自的描述符來描述。以這種方式添加的屬性會遮蔽原型對象上的同名屬性。比如:

let person = {   name: 'CoderBin',   letters: ['a', 'b', 'c'] }  let p1 = Object.create(person, {   name: {     value: 'CoderBin'   } })  console.log(p1.name)
登錄后復制

原型式繼承非常適合不需要單獨創建構造函數,但仍然需要在對象間共享信息的場合。但要記住,屬性中包含的引用值始終會在相關對象間共享,跟使用原型模式是一樣的。

5. 寄生式繼承【方案五】

與原型式繼承比較接近的一種繼承方式是寄生式繼承 (parasitic inheritance),也是Crockford首倡的一種模式。寄生式繼承背后的思路類似于寄生構造函數和工廠模式:創建一個實現繼承的函數,以某種方式增強對象,然后返回這個對象?;镜募纳^承模式如下:

function inheritPrototype(o) {   let clone = Object.create(o)  // 通過調用函數創建一個新對象   clone.sayHi = function() {     // 以某種方式增強這個對象     console.log('Hi~')   }   return clone  // 返回這個對象 }
登錄后復制

代碼解析:在這段代碼中,inheritPrototype() 函數接收一個參數,就是新對象的基準對象。這個對象 o 會被傳給Object.create()函數,然后將返回的新對象賦值給 clone 。接著給 clone 對象添加一個新方法 sayHi() 。最后返回這個對象??梢韵裣旅孢@樣使用 inheritPrototype() 函數:

let person = {   name: 'CoderBin',   letters: ['a', 'b', 'c'] }  let p1 = inheritPrototype(person) p1.sayHi()  // Hi~
登錄后復制

代碼解析:這個例子基于 person 對象返回了一個新對象。新返回的 p1 對象具有 person 的所有屬性和方法,還有一個新方法叫 sayHi() 。寄生式繼承同樣適合主要關注對象,而不在乎類型和構造函數的場景。Object.create()函數不是寄生式繼承所必需的,任何返回新對象的函數都可以在這里使用。

注意: 通過寄生式繼承給對象添加函數會導致函數難以重用,與構造函數模式類似。

6. 寄生式組合繼承【方案六】

組合繼承其實也存在效率問題。最主要的效率問題就是父類構造函數始終會被調用兩次:一次在是創建子類原型時調用,另一次是在子類構造函數中調用。本質上,子類原型最終是要包含超類對象的所有實例屬性,子類構造函數只要在執行時重寫自己的原型就行了。

6.1 組合式繼承的缺陷

再來看一看這個組合繼承的例子:

// 定義 Person 構造函數 function Person(name) {   this.name = name   this.letters = ['a', 'b', 'c'] }  // 在 Person 的原型上添加 sayName 方法 Person.prototype.sayName = function() {   console.log(this.name) }  // 定義 Student 構造函數 function Student(name, age) {   Person.call(this, name)   // 第一次調用 Person()   this.age = age }  Student.prototype = new Person()  // 第二次調用 Person()  // 讓 Student 的原型指回 Student Student.prototype.constructor = Student  // 在 Student 的原型上添加 sayAge 方法 Student.prototype.sayAge = function() {   console.log(this.age) }  let stu = new Student('CoderBin', 18)  console.log(stu) // 輸出:Student { name: 'CoderBin', letters: [ 'a', 'b', 'c' ], age: 18 }  console.log(Student.prototype) // 輸出: // Person { //   name: undefined, //   letters: [ 'a', 'b', 'c' ],       //   constructor: [Function: Student], //   sayAge: [Function (anonymous)]    // }
登錄后復制

代碼解析:代碼中注釋的部分是調用 Person 構造函數的地方。在上面的代碼執行后,Student.prototype上會有兩個屬性:name 和 letters 。它們都是 Person 的實例屬性,但現在成為了 Student 的原型屬性。在調用 Student 構造函數時,也會調用 Person 構造函數,這一次會在新對象上創建實例屬性 name 和 letters 。這兩個實例屬性會遮蔽原型上同名的屬性。

所以,執行完上面的代碼后,有兩組 name 和 letters 屬性:一組在實例上,另一組在 Student 的原型上。這是調用兩次 Person 構造函數的結果。

6.2 解決方法

寄生式組合繼承通過盜用構造函數繼承屬性,但使用混合式原型鏈繼承方法。基本思路是不通過調用父類構造函數給子類原型賦值,而是取得父類原型的一個副本。說到底就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型。寄生式組合繼承的基本模式如下所示:

function inheritPrototype(subType, superType) {   let prototype = Object.create(superType.prototype)   // 創建對象   prototype.constructor = subType                             // 增強對象   subType.prototype = prototype                               // 賦值對象 }
登錄后復制

代碼解析:這個 inheritPrototype() 函數實現了寄生式組合繼承的核心邏輯。這個函數接收兩個參數:子類構造函數和父類構造函數。在這個函數內部,第一步是創建父類原型的一個副本。然后,給返回的prototype 對象設置 constructor 屬性,解決由于重寫原型導致默認 constructor 丟失的問題。最后將新創建的對象賦值給子類型的原型。如下例所示,調用 inheritPrototype() 就可以實現前面例子中的子類型原型賦值:

// 定義 Person 構造函數 function Person(name) {   this.name = name   this.letters = ['a', 'b', 'c'] }  // 在 Person 的原型上添加 sayName 方法 Person.prototype.sayName = function() {   console.log(this.name) }  // 定義 Student 構造函數 function Student(name, age) {   Person.call(this, name)   this.age = age } // 調用 inheritPrototype() 函數,傳入 子類構造函數 和 父類構造函數 inheritPrototype(Student, Person)  // 在 Person 的原型上添加 sayAge 方法 Student.prototype.sayAge = function() {   console.log(this.age) }  let stu = new Student('CoderBin', 18)  console.log(stu) // 輸出:Student { name: 'CoderBin', letters: [ 'a', 'b', 'c' ], age: 18 }  console.log(Student.prototype) // 輸出 // Person { //   constructor: [Function: Student], //   sayAge: [Function (anonymous)]    // }
登錄后復制

這里只調用了一次 Person 構造函數,避免了Student.prototype上不必要也用不到的屬性,因此可以說這個例子的效率更高。而且,原型鏈仍然保持不變,因此instanceof操作符和isPrototypeOf()方法正常有效。寄生式組合繼承可以算是引用類型繼承的最佳模式。

7. 寫到最后

到此為止,關于 JavaScript 中實現繼承的六種方法就全部總結完畢了,如果你能堅持看到這里,相信繼承這一塊的知識你已經足夠掌握了。當然,JS 還有其他相當重要的知識點,比如 this 指向等等,可以點擊 一篇文章帶你搞懂 this 的四個綁定規則 ✍ 前往學習。

【推薦學習:javascript高級教程】

贊(0)
分享到: 更多 (0)
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区
日韩亚洲欧美视频| 久久久国内精品| 亚洲熟妇无码一区二区三区| 97人人模人人爽人人澡| 爱爱爱爱免费视频| 最新中文字幕免费视频| av视屏在线播放| 免费裸体美女网站| 香蕉视频禁止18| 性欧美videossex精品| 日日躁夜夜躁aaaabbbb| 中文字幕22页| 99亚洲国产精品| 国产三级中文字幕| 欧美这里只有精品| 欧美精品自拍视频| 国产h视频在线播放| 北条麻妃在线一区| 日本中文字幕影院| 欧美日韩理论片| 人妻夜夜添夜夜无码av| 国产视频一视频二| 手机看片一级片| 国产系列第一页| 日本日本19xxxⅹhd乱影响| 色欲av无码一区二区人妻| 日韩精品视频一区二区在线观看| 亚洲色图久久久| 日本一二三区视频在线| 国产亚洲精品网站| 天天做天天爱天天高潮| 久久久久久免费看| 日韩爱爱小视频| 免费人成自慰网站| 777久久久精品一区二区三区| 天天干天天操天天做| 18黄暴禁片在线观看| 三年中国国语在线播放免费| 三年中文高清在线观看第6集| 黄页网站大全在线观看| 中日韩av在线播放| 日日摸日日碰夜夜爽无码| 伊人网在线综合| 免费在线观看的av网站| 黄色小视频免费网站| 91视频 -- 69xx| 国风产精品一区二区| 国产97色在线 | 日韩| 毛片av在线播放| 日韩不卡一二三| 97超碰青青草| 17c丨国产丨精品视频| 在线观看日本一区二区| 色综合av综合无码综合网站| 奇米777四色影视在线看| 日韩av手机版| 久久人妻精品白浆国产| 黄色小视频大全| 亚洲av毛片在线观看| 日本在线观看视频一区| 噼里啪啦国语在线观看免费版高清版| 欧美激情亚洲天堂| 日本高清免费观看| 色国产在线视频| 国产又猛又黄的视频| 欧美成人黄色网址| wwwxxx黄色片| 亚洲少妇第一页| 不卡的在线视频| 天堂在线一区二区三区| 欧美在线一区视频| 9久久婷婷国产综合精品性色| 午夜宅男在线视频| 无码无遮挡又大又爽又黄的视频| 国产成人永久免费视频| 国产不卡的av| 亚洲AV无码成人精品一区| 天天爽天天爽夜夜爽| 国产精品视频分类| 一个色综合久久| 成人不卡免费视频| 九色porny自拍| 在线免费黄色小视频| 欧美日韩在线免费观看视频| 欧美第一页浮力影院| 成年人网站av| 精品一区二区成人免费视频 | 天天干天天爽天天射| 97在线免费公开视频| 欧美亚洲一二三区| 日韩欧美在线免费观看视频| 午夜国产一区二区三区| 艳母动漫在线免费观看| 亚洲熟妇无码另类久久久| 成人在线观看a| 久久成年人网站| 草草草视频在线观看| 国产日产欧美视频| 91丝袜超薄交口足| 久久www视频| 日韩精品你懂的| 青青草综合在线| 韩国视频一区二区三区| 国产精品啪啪啪视频| 国产成人久久婷婷精品流白浆| 97超碰人人爽| 精品欧美一区免费观看α√| 在线免费观看av的网站| 精品一区二区三区毛片| 国产一区亚洲二区三区| 欧美性受xxxx黑人猛交88| 国产l精品国产亚洲区久久| 欧美在线a视频| 精品一卡二卡三卡| 久久手机在线视频| 日韩欧美中文视频| 久草在在线视频| 亚洲熟妇无码一区二区三区| theporn国产精品| 91视频免费版污| 妺妺窝人体色www在线小说| 在线观看免费黄色片| 免费一区二区三区在线观看| 久久精品午夜福利| 欧洲精品一区二区三区久久| 青青草影院在线观看| www.国产福利| 亚洲欧美在线精品| 欧美一级裸体视频| 黄色片视频在线播放| 国产淫片免费看| 草b视频在线观看| 欧美又粗又长又爽做受| 成人在线免费观看视频网站| 日韩a一级欧美一级| 黄色片免费网址| 国产探花在线观看视频| 男人的天堂最新网址| 欧美自拍小视频| 99视频在线视频| 日本三级黄色网址| 亚洲综合123| 免费的av在线| 日韩在线观看a| 国产裸体舞一区二区三区| 久久久久狠狠高潮亚洲精品| 久久久久狠狠高潮亚洲精品| 午夜dv内射一区二区| 我看黄色一级片| 日本三级福利片| 强开小嫩苞一区二区三区网站| 黄色a级在线观看| www..com日韩| 亚洲娇小娇小娇小| 午夜激情视频网| 999一区二区三区| 欧美少妇性生活视频| 99视频在线视频| 免费看污污视频| 鲁一鲁一鲁一鲁一澡| 在线免费视频a| 日本中文字幕一级片| 青青青免费在线| 午夜xxxxx| 国产午夜福利100集发布| 日本精品www| eeuss中文| 国产a级片免费观看| 久久免费看毛片| 一本久道综合色婷婷五月| 91高清国产视频| 欧美视频在线第一页| a在线观看免费视频| 天堂8在线天堂资源bt| 黑人粗进入欧美aaaaa| 免费观看国产视频在线| 黄色一级一级片| 免费人成在线观看视频播放| 国产一级特黄a大片免费| 国产亚洲精品久久久久久久| 天天天干夜夜夜操| 鲁一鲁一鲁一鲁一色| 日本三日本三级少妇三级66| 亚洲 欧美 日韩系列| 免费成人午夜视频| 中文字幕第50页| 北条麻妃亚洲一区| 天天爽天天爽夜夜爽| 国产成人久久婷婷精品流白浆| 亚洲精品少妇一区二区| 在线观看视频在线观看| 天天干天天综合| 激情综合网俺也去| 国产91对白刺激露脸在线观看| www.亚洲成人网| 国产精品igao激情视频| 日本美女爱爱视频| 国产对白在线播放| av动漫免费观看| 亚洲色图都市激情|