国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

Javascript面向對象特性_javascript技巧

來源:懂視網 責編:小采 時間:2020-11-27 20:46:35
文檔

Javascript面向對象特性_javascript技巧

Javascript面向對象特性_javascript技巧:1. JavaScript中的類型 -------- 雖然JavaScript是一個基于對象的語言,但對象(Object)在JavaScript中不是第一型的。JS 是以函數(Function)為第一型的語言。這樣說,不但是因為JS中的函數具有高級語言中的函 數的各種特性,而且也因為在JS中,Object
推薦度:
導讀Javascript面向對象特性_javascript技巧:1. JavaScript中的類型 -------- 雖然JavaScript是一個基于對象的語言,但對象(Object)在JavaScript中不是第一型的。JS 是以函數(Function)為第一型的語言。這樣說,不但是因為JS中的函數具有高級語言中的函 數的各種特性,而且也因為在JS中,Object

1. JavaScript中的類型
--------
雖然JavaScript是一個基于對象的語言,但對象(Object)在JavaScript中不是第一型的。JS
是以函數(Function)為第一型的語言。這樣說,不但是因為JS中的函數具有高級語言中的函
數的各種特性,而且也因為在JS中,Object也是由函數來實現的。——關于這一點,可以在
后文中“構造與析構”部分看到更進一步的說明。

JS中是弱類型的,他的內置類型簡單而且清晰:
---------------------------------------------------------
undefined : 未定義
number : 數字
boolean : 布爾值
string : 字符串
function : 函數
object : 對象

1). undefined類型
========================
在IE5及以下版本中,除了直接賦值和typeof()之外,其它任何對undefined的操作都將導致
異常。如果需要知道一個變量是否是undefined,只能采用typeof()的方法:
輸出1234
testFoo(1234);

// 2. 嘗試輸出obj1
// 3. 嘗試輸出obj2
testFoo(obj1);
try {
testFoo(obj2);
}
catch(e) {
document.writeln('Exception: ', e.description, '
');
}

// 聲明testFoo()
function testFoo(v) {
document.writeln(v, '
');
}

// 聲明object
var obj1 = {};
obj2 = {
toString: function() {return 'hi, object.'}
}

// 4. 輸出obj1
// 5. 輸出obj2
testFoo(obj1);
testFoo(obj2);

這個示例代碼在JS環境中執行的結果是:
------------------------------------
1234
undefined
Exception: 'obj2' 未定義
[object Object]
hi, obj
------------------------------------
問題是,testFoo()是在它被聲明之前被執行的;而同樣用“直接聲明”的
形式定義的object變量,卻不能在聲明之前引用。——例子中,第二、三
個輸入是不正確的。

函數可以在聲明之前引用,而其它類型的數值必須在聲明之后才能被使用。
這說明“聲明”與“執行期引用”在JavaScript中是兩個過程。

另外我們也可以發現,使用"var"來聲明的時候,編譯器會先確認有該變量
存在,但變量的值會是“undefined”。——因此“testFoo(obj1)”不會發
生異常。但是,只有等到關于obj1的賦值語句被執行過,才會有正常的輸出。
請對照第二、三與第四、五行輸出的差異。

由于JavaScript對原型鏈的維護是“執行”而不是“聲明”,這說明“原型
鏈是由用戶代碼來維護的,而不是編譯器維護的。

由這個推論,我們來看下面這個例子:
//---------------------------------------------------------
// 示例:錯誤的原型鏈
//---------------------------------------------------------
// 1. 構造器
function Animal() {}; // 動物
function Mammal() {}; // 哺乳動物
function Canine() {}; // 犬科的哺乳動物

// 2. 構造原型鏈
var instance = new Mammal();
Mammal.prototype = new Animal();
Canine.prototype = instance;

// 3. 測試輸出
var obj = new Canine();
document.writeln(obj instanceof Animal);

這個輸出結果,使我們看到一個錯誤的原型鏈導致的結果“犬科的哺乳動
物‘不是'一種動物”。

根源在于“2. 構造原型鏈”下面的幾行代碼是解釋執行的,而不是象var和
function那樣是“聲明”并在編譯期被理解的。解決問題的方法是修改那三
行代碼,使得它的“執行過程”符合邏輯:
//---------------------------------------------------------
// 上例的修正代碼(部分)
//---------------------------------------------------------
// 2. 構造原型鏈
Mammal.prototype = new Animal();
var instance = new Mammal();
Canine.prototype = instance;

3). 原型實例是如何被構造過程使用的
------
仍以Delphi為例。構造過程中,delphi中會首先創建一個指定實例大小的
“空的對象”,然后逐一給屬性賦值,以及調用構造過程中的方法、觸發事
件等。

JavaScript中的new()關鍵字中隱含的構造過程,與Delphi的構造過程并不完全一致。但
在構造器函數中發生的行為卻與上述的類似:
//---------------------------------------------------------
// JS中的構造過程(形式代碼)
//---------------------------------------------------------
function MyObject2() {
this.prop = 3;
this.method = a_method_function;

if (you_want) {
this.method();
this.fire_OnCreate();
}
}
MyObject2.prototype = new MyObject(); // MyObject()的聲明略

var obj = new MyObject2();

如果以單個類為參考對象的,這個構造過程中JavaScript可以擁有與Delphi
一樣豐富的行為。然而,由于Delphi中的構造過程是“動態的”,因此事實上
Delphi還會調用父類(MyObject)的構造過程,以及觸發父類的OnCreate()事件。

JavaScript沒有這樣的特性。父類的構造過程僅僅發生在為原型(prototype
屬性)賦值的那一行代碼上。其后,無論有多少個new MyObject2()發生,
MyObject()這個構造器都不會被使用。——這也意味著:
- 構造過程中,原型對象是一次性生成的;新對象只持有這個原型實例的引用
(并用“寫復制”的機制來存取其屬性),而并不再調用原型的構造器。

由于不再調用父類的構造器,因此Delphi中的一些特性無法在JavaScript中實現。
這主要影響到構造階段的一些事件和行為。——無法把一些“對象構造過程中”
的代碼寫到父類的構造器中。因為無論子類構造多少次,這次對象的構造過程根
本不會激活父類構造器中的代碼。

JavaScript中屬性的存取是動態的,因為對象存取父類屬性依賴于原型鏈表,構造
過程卻是靜態的,并不訪問父類的構造器;而在Delphi等一些編譯型語言中,(不使
用讀寫器的)屬性的存取是靜態的,而對象的構造過程則動態地調用父類的構造函數。
所以再一次請大家看清楚new()關鍵字的形式代碼中的這一行:
//---------------------------------------------------------
// new()關鍵字的形式化代碼
//---------------------------------------------------------
function new(aFunction) {
// 原型引用
var _proto= aFunction.prototype;

// ...
}

這個過程中,JavaScript做的是“get a prototype_Ref”,而Delphi等其它語言做
的是“Inherited Create()”。

八、JavaScript面向對象的支持
~~~~~~~~~~~~~~~~~~
(續)

4). 需要用戶維護的另一個屬性:constructor
------
回顧前面的內容,我們提到過:
- (如果正常地實現繼承模型,)對象實例的constructor屬性指向構造器
- obj.constructor.prototype指向該對象的原型
- 通過Object.constructor屬性,可以檢測obj2與obj1是否是相同類型的實例

與原型鏈要通過用戶代碼來維護prototype屬性一樣,實例的構造器屬性constructor
也需要用戶代碼維護。

對于JavaScript的內置對象來說,constructor屬性指向內置的構造器函數。如:
//---------------------------------------------------------
// 內置對象實例的constructor屬性
//---------------------------------------------------------
var _object_types = {
'function' : Function,
'boolean' : Boolean,
'regexp' : RegExp,
// 'math' : Math,
// 'debug' : Debug,
// 'image' : Image;
// 'undef' : undefined,
// 'dom' : undefined,
// 'activex' : undefined,
'vbarray' : VBArray,
'array' : Array,
'string' : String,
'date' : Date,
'error' : Error,
'enumerator': Enumerator,
'number' : Number,
'object' : Object
}

function objectTypes(obj) {
if (typeof obj !== 'object') return typeof obj;
if (obj === null) return 'null';

for (var i in _object_types) {
if (obj.constructor===_object_types[i]) return i;
}
return 'unknow';
}

// 測試數據和相關代碼
function MyObject() {
}
function MyObject2() {
}
MyObject2.prototype = new MyObject();

window.execScript(''+
'Function CreateVBArray()' +
' Dim a(2, 2)' +
' CreateVBArray = a' +
'End Function', 'VBScript');

document.writeln('dom<', '/div>');

// 測試代碼
var ax = new ActiveXObject("Microsoft.XMLHTTP");
var dom = document.getElementById('dom');
var vba = new VBArray(CreateVBArray());
var obj = new MyObject();
var obj2 = new MyObject2();

document.writeln(objectTypes(vba), '
');
document.writeln(objectTypes(ax), '
');
document.writeln(objectTypes(obj), '
');
document.writeln(objectTypes(obj2), '
');
document.writeln(objectTypes(dom), '
');

在這個例子中,我們發現constructor屬性被實現得并不完整。對于DOM對象、ActiveX對象
來說這個屬性都沒有正確的返回。

確切的說,DOM(包括Image)對象與ActiveX對象都不是標準JavaScript的對象體系中的,
因此它們也可能會具有自己的constructor屬性,并有著與JavaScript不同的解釋。因此,
JavaScript中不維護它們的constructor屬性,是具有一定的合理性的。

另外的一些單體對象(而非構造器),也不具有constructor屬性,例如“Math”和“Debug”、
“Global”和“RegExp對象”。他們是JavaScript內部構造的,不應該公開構造的細節。

我們也發現實例obj的constructor指向function MyObject()。這說明JavaScript維護了對
象的constructor屬性。——這與一些人想象的不一樣。

然而再接下來,我們發現MyObject2()的實例obj2的constructor仍然指向function MyObject()。
盡管這很說不通,然而現實的確如此。——這到底是為什么呢?

事實上,僅下面的代碼:
--------
function MyObject2() {
}

obj2 = new MyObject2();
document.writeln(MyObject2.prototype.constructor === MyObject2);
--------
構造的obj2.constructor將正確的指向function MyObject2()。事實上,我們也會注意到這
種情況下,MyObject2的原型屬性的constructor也正確的指向該函數。然而,由于JavaScript
要求指定prototype對象來構造原型鏈:
--------
function MyObject2() {
}
MyObject2.prototype = new MyObject();

obj2 = new MyObject2();
--------
這時,再訪問obj2,將會得到新的原型(也就是MyObject2.prototype)的constructor屬性。
因此,一切很明了:原型的屬性影響到構造過程對對象的constructor的初始設定。

作為一種補充的解決問題的手段,JavaScript開發規范中說“need to remember to reset
the constructor property',要求用戶自行設定該屬性。

所以你會看到更規范的JavaScript代碼要求這樣書寫:
//---------------------------------------------------------
// 維護constructor屬性的規范代碼
//---------------------------------------------------------
function MyObject2() {
}
MyObject2.prototype = new MyObject();
MyObject2.prototype.constructor = MyObject2;

obj2 = new MyObject2();

更外一種解決問題的方法,是在function MyObject()中去重置該值。當然,這樣會使
得執行效率稍低一點點:
//---------------------------------------------------------
// 維護constructor屬性的第二種方式
//---------------------------------------------------------
function MyObject2() {
this.constructor = arguments.callee;
// or, this.constructor = MyObject2;

// ...
}
MyObject2.prototype = new MyObject();

obj2 = new MyObject2();

5). 析構問題
------
JavaScript中沒有析構函數,但卻有“對象析構”的問題。也就是說,盡管我們不
知道一個對象什么時候會被析構,也不能截獲它的析構過程并處理一些事務。然而,
在一些不多見的時候,我們會遇到“要求一個對象立即析構”的問題。

問題大多數的時候出現在對ActiveX Object的處理上。因為我們可能在JavaScript
里創建了一個ActiveX Object,在做完一些處理之后,我們又需要再創建一個。而
如果原來的對象供應者(Server)不允許創建多個實例,那么我們就需要在JavaScript
中確保先前的實例是已經被釋放過了。接下來,即使Server允許創建多個實例,而
在多個實例間允許共享數據(例如OS的授權,或者資源、文件的鎖),那么我們在新
實例中的操作就可能會出問題。

可能還是有人不明白我們在說什么,那么我就舉一個例子:如果創建一個Excel對象,
打開文件A,然后我們save它,然后關閉這個實例。然后我們再創建Excel對象并打開
同一文件。——注意這時JavaScript可能還沒有來得及析構前一個對象。——這時我們
再想Save這個文件,就發現失敗了。下面的代碼示例這種情況:
//---------------------------------------------------------
// JavaScript中的析構問題(ActiveX Object示例)
//---------------------------------------------------------
結果為值類型,或變量為值類型時,等值(或全等)比較可以得到預想結果
- (即使包含相同的數據,)不同的對象實例之間是不等值(或全等)的
- 同一個對象的不同引用之間,是等值(==)且全等(===)的

但對于String類型,有一點補充:根據JScript的描述,兩個字符串比較時,只要有一個是值類型,則按值比較。這意味著在上面的例子中,代碼“str1==obj1”會得到結果true。而全等(===)運算需要檢測變量類型的一致性,因此“str1===obj1”的結果返回false。

JavaScript中的函數參數總是傳入值參,引用類型(的實例)是作為指針值傳入的。因此函數可以隨意重寫入口變量,而不用擔心外部變量被修改。但是,需要留意傳入的引用類型的變量,因為對它方法調用和屬性讀寫可能會影響到實例本身。——但,也可以通過引用類型的參數來傳出數據。

最后補充說明一下,值類型比較會逐字節檢測對象實例中的數據,效率低但準確性高;而引用類型只檢測實例指針和數據類型,因此效率高而準確性低。如果你需要檢測兩個引用類型是否真的包含相同的數據,可能你需要嘗試把它轉換成“字符串值”再來比較。

6. 函數的上下文環境
--------
只要寫過代碼,你應該知道變量是有“全局變量”和“局部變量”之分的。絕大多數的
JavaScript程序員也知道下面這些概念:
//---------------------------------------------------------
// JavaScript中的全局變量與局部變量
//---------------------------------------------------------
var v1 = '全局變量-1';
v2 = '全局變量-2';

function foo() {
v3 = '全局變量-3';

var v4 = '只有在函數內部并使用var定義的,才是局部變量';
}

按照通常對語言的理解來說,不同的代碼調用函數,都會擁有一套獨立的局部變量。
因此下面這段代碼很容易理解:
//---------------------------------------------------------
// JavaScript的局部變量
//---------------------------------------------------------
function MyObject() {
var o = new Object;

this.getValue = function() {
return o;
}
}

var obj1 = new MyObject();
var obj2 = new MyObject();
document.writeln(obj1.getValue() == obj2.getValue());

結果顯示false,表明不同(實例的方法)調用返回的局部變量“obj1/obj2”是不相同。

變量的局部、全局特性與OOP的封裝性中的“私有(private)”、“公開(public)”具有類同性。因此絕大多數資料總是以下面的方式來說明JavaScript的面向對象系統中的“封裝權限級別”問題:
//---------------------------------------------------------
// JavaScript中OOP封裝性
//---------------------------------------------------------
function MyObject() {
// 1. 私有成員和方法
var private_prop = 0;
var private_method_1 = function() {
// ...
return 1
}
function private_method_2() {
// ...
return 1
}

// 2. 特權方法
this.privileged_method = function () {
private_prop++;
return private_prop + private_method_1() + private_method_2();
}

// 3. 公開成員和方法
this.public_prop_1 = '';
this.public_method_1 = function () {
// ...
}
}

// 4. 公開成員和方法(2)
MyObject.prototype.public_prop_1 = '';
MyObject.prototype.public_method_1 = function () {
// ...
}

var obj1 = new MyObject();
var obj2 = new MyObject();

document.writeln(obj1.privileged_method(), '
');
document.writeln(obj2.privileged_method());

在這里,“私有(private)”表明只有在(構造)函數內部可訪問,而“特權(privileged)”是特指一種存取“私有域”的“公開(public)”方法。“公開(public)”表明在(構造)函數外可以調用和存取。

除了上述的封裝權限之外,一些文檔還介紹了其它兩種相關的概念:
- 原型屬性:Classname.prototype.propertyName = someValue
- (類)靜態屬性:Classname.propertyName = someValue

然而,從面向對象的角度上來講,上面這些概念都很難自圓其說:JavaScript究竟是為何、以及如何劃分出這些封裝權限和概念來的呢?

——因為我們必須注意到下面這個例子所帶來的問題:
//---------------------------------------------------------
// JavaScript中的局部變量
//---------------------------------------------------------
function MyFoo() {
var i;

MyFoo.setValue = function (v) {
i = v;
}
MyFoo.getValue = function () {
return i;
}
}
MyFoo();

var obj1 = new Object();
var obj2 = new Object();

// 測試一
MyFoo.setValue.call(obj1, 'obj1');
document.writeln(MyFoo.getValue.call(obj1), '
');

// 測試二
MyFoo.setValue.call(obj2, 'obj2');
document.writeln(MyFoo.getValue.call(obj2));
document.writeln(MyFoo.getValue.call(obj1));
document.writeln(MyFoo.getValue());

在這個測試代碼中,obj1/obj2都是Object()實例。我們使用function.call()的方式來調用setValue/getValue,使得在MyFoo()調用的過程中替換this為obj1/obj2實例。

然而我們發現“測試二”完成之后,obj2、obj1以及function MyFoo()所持有的局部變量都返回了“obj2”。——這表明三個函數使用了同一個局部變量。

由此可見,JavaScript在處理局部變量時,對“普通函數”與“構造器”是分別對待的。這種處理策略在一些JavaScript相關的資料中被解釋作“面向對象中的私有域”問題。而事實上,我更愿意從源代碼一級來告訴你真相:這是對象的上下文環境的問題。——只不過從表面看去,“上下文環境”的問題被轉嫁到對象的封裝性問題上了。

(在閱讀下面的文字之前,)先做一個概念性的說明:
- 在普通函數中,上下文環境被window對象所持有
 - 在“構造器和對象方法”中,上下文環境被對象實例所持有

在JavaScript的實現代碼中,每次創建一個對象,解釋器將為對象創建一個上下文環境鏈,用于存放對象在進入“構造器和對象方法”時對function()內部數據的一個備份。JavaScript保證這個對象在以后再進入“構造器和對象方法”內部時,總是持有該上下文環境,和一個與之相關的this對象。由于對象可能有多個方法,且每個方法可能又存在多層嵌套函數,因此這事實上構成了一個上下文環境的樹型鏈表結構。而在構造器和對象方法之外,JavaScript不提供任何訪問(該構造器和對象方法的)上下文環境的方法。

簡而言之:
- 上下文環境與對象實例調用“構造器和對象方法”時相關,而與(普通)函數無關
- 上下文環境記錄一個對象在“構造函數和對象方法”內部的私有數據
- 上下文環境采用鏈式結構,以記錄多層的嵌套函數中的上下文

由于上下文環境只與構造函數及其內部的嵌套函數有關,重新閱讀前面的代碼:
//---------------------------------------------------------
// JavaScript中的局部變量
//---------------------------------------------------------
function MyFoo() {
var i;

MyFoo.setValue = function (v) {
i = v;
}
MyFoo.getValue = function () {
return i;
}
}
MyFoo();

var obj1 = new Object();
MyFoo.setValue.call(obj1, 'obj1');

我們發現setValue()的確可以訪問到位于MyFoo()函數內部的“局部變量i”,但是由于setValue()方法的執有者是MyFoo對象(記住函數也是對象),因此MyFoo對象擁有MyFoo()函數的唯一一份“上下文環境”。

接下來MyFoo.setValue.call()調用雖然為setValue()傳入了新的this對象,但實際上擁有“上下文環境”的仍舊是MyFoo對象。因此我們看到無論創建多少個obj1/obj2,最終操作的都是同一個私有變量i。

全局函數/變量的“上下文環境”持有者為window,因此下面的代碼說明了“為什么全局變量能被任意的對象和函數訪問”:
//---------------------------------------------------------
// 全局函數的上下文
//---------------------------------------------------------
/*
function Window() {
*/
var global_i = 0;
var global_j = 1;

function foo_0() {
}

function foo_1() {
}
/*
}

window = new Window();
*/

因此我們可以看到foo_0()與foo_1()能同時訪問global_i和global_j。接下來的推論是,上下文環境決定了變量的“全局”與“私有”。而不是反過來通過變量的私有與全局來討論上下文環境問題。

更進一步的推論是:JavaScript中的全局變量與函數,本質上是window對象的私有變量與方法。而這個上下文環境塊,位于所有(window對象內部的)對象實例的上下文環境鏈表的頂端,因此都可能訪問到。

用“上下文環境”的理論,你可以順利地解釋在本小節中,有關變量的“全局/局部”作用域的問題,以及有關對象方法的封裝權限問題。事實上,在實現JavaScript的C源代碼中,這個“上下文環境”被叫做“JSContext”,并作為函數/方法的第一個參數傳入。——如果你有興趣,你可以從源代碼中證實本小節所述的理論。

另外,《JavaScript權威指南》這本書中第4.7節也講述了這個問題,但被叫做“變量的作用域”。然而重要的是,這本書把問題講反了。——作者試圖用“全局、局部的作用域”,來解釋產生這種現象的“上下文環境”的問題。因此這個小節顯得凌亂而且難以自圓其說。

不過在4.6.3小節,作者也提到了執行環境(execution context)的問題,這就與我們這里說的“上下文環境”是一致的了。然而更麻煩的是,作者又將讀者引錯了方法,試圖用函數的上下文環境去解釋DOM和ScriptEngine中的問題。

但這本書在“上下文環境鏈表”的查詢方式上的講述,是正確的而合理的。只是把這個叫成“作用域”有點不對,或者不妥。

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

Javascript面向對象特性_javascript技巧

Javascript面向對象特性_javascript技巧:1. JavaScript中的類型 -------- 雖然JavaScript是一個基于對象的語言,但對象(Object)在JavaScript中不是第一型的。JS 是以函數(Function)為第一型的語言。這樣說,不但是因為JS中的函數具有高級語言中的函 數的各種特性,而且也因為在JS中,Object
推薦度:
標簽: 特征 特點 js
  • 熱門焦點

最新推薦

猜你喜歡

熱門推薦

專題
Top
主站蜘蛛池模板: 日本我不卡 | 精品免费久久久久国产一区 | 午夜影院欧美 | 国产亚洲欧美一区二区三区 | 国产欧美日韩一区二区三区 | 国产成人精品.一二区 | 国产亚洲精品sese在线播放 | 欧美日韩国产一区 | 九九久久久2 | 亚洲欧美日韩高清一区二区一 | 日韩亚洲欧美视频 | 国产精品美女一区二区三区 | 亚洲欧美另类专区 | 亚洲最新 | 一道本一区二区三区 | 中文国产成人精品少久久 | 九九精品视频一区二区三区 | 久久精品国产国产精品四凭 | 久久精品香蕉 | 丝袜视频一区 | 欧美亚洲免费 | 成人精品久久 | 亚洲欧美日韩另类 | 国产日韩欧美精品在线 | 国产高清精品一级毛片 | 国语对白91 | 亚洲精品免费视频 | 亚洲色图另类 | 美国美女一级毛片免费全 | 亚洲欧美日韩一区 | 亚洲精品在线免费观看 | 中文字幕欧美在线观看 | 国产在线高清视频 | 国产在线视频不卡 | 欧美日韩有码 | 久久综合精品国产一区二区三区无 | 国产免费一区二区三区 | 日韩精品成人在线 | 国产成人久久一区二区三区 | 尤物视频黄 | 欧美视频在线观看免费 |