久久福利_99r_国产日韩在线视频_直接看av的网站_中文欧美日韩_久久一

您的位置:首頁技術(shù)文章
文章詳情頁

Javascript的繼承與多態(tài)

瀏覽:9日期:2023-11-13 16:01:15

本文先對(duì)es6發(fā)布之前javascript各種繼承實(shí)現(xiàn)方式進(jìn)行深入的分析比較,然后再介紹es6中對(duì)類繼承的支持以及優(yōu)缺點(diǎn)討論。最后介紹了javascript面向?qū)ο缶幊讨泻苌俦簧婕暗摹岸鄳B(tài)”,并提供了“運(yùn)算符重載”的思路。本文假設(shè)你已經(jīng)知道或了解了js中原型、原型鏈的概念。

es6之前,javascript本質(zhì)上不能算是一門面向?qū)ο蟮木幊陶Z言,因?yàn)樗鼘?duì)于封裝、繼承、多態(tài)這些面向?qū)ο笳Z言的特點(diǎn)并沒有在語言層面上提供原生的支持。但是,它引入了原型(prototype)的概念,可以讓我們以另一種方式模仿類,并通過原型鏈的方式實(shí)現(xiàn)了父類子類之間共享屬性的繼承以及身份確認(rèn)機(jī)制。其實(shí),面向?qū)ο蟮母拍畋举|(zhì)上來講不是指某種語言特性,而是一種設(shè)計(jì)思想。如果你深諳面向?qū)ο蟮木幊趟枷?,即使用c這種面向過程的語言也能寫出面向?qū)ο蟮拇a(典型的代表就是windows NT 內(nèi)核實(shí)現(xiàn)),而javascript亦是如此!正是由于javascript本身對(duì)面向?qū)ο缶幊虥]有一個(gè)語言上的支持標(biāo)準(zhǔn),所以才有了五花八門、令人眼花繚亂的“類繼承”的代碼。所幸,es6增加了class、extends、static等關(guān)鍵字用以在語言層面支持面向?qū)ο?,但是,還是有些保守!我們先列舉出es6之前常見的幾種繼承方案,然后再來一探es6的類繼承機(jī)制,最后再討論下javascript多態(tài)。

ES6之前的繼承 原型賦值方式

簡(jiǎn)而言之,就是直接將父類的一個(gè)實(shí)例賦給子類的原型。如下示例:

function Person(name){ this.name=name; this.className='person' }Person.prototype.getClassName=function(){ console.log(this.className)}function Man(){}Man.prototype=new Person();//1//Man.prototype=new Person('Davin');//2var man=new Man;>man.getClassName()>'person'>man instanceof Person>true

如代碼中1處所示,這種方法是直接new 了一個(gè)父類的實(shí)例,然后賦給子類的原型。這樣也就相當(dāng)于直接將父類原型中的方法屬性以及掛在this上的各種方法屬性全賦給了子類的原型,簡(jiǎn)單粗暴!我們?cè)賮砜纯磎an,它是Man的一個(gè)實(shí)例,因?yàn)閙an本身沒有g(shù)etClassName方法,那么就會(huì)去原型鏈上去找,找到的是person的getClassName。這種繼承方式下,所有的子類實(shí)例會(huì)共享一個(gè)父類對(duì)象的實(shí)例,這種方案最大問題就是 子類無法通過父類創(chuàng)建私有屬性 。比如每一個(gè)Person都有一個(gè)名字,我們?cè)诔跏蓟總€(gè)Man的時(shí)候要指定一個(gè)不同名字,然后子類將這個(gè)名字傳遞給父類,對(duì)于每個(gè)man來說,保存在相應(yīng)person中的name應(yīng)該是不同的,但是這種方式根本做不到。所以,這種繼承方式,實(shí)戰(zhàn)中基本不用!

調(diào)用構(gòu)造函數(shù)方式

function Person(name){ this.name=name; this.className='person' }Person.prototype.getName=function(){ console.log(this.name)}function Man(name){ Person.apply(this,arguments)}var man1=new Man('Davin');var man2=new Man('Jack');>man1.name>'Davin'>man2.name>'Jack'>man1.getName() //1 報(bào)錯(cuò)>man1 instanceof Person>true

這里在子類的在構(gòu)造函數(shù)里用子類實(shí)例的this去調(diào)用父類的構(gòu)造函數(shù),從而達(dá)到繼承父類屬性的效果。這樣一來,每new一個(gè)子類的實(shí)例,構(gòu)造函數(shù)執(zhí)行完后,都會(huì)有自己的一份資源(name)。但是這種辦法只能繼承父類構(gòu)造函數(shù)中聲明的實(shí)例屬性,并沒有繼承父類原型的屬性和方法,所以就找不到getName方法,所以1處會(huì)報(bào)錯(cuò)。為了同時(shí)繼承父類原型,從而誕生了組合繼承的方式:

組合繼承

function Person(name){ this.name=name||'default name'; //1 this.className='person' }Person.prototype.getName=function(){ console.log(this.name)}function Man(name){ Person.apply(this,arguments)}//繼承原型Man.prototype = new Person();var man1=new Man('Davin');> man1.name>'Davin'> man1.getName()>'Davin'

這個(gè)例子很簡(jiǎn)單,這樣不僅會(huì)繼承構(gòu)造函數(shù)中的屬性,也會(huì)復(fù)制父類原型鏈中的屬性。但是,有個(gè)問題, Man.prototype = new Person(); 這句執(zhí)行后,Man的原型如下:

> Man.prototype> {name: 'default name', className: 'person'}

也就是說Man的原型中已經(jīng)有了一個(gè)name屬性,而之后創(chuàng)建man1時(shí)傳給構(gòu)造的函數(shù)的name則是通過this重新定義了一個(gè)name屬性,相當(dāng)于只是覆蓋掉了原型的name屬性(原型中的name依然還在),這樣很不優(yōu)雅。

分離組合繼承

這是目前es5中主流的繼承方式,有些人起了一個(gè)吊炸天的名字“寄生組合繼承”。首先說明一下,兩者是一回事。分離組合繼承的名字是我起的,一來感覺不裝逼會(huì)好點(diǎn),二來,更確切。綜上所述,其實(shí)我們可以將繼承分為兩步:構(gòu)造函數(shù)屬性繼承和建立子類和父類原型的鏈接。所謂的分離就是分兩步走;組合是指同時(shí)繼承子類構(gòu)造函數(shù)和原型中的屬性。

function Person(name){ this.name=name; //1 this.className='person' }Person.prototype.getName=function(){ console.log(this.name)}function Man(name){ Person.apply(this,arguments)}//注意此處Man.prototype = Object.create(Person.prototype);var man1=new Man('Davin');> man1.name>'Davin'> man1.getName()>'Davin'

這里用到了 Object.creat(obj) 方法,該方法會(huì)對(duì)傳入的obj對(duì)象進(jìn)行淺拷貝。和上面組合繼承的主要區(qū)別就是:將父類的 原型 復(fù)制給了子類原型。這種做法很清晰:

構(gòu)造函數(shù)中繼承父類屬性/方法,并初始化父類。 子類原型和父類原型建立聯(lián)系。

還有一個(gè)問題,就是constructor屬性,我們來看一下:

> Person.prototype.constructor< Person(name){ this.name=name; //1 this.className='person' }> Man.prototype.constructor< Person(name){ this.name=name; //1 this.className='person' }

constructor是類的構(gòu)造函數(shù),我們發(fā)現(xiàn),Person和Man實(shí)例的constructor指向都是Person,當(dāng)然,這并不會(huì)改變instanceof的結(jié)果,但是對(duì)于需要用到construcor的場(chǎng)景,就會(huì)有問題。所以一般我們會(huì)加上這么一句:

Man.prototype.constructor = Man

綜合來看,es5下,這種方式是首選,也是實(shí)際上最流行的。

行文至此,es5下的主要繼承方式就介紹完了,在介紹es6繼承之前,我們?cè)偻畹目?,下面是?dú)家干貨,我們來看一下Neat.js中的一段簡(jiǎn)化源碼(關(guān)于Neat.js,這里是傳送門Neat.js官網(wǎng),待會(huì)再安利):

//下面為Neat源碼的簡(jiǎn)化-------------------------function Neat(){ Array.call(this)}Neat.prototype=Object.create(Array.prototype)Neat.prototype.constructor=Neat-------------------------//測(cè)試代碼var neat=new Neat;>neat.push(1,2,3,4)>neat.length //1>neat[4]=5>neat.length//2>neat.concat([6,7,8])//3

現(xiàn)在提問,上面分割線包起來的代碼塊干了件什么事?

對(duì),就是定義了一個(gè)繼承自數(shù)組的Neat對(duì)象!下面再來看一下下面的測(cè)試代碼,先猜猜1、2、3處執(zhí)行的結(jié)果分別是什么?期望的結(jié)果應(yīng)該是:

451,2,3,4,5,6,7,8

而實(shí)際上卻是:

44[[1,2,3,4],6,7,8]

吶尼!這不科學(xué)啊 !why ?

我曾在阮一峰的一篇文章中看到的解釋如下:

因?yàn)樽宇悷o法獲得原生構(gòu)造函數(shù)的內(nèi)部屬性,通過 Array.apply() 或者分配給原型對(duì)象都不行。原生構(gòu)造函數(shù)會(huì)忽略 apply 方法傳入的 this ,也就是說,原生構(gòu)造函數(shù)的 this 無法綁定,導(dǎo)致拿不到內(nèi)部屬性。ES5是先新建子類的實(shí)例對(duì)象 this ,再將父類的屬性添加到子類上,由于父類的內(nèi)部屬性無法獲取,導(dǎo)致無法繼承原生的構(gòu)造函數(shù)。比如,Array構(gòu)造函數(shù)有一個(gè)內(nèi)部屬性 [[DefineOwnProperty]] ,用來定義新屬性時(shí),更新 length 屬性,這個(gè)內(nèi)部屬性無法在子類獲取,導(dǎo)致子類的 length 屬性行為不正常。

然而,事實(shí)并非如此!確切來說,并不是原生構(gòu)造函數(shù)會(huì)忽略掉 apply 方法傳入的this而導(dǎo)致屬性無法綁定。要不然1處也不會(huì)輸出4了。還有,neat依然可以正常調(diào)用push等方法,但繼承之后原型上的方法有些也是有問題的,如neat.concat。其實(shí)可以看出,我們通過 Array.call(this) 也是有用的,比如length屬性可用。但是,為什么會(huì)出問?根據(jù)癥狀,可以肯定的是最終的this肯定有問題,但具體是什么問題呢?難道是我們漏了什么地方導(dǎo)致有遺漏的屬性沒有正常初始化?或者就是瀏覽器初始化數(shù)組的過程比較特殊,和自定義對(duì)象不一樣?首先我們看第一種可能,唯一漏掉的可能就是數(shù)組的靜態(tài)方法(上面的所有繼承方式都不會(huì)繼承父類靜態(tài)方法)。我們可以測(cè)試一下:

for(var i in Array){ console.log(i,'xx')}

然而并沒有一行輸出,也就是說Array并沒有靜態(tài)方法。當(dāng)然,這種方法只可以遍歷可枚舉的屬性,如果存在不可枚舉的屬性呢?其實(shí)即使有,在瀏覽器看來也應(yīng)該是數(shù)組私有的,瀏覽器不希望你去操作!所以第一種情況pass。那么只可能是第二種情況了,而事實(shí),直到es6出來后,才找到了答案:

ES6允許繼承原生構(gòu)造函數(shù)定義子類,因?yàn)镋S6是先新建父類的實(shí)例對(duì)象this,然后再用子類的構(gòu)造函數(shù)修飾this,使得父類的 所有行為 都可以繼承。

請(qǐng)注意我加粗的文字。“所有”,這個(gè)詞很微妙,不是“沒有”,那么言外之意就是說es5是部分了。根據(jù)我之前的測(cè)試(在es5下),下標(biāo)操作和concat在chrome下是有問題的,而大多數(shù)函數(shù)都是正常的,當(dāng)然,不同瀏覽器可能不一樣,這應(yīng)該也是jQuery每次操作后的結(jié)果集以一個(gè)新的擴(kuò)展后的數(shù)組的形式返回而不是本身繼承數(shù)組(然后再直接返回this的)的主要原因,畢竟jQuery要兼容各種瀏覽器。而Neat.js面臨的問題并沒有這么復(fù)雜,只需把有坑的地方繞過去就行。言歸正傳,在es5中,像數(shù)組一樣的,瀏覽器不讓我們愉快與之玩耍的對(duì)象還有:

Boolean()Number()String()Array()Date()Function()RegExp()Error()Object() es6的繼承方式

es6引入了class、extends、super、static(部分為ES2016標(biāo)準(zhǔn))

class Person{ //static sCount=0 //1 constructor(name){ this.name=name; this.sCount++; } //實(shí)例方法 //2 getName(){ console.log(this.name) } static sTest(){ console.log('static method test') }}class Man extends Person{ constructor(name){ super(name)//3 this.sex='male' }}var man=new Man('Davin')man.getName()//man.sTest()Man.sTest()//4輸出結(jié)果:Davinstatic method test

ES6明確規(guī)定,Class內(nèi)部只有靜態(tài)方法,沒有靜態(tài)屬性,所以1處是有問題的,ES7有一個(gè)靜態(tài)屬性的 提案 ,目前Babel轉(zhuǎn)碼器支持。熟悉java的可能對(duì)上面的代碼感覺很親切,幾乎是自解釋的。我們大概解釋一下,按照代碼中標(biāo)號(hào)對(duì)應(yīng):

constructor為構(gòu)造函數(shù),一個(gè)類有一個(gè),相當(dāng)于es5中構(gòu)造函數(shù)標(biāo)準(zhǔn)化,負(fù)責(zé)一些初始化工作,如果沒有定義,js vm會(huì)定義一個(gè)空的默認(rèn)的構(gòu)造函數(shù)。 實(shí)例方法,es6中可以不加'function'關(guān)鍵字, class內(nèi)定義的所有函數(shù)都會(huì)置于該類的原型當(dāng)中 ,所以,class本身只是一個(gè)語法糖。 構(gòu)造函數(shù)中通過super()調(diào)用父類構(gòu)造函數(shù),如果有super方法,需要時(shí)構(gòu)造函數(shù)中第一個(gè)執(zhí)行的語句,this關(guān)鍵字在調(diào)用super之后才可用。 靜態(tài)方法,在類定義的外部只能通過類名調(diào)用,內(nèi)部可以通過this調(diào)用,并且靜態(tài)函數(shù)是會(huì)被繼承的。如示例中:sTest是在Person中定義的靜函數(shù),可以通過 Man.sTest() 直接調(diào)用。 es6和es5繼承的區(qū)別

大多數(shù)瀏覽器的ES5實(shí)現(xiàn)之中,每一個(gè)對(duì)象都有 __proto__ 屬性,指向?qū)?yīng)的構(gòu)造函數(shù)的prototype屬性。Class作為構(gòu)造函數(shù)的語法糖,同時(shí)有prototype屬性和 __proto__ 屬性,因此同時(shí)存在兩條繼承鏈。

(1)子類的 __proto__ 屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。

(2)子類 prototype 屬性的 __proto__ 屬性,表示方法的繼承,總是指向父類的 prototype 屬性。

class A {}class B extends A {}B.__proto__ === A // trueB.prototype.__proto__ === A.prototype // true

上面代碼中,子類 B 的 __proto__ 屬性指向父類 A ,子類 B 的 prototype 屬性的 __proto__ 屬性指向父類 A 的 prototype 屬性。

這樣的結(jié)果是因?yàn)?,類的繼承是按照下面的模式實(shí)現(xiàn)的:

class A {}class B {}// B的實(shí)例繼承A的實(shí)例Object.setPrototypeOf(B.prototype, A.prototype);// B繼承A的靜態(tài)屬性O(shè)bject.setPrototypeOf(B, A);

Object.setPrototypeOf的簡(jiǎn)單實(shí)現(xiàn)如下:

Object.setPrototypeOf = function (obj, proto) { obj.__proto__ = proto; return obj;}

因此,就得到了上面的結(jié)果。

Object.setPrototypeOf(B.prototype, A.prototype);// 等同于B.prototype.__proto__ = A.prototype;Object.setPrototypeOf(B, A);// 等同于B.__proto__ = A;

這兩條繼承鏈,可以這樣理解:作為一個(gè)對(duì)象,子類( B )的原型( __proto__ 屬性)是父類( A );作為一個(gè)構(gòu)造函數(shù),子類( B )的原型( prototype 屬性)是父類的實(shí)例。

Object.create(A.prototype);// 等同于B.prototype.__proto__ = A.prototype; es6繼承的不足 不支持靜態(tài)屬性(除函數(shù))。 class中不能定義私有變量和函數(shù)。class中定義的所有函數(shù)都會(huì)被放倒原型當(dāng)中,都會(huì)被子類繼承,而屬性都會(huì)作為實(shí)例屬性掛到this上。如果子類想定義一個(gè)私有的方法或定義一個(gè)private 變量,便不能直接在class花括號(hào)內(nèi)定義,這真的很不方便!

總結(jié)一下,和es5相比,es6在語言層面上提供了面向?qū)ο蟮牟糠种С郑m然大多數(shù)時(shí)候只是一個(gè)語法糖,但使用起來更方便,語意化更強(qiáng)、更直觀,同時(shí)也給javascript繼承提供一個(gè)標(biāo)準(zhǔn)的方式。還有很重要的一點(diǎn)就是-es6支持原生對(duì)象繼承。

更多es6類繼承資料請(qǐng)移步:MDN Classess 。

多態(tài)

多態(tài)(Polymorphism)按字面的意思就是“多種狀態(tài)”。在面向?qū)ο笳Z言中,接口的多種不同的實(shí)現(xiàn)方式即為多態(tài)。這是標(biāo)準(zhǔn)定義,在c++中實(shí)現(xiàn)多態(tài)的方式有虛函數(shù)、抽象類、模板,在java中更粗暴,所有函數(shù)都是“虛”的,子類都可以重寫,當(dāng)然java中沒有虛函數(shù)的概念,我們暫且把相同簽名的、子類和父類可以有不同實(shí)現(xiàn)的函數(shù)稱之為虛函數(shù),虛函數(shù)和模版(java中的范型)是支持多態(tài)的主要方式,因?yàn)閖avascript中沒有模版,所以下面我們只討論虛函數(shù),下面先看一個(gè)例子:

function Person(name,age){ this.name=name this.age=age}Person.prototype.toString=function(){ return 'I am a Person, my name is '+ this.name}function Man(name,age){ Person.apply(this,arguments)}Man.prototype = Object.create(Person.prototype);Man.prototype.toString=function(){ return 'I am a Man, my name is'+this.name;}var person=new Person('Neo',19)var man1=new Man('Davin',18)var man2=new Man('Jack',19)> person+''> 'I am a Person, my name is Neo'> man1+''> 'I am a Man, my name isDavin'> man1<man2 //期望比較年齡大小 1> false

上面例子中,我們分別在子類和父類實(shí)現(xiàn)了toString方法,其實(shí),在js中上述代碼原理很簡(jiǎn)單,對(duì)于同名函數(shù),子類會(huì)覆父類的,這種特性其實(shí)就是虛函數(shù),只不過js中不區(qū)分參數(shù)個(gè)數(shù),也不區(qū)分參數(shù)類型,只看函數(shù)名稱,如果名稱相同就會(huì)覆蓋。現(xiàn)在我們來看注釋1,我們期望直接用比較運(yùn)算符比較兩個(gè)man的大?。ò茨挲g),怎么實(shí)現(xiàn)?在c++中有運(yùn)算符重載,但java和js中都沒有,所幸的是,js可以用一種變通的方法來實(shí)現(xiàn):

function Person(name,age){ this.name=name this.age=age}Person.prototype.valueOf=function(){ return this.age}function Man(name,age){ Person.apply(this,arguments)}Man.prototype = Object.create(Person.prototype);var person=new Person('Neo',19)var man1=new Man('Davin',18)var man2=new Man('Jack',19)var man3=new Man('Joe',19)>man1<19//1>true>person==19//2>true>man1<man2//3>true>man2==man3 //4 注意>true>person==man2//5>false

其中1、2、3、5在所有js vm下結(jié)果都是確定的。但是4并不一定!javascript規(guī)定,對(duì)于比較運(yùn)算符,如果一個(gè)值是對(duì)象,另一個(gè)值是數(shù)字時(shí),會(huì)先嘗試調(diào)用valueOf,如果valueOf未指定,就會(huì)調(diào)用toString;如果是字符串時(shí),則先嘗試調(diào)用toString,如果沒指定,則嘗試valueOf,如果兩者都沒指定,將拋出一個(gè)類型錯(cuò)誤異常。如果比較的兩個(gè)值都是對(duì)象時(shí),則比較的時(shí)對(duì)象的引用地址,所以若是對(duì)象,只有自身===自身,其它情況都是false?,F(xiàn)在我們回過頭來看看示例代碼,前三個(gè)都是標(biāo)準(zhǔn)的行為。而第四點(diǎn)取決于瀏覽器的實(shí)現(xiàn),如果嚴(yán)格按照標(biāo)準(zhǔn),這應(yīng)該算是chrome的一個(gè)bug ,但是,我們的代碼使用時(shí)雙等號(hào),并非嚴(yán)格相等判斷,所以瀏覽器的相等規(guī)則也會(huì)放寬。值得一提的是5,雖然person和man2 age都是19,但是結(jié)果卻是false。 總結(jié)一下,chrome對(duì)相同類的實(shí)例比較策略是先會(huì)嘗試轉(zhuǎn)化,然后再比較大小,而對(duì)非同類實(shí)例的比較,則會(huì)直接返回false,不會(huì)做任何轉(zhuǎn)化。 所以我的建議是:如果數(shù)字和類實(shí)例比較,永遠(yuǎn)是安全的,可以放心玩,如果是同類實(shí)例之間,可以進(jìn)行 非等 比較,這個(gè)結(jié)果是可以保證的,不要進(jìn)行相等比較,結(jié)果是不能保證的,一般相等比較,變通的做法是:

var equal= !(ob1<ob2||ob1>ob2) //不小于也不大于,就是等于,前提是比較操作符兩邊的對(duì)象要實(shí)現(xiàn)valueOf或toString

當(dāng)然類似toString、valueOf的還有toJson方法,但它和重載沒有什么關(guān)系,故不冗述。

數(shù)學(xué)運(yùn)算符

讓對(duì)象支持?jǐn)?shù)學(xué)運(yùn)算符本質(zhì)上和讓對(duì)象支持比較運(yùn)算符原理類似,底層也都是通過valueOf、toString來轉(zhuǎn)化實(shí)現(xiàn)。 但是通過這種覆蓋原始方法模擬的運(yùn)算符重載有個(gè)比較大局限就是:返回值只能是數(shù)字!而c++中的運(yùn)算符重載的結(jié)果可以是一個(gè)對(duì)象 。試想一下,如果我們現(xiàn)在要實(shí)現(xiàn)一個(gè)復(fù)數(shù)類的加法,復(fù)數(shù)包括實(shí)部與虛部,加法要同時(shí)應(yīng)用到兩個(gè)部分,而相加的結(jié)果(返回值)仍然是一個(gè)復(fù)數(shù)對(duì)象,這種情況下,javascript也就無能為力了。

總結(jié)

本文系統(tǒng)的介紹了javascript類繼承和多態(tài)。如要轉(zhuǎn)載請(qǐng)注明作者和原文鏈接。最后向大家安利一下我的開源項(xiàng)目:Neat.js ,歡迎star。如文中有誤,歡迎斧正。

參考資料

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf http://es6.ruanyifeng.com/#docs/class

來自:https://juejin.im/post/5912753ba22b9d005817524e

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 91在线看视频 | 成人免费一区二区三区 | 午夜av电影 | 国产午夜视频 | 亚洲精品一二区 | 精品久久久一区 | 成人免费在线观看网址 | 日本精品久久久一区二区三区 | 欧美成a| 精品国产一级毛片 | 精品亚洲在线 | 欧洲一区在线 | 中文字幕在线永久在线视频 | 日韩中文字幕无码一区二区三区 | 一区二区三区免费 | 日韩在线成人 | 中文字幕日韩欧美 | h在线观看| 99国产精品久久久久久久 | 亚洲狠狠爱一区二区三区 | 久久久久久久国产 | 香蕉国产精品 | 日韩精品在线播放 | 一级做a爰片性色毛片2021 | 国产精品无码久久久久 | 亚洲 欧美日韩 国产 中文 | 欧美精品在线一区 | 国产真实乱全部视频 | 日韩有码在线播放 | 免费一级片| 在线看国产| 欧美日本韩国一区二区三区 | 成人一级片在线观看 | 日韩在线一区二区三区 | 亚洲色图综合 | 日韩aaa久久蜜桃av | 国产精品第一区第27页 | 一级毛片免费完整视频 | 国产福利在线观看 | 国产日韩一区二区 | 欧美精品福利视频 |