久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合

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

JavaScript中7個處理undefined的小技巧

JavaScript中7個處理undefined的小技巧

當原作者開始學習JS時,遇到了一個奇怪的情況,既存在undefined 的值,也存在表示空值的null。它們之間的明顯區別是什么?它們似乎都定義了空值,而且,比較null == undefined的計算結果為true

大多數現代語言,如Ruby、Python或Java都有一個空值(nilnull),這似乎是一種合理的方式。

對于JavaScript,解釋器在訪問尚未初始化的變量或對象屬性時返回undefined。例如:

let company; company;    // => undefined let person = { name: 'John Smith' }; person.age; // => undefined

另一方面,null表示缺少的對象引用,JS本身不會將變量或對象屬性設置為null

一些原生方法,比如String.prototype.match(),可以返回null來表示丟失的對象。看看下面的示例:

let array = null; array; // => null let movie = { name: "Starship Troopers", musicBy: null }; movie.musicBy; // => null "abc".match(/[0-9]/); // => null

由于 JS 的寬容特性,開發人員很容易訪問未初始化的值,我也犯了這樣的錯誤。

通常,這種危險的操作會生成 undefined 的相關錯誤,從而快速地結束腳本。相關的常見錯誤消息有:

  • TypeError: 'undefined' is not a function

  • TypeError: Cannot read property '<prop-name>' of undefined

  • type errors

JS 開發人員可以理解這個笑話的諷刺:

function undefined() {     // problem solved }

為了降低此類錯誤的風險,必須理解生成undefined的情況。更重要的是抑制它的出現并阻止在應用程序中傳播,從而提高代碼的持久性。

讓咱們詳細討論 undefined 及其對代碼安全性的影響。

1、undefined 是什么鬼

JS 有6種基本類型

  • Boolean: truefalse

  • Number: 1, 6.7, 0xFF

  • String: "Gorilla and banana"

  • Symbol: Symbol("name") (starting ES2015)

  • Null: null

  • Undefined: undefined

和一個單獨的Object 類型:{name: "Dmitri"} ["apple", "orange"]

根據ECMAScript規范,從6種原始類型中,undefined是一個特殊的值,它有自己的Undefined類型。

未為變量賦值時默認值為undefined

該標準明確定義,當訪問未初始化的變量、不存在的對象屬性、不存在的數組元素等時,將接收到一個undefined 的值。例如:

let number; number; // => undefined  let movie = { name: "Interstellar" }; movie.year; // => undefined  let movies = ["Interstellar", "Alexander"]; movies[3]; // => undefined

上述代碼大致流程:

  • 未初始化的變量number

  • 一個不存在的對象屬性movie.year

  • 或者不存在數組元素movies[3]

都會被定義為undefined

ECMAScript規范定義了 undefined 值的類型

Undefined type是其唯一值為undefined 值的類型。

在這個意義上,typeof undefined返回“undefined”字符串

typeof undefined === "undefined"; // => true

當然typeof可以很好地驗證變量是否包含undefined的值

let nothing; typeof nothing === "undefined"; // => true

2、導致undefined的常見場景

2.1 未初始化變量

尚未賦值(未初始化)的聲明變量默認為undefined

let myVariable; myVariable; // => undefined

myVariable已聲明,但尚未賦值,默認值為undefined

解決未初始化變量問題的有效方法是盡可能分配初始值。 變量在未初始化狀態中越少越好。 理想情況下,你可以在聲明const myVariable ='Initial value'之后立即指定一個值,但這并不總是可行的。

技巧1:使用 let 和 const 來代替 var

在我看來,ES6 最好的特性之一是使用const和let聲明變量的新方法。const和let具有塊作用域(與舊的函數作用域var相反),在聲明行之前都存在于暫時性死區。

當變量一次性且永久地接收到一個值時,建議使用const聲明,它創建一個不可變的綁定。

const的一個很好的特性是必須為變量const myVariable ='initial'分配一個初始值。 變量未暴露給未初始化狀態,并且訪問undefined是不可能的。

以下示例檢查驗證一個單詞是否是回文的函數:

function isPalindrome(word) {     const length = word.length;     const half = Math.floor(length / 2);     for (let index = 0; index < half; index++) {         if (word[index] !== word[length - index - 1]) {             return false;         }     }     return true; } isPalindrome("madam"); // => true isPalindrome("hello"); // => false

length 和 half 變量被賦值一次。將它們聲明為const似乎是合理的,因為這些變量不會改變。

如果需要重新綁定變量(即多次賦值),請應用let聲明。只要可能,立即為它賦一個初值,例如,let index = 0。

那么使用 var 聲明呢,相對于ES6,建議是完全停止使用它。

JavaScript中7個處理undefined的小技巧

var 聲明的變量提會被提升到整個函數作用域頂部。可以在函數作用域末尾的某個地方聲明var變量,但是仍然可以在聲明之前訪問它:對應變量的值是 undefined。

相反,用let 或者 const 聲明的變量之前不能訪問該變量。之所以會發生這種情況,是因為變量在聲明之前處于暫時死區。這很好,因為這樣就很少有機會訪問到 undefined 值。

使用let(而不是var)更新的上述示例會引發ReferenceError 錯誤,因為無法訪問暫時死區中的變量。

function bigFunction() {   // code...   myVariable; // => Throws 'ReferenceError: myVariable is not defined'   // code...   let myVariable = 'Initial value';   // code...   myVariable; // => 'Initial value' } bigFunction();

技巧2:增加內聚性

內聚描述模塊的元素(命名空間、類、方法、代碼塊)內聚在一起的程度。凝聚力的測量通常被稱為高凝聚力或低內聚。

高內聚是優選的,因為它建議設計模塊的元素以僅關注單個任務,它構成了一個模塊。

  • 專注且易懂:更容易理解模塊的功能

  • 可維護且更容易重構:模塊中的更改會影響更少的模塊

  • 可重用:專注于單個任務,使模塊更易于重用

  • 可測試:可以更輕松地測試專注于單個任務的模塊

JavaScript中7個處理undefined的小技巧

高內聚和低耦合是一個設計良好的系統的特征。

代碼塊本身可能被視為一個小模塊,為了盡可能實現高內聚,需要使變量盡可能接近使用它們代碼塊位置。

例如,如果一個變量僅存在以形成塊作用域內,不要將此變量公開給外部塊作用域,因為外部塊不應該關心此變量。

不必要地延長變量生命周期的一個典型例子是函數中for循環的使用:

function someFunc(array) {   var index, item, length = array.length;   // some code...   // some code...   for (index = 0; index < length; index++) {     item = array[index];     // some code...   }   return 'some result'; }

indexitemlength變量在函數體的開頭聲明,但是,它們僅在最后使用,那么這種方式有什么問題呢?

從頂部的聲明到for語句中變量 index 和 item 都是未初始化的,值為 undefined。它們在整個函數作用域內具有不合理較長的生命周期。

一種更好的方法是將這些變量盡可能地移動到使用它們的位置:

function someFunc(array) {   // some code...   // some code...   const length = array.length;   for (let index = 0; index < length; index++) {     const item = array[index];     // some    }   return 'some result'; }

indexitem變量僅存在于for語句的作用域內,for 之外沒有任何意義。length變量也被聲明為接近其使用它的位置。

為什么修改后的版本優于初始版本? 主要有幾點:

  • 變量未暴露undefined狀態,因此沒有訪問undefined的風險

  • 將變量盡可能地移動到它們的使用位置會增加代碼的可讀性

  • 高內聚的代碼塊在必要時更容易重構并提取到單獨的函數中

2.2 訪問不存在的屬性

訪問不存在的對象屬性時,JS 返回undefined

咱們用一個例子來說明這一點:

let favoriteMovie = {   title: 'Blade Runner' }; favoriteMovie.actors; // => undefined

favoriteMovie是一個具有單個屬性 title 的對象。 使用屬性訪問器favoriteMovie.actors訪問不存在的屬性actors將被計算為undefined

本身訪問不存在的屬性不會引發錯誤, 但嘗試從不存在的屬性值中獲取數據時就會出現問題。 常見的的錯誤是 TypeError: Cannot read property <prop> of undefined

稍微修改前面的代碼片段來說明TypeError throw

let favoriteMovie = {   title: 'Blade Runner' }; favoriteMovie.actors[0]; // TypeError: Cannot read property '0' of undefined

favoriteMovie沒有屬性actors,所以favoriteMovie.actors的值 undefined。因此,使用表達式favoriteMovie.actors[0]訪問undefined值的第一項會引發TypeError

JS 允許訪問不存在的屬性,這種允許訪問的特性容易引起混淆:可能設置了屬性,也可能沒有設置屬性,繞過這個問題的理想方法是限制對象始終定義它所持有的屬性。

不幸的是,咱們常常無法控制對象。在不同的場景中,這些對象可能具有不同的屬性集,因此,必須手動處理所有這些場景:

接著我們實現一個函數append(array, toAppend),它的主要功能在數組的開頭和/或末尾添加新的元素。 toAppend參數接受具有屬性的對象:

  • first:元素插入數組的開頭

  • last:元素在數組末尾插入。

函數返回一個新的數組實例,而不改變原始數組(即它是一個純函數)。

append()的第一個版本看起來比較簡單,如下所示:

function append(array, toAppend) {   const arrayCopy = array.slice();   if (toAppend.first) {     arrayCopy.unshift(toAppend.first);   }   if (toAppend.last) {     arrayCopy.push(toAppend.last);   }   return arrayCopy; } append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5] append(['Hello'], { last: 'World' });     // => ['Hello', 'World'] append([8, 16], { first: 4 });            // => [4, 8, 16]

由于toAppend對象可以省略first或last屬性,因此必須驗證toAppend中是否存在這些屬性。如果屬性不存在,則屬性訪問器值為undefined

檢查firstlast屬性是否是undefined,在條件為 if(toappendix .first){}if(toappendix .last){}中進行驗證:

這種方法有一個缺點, undefinedfalsenull0NaN''是虛值。

append() 的當前實現中,該函數不允許插入虛值元素:

append([10], { first: 0, last: false }); // => [10]

0false是虛值的。 因為 if(toAppend.first){}if(toAppend.last){}實際上與falsy進行比較,所以這些元素不會插入到數組中,該函數返回初始數組[10]而不會進行任何修改。

以下技巧解釋了如何正確檢查屬性的存在。

技巧3: 檢查屬性是否存在

JS 提供了許多方法來確定對象是否具有特定屬性:

  • obj.prop!== undefined:直接與undefined進行比較

  • typeof obj.prop!=='undefined':驗證屬性值類型

  • obj.hasOwnProperty('prop'):驗證對象是否具有自己的屬性

  • 'prop' in obj:驗證對象是否具有自己的屬性或繼承屬性

我的建議是使用 in 操作符,它的語法短小精悍。in操作符的存在表明一個明確的意圖,即檢查對象是否具有特定的屬性,而不訪問實際的屬性值。

JavaScript中7個處理undefined的小技巧

obj.hasOwnProperty('prop')也是一個很好的解決方案,它比 in 操作符稍長,僅在對象自己的屬性中進行驗證。

涉及與undefined進行比較剩下的兩種方式可能有效,但在我看來,obj.prop!== undefinedtypeof obj.prop!=='undefined'看起來冗長而怪異,并暴露出直接處理undefined的可疑路徑。。

讓咱們使用in操作符改進append(array, toAppend) 函數:

function append(array, toAppend) {   const arrayCopy = array.slice();   if ('first' in toAppend) {     arrayCopy.unshift(toAppend.first);   }   if ('last' in toAppend) {     arrayCopy.push(toAppend.last);   }   return arrayCopy; } append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5] append([10], { first: 0, last: false });  // => [0, 10, false]

'first' in toAppend (和'last' in toAppend)在對應屬性存在時為true,否則為falsein操作符的使用解決了插入虛值元素0false的問題。現在,在[10]的開頭和結尾添加這些元素將產生預期的結果[0,10,false]

技巧4:解構訪問對象屬性

在訪問對象屬性時,如果屬性不存在,有時需要指示默認值。可以使用in和三元運算符來實現這一點。

const object = { }; const prop = 'prop' in object ? object.prop : 'default'; prop; // => 'default'

當要檢查的屬性數量增加時,三元運算符語法的使用變得令人生畏。對于每個屬性,都必須創建新的代碼行來處理默認值,這就增加了一堵難看的墻,里面都是外觀相似的三元運算符。

為了使用更優雅的方法,可以使用 ES6 對象的解構。

對象解構允許將對象屬性值直接提取到變量中,并在屬性不存在時設置默認值,避免直接處理undefined的方便語法。

實際上,屬性提取現在看起來簡短而有意義:

const object = {  }; const { prop = 'default' } = object; prop; // => 'default'

要查看實際操作中的內容,讓我們定義一個將字符串包裝在引號中的有用函數。quote(subject, config)接受第一個參數作為要包裝的字符串。 第二個參數config是一個具有以下屬性的對象:

  • char:包裝的字符,例如 '(單引號)或(雙引號),默認為

  • skipIfQuoted:如果字符串已被引用則跳過引用的布爾值,默認為true

使用對象析構的優點,讓咱們實現quote()

function quote(str, config) {   const { char = '"', skipIfQuoted = true } = config;   const length = str.length;   if (skipIfQuoted       && str[0] === char       && str[length - 1] === char) {     return str;   }   return char + str + char; } quote('Hello World', { char: '*' });        // => '*Hello World*' quote('"Welcome"', { skipIfQuoted: true }); // => '"Welcome"'

const {char = '", skipifquote = true} = config解構賦值在一行中從config對象中提取charskipifquote屬性。如果config對象中有一些屬性不可用,那么解構賦值將設置默認值:char'"'skipifquotefalse

該功能仍有改進的空間。讓我們將解構賦值直接移動到參數部分。并為config參數設置一個默認值(空對象{}),以便在默認設置足夠時跳過第二個參數。

function quote(str, { char = '"', skipIfQuoted = true } = {}) {   const length = str.length;   if (skipIfQuoted       && str[0] === char       && str[length - 1] === char) {     return str;   }   return char + str + char; } quote('Hello World', { char: '*' }); // => '*Hello World*' quote('Sunny day');                  // => '"Sunny day"'

注意,解構賦值替換了函數 config 參數。我喜歡這樣:quote()縮短了一行。

={}在解構賦值的右側,確保在完全沒有指定第二個參數的情況下使用空對象。

對象解構是一個強大的功能,可以有效地處理從對象中提取屬性。 我喜歡在被訪問屬性不存在時指定要返回的默認值的可能性。因為這樣可以避免undefined以及與處理它相關的問題。

技巧5: 用默認屬性填充對象

如果不需要像解構賦值那樣為每個屬性創建變量,那么丟失某些屬性的對象可以用默認值填充。

ES6 Object.assign(target,source1,source2,...)將所有可枚舉的自有屬性的值從一個或多個源對象復制到目標對象中,該函數返回目標對象。

例如,需要訪問unsafeOptions對象的屬性,該對象并不總是包含其完整的屬性集。

為了避免從unsafeOptions訪問不存在的屬性,讓我們做一些調整:

定義包含默認屬性值的defaults對象

調用Object.assign({},defaults,unsafeOptions)來構建新的對象options。 新對象從unsafeOptions接收所有屬性,但缺少的屬性從defaults對象獲取。

const unsafeOptions = {   fontSize: 18 }; const defaults = {   fontSize: 16,   color: 'black' }; const options = Object.assign({}, defaults, unsafeOptions); options.fontSize; // => 18 options.color;    // => 'black'

unsafeOptions僅包含fontSize屬性。 defaults對象定義屬性fontSizecolor的默認值。

Object.assign() 將第一個參數作為目標對象{}。 目標對象從unsafeOptions源對象接收fontSize屬性的值。 并且人defaults對象的獲取color屬性值,因為unsafeOptions不包含color屬性。

枚舉源對象的順序很重要:后面的源對象屬性會覆蓋前面的源對象屬性。

現在可以安全地訪問options對象的任何屬性,包括options.color在最初的unsafeOptions中是不可用的。

還有一種簡單的方法就是使用ES6中展開運算符:

const unsafeOptions = {   fontSize: 18 }; const defaults = {   fontSize: 16,   color: 'black' }; const options = {   ...defaults,   ...unsafeOptions }; options.fontSize; // => 18 options.color;    // => 'black'

對象初始值設定項從defaultsunsafeOptions源對象擴展屬性。 指定源對象的順序很重要,后面的源對象屬性會覆蓋前面的源對象。

使用默認屬性值填充不完整的對象是使代碼安全且持久的有效策略。無論哪種情況,對象總是包含完整的屬性集:并且無法生成undefined的屬性。

2.3 函數參數

函數參數隱式默認為undefined

通常,用特定數量的參數定義的函數應該用相同數量的參數調用。在這種情況下,參數得到期望的值

function multiply(a, b) {   a; // => 5   b; // => 3   return a * b; } multiply(5, 3); // => 15

調用multiply(5,3)使參數ab接收相應的53值,返回結果:5 * 3 = 15

在調用時省略參數會發生什么?

function multiply(a, b) {   a; // => 5   b; // => undefined   return a * b; } multiply(5); // => NaN

函數multiply(a, b){}由兩個參數ab定義。調用multiply(5)用一個參數執行:結果一個參數是5,但是b參數是undefined

技巧6: 使用默認參數值

有時函數不需要調用的完整參數集,可以簡單地為沒有值的參數設置默認值。

回顧前面的例子,讓我們做一個改進,如果b參數未定義,則為其分配默認值2

function multiply(a, b) {   if (b === undefined) {     b = 2;   }   a; // => 5   b; // => 2   return a * b; } multiply(5); // => 10

雖然所提供的分配默認值的方法有效,但不建議直接與undefined值進行比較。它很冗長,看起來像一個hack .

這里可以使用 ES6 的默認值:

function multiply(a, b = 2) {   a; // => 5   b; // => 2   return a * b; } multiply(5);            // => 10 multiply(5, undefined); // => 10

2.4 函數返回值

隱式地,沒有return語句,JS 函數返回undefined

在JS中,沒有任何return語句的函數隱式返回undefined

function square(x) {   const res = x * x; } square(2); // => undefined

square() 函數沒有返回計算結果,函數調用時的結果undefined

return語句后面沒有表達式時,默認返回 undefined

function square(x) {   const res = x * x;   return; } square(2); // => undefined

return; 語句被執行,但它不返回任何表達式,調用結果也是undefined

function square(x) {   const res = x * x;   return res; } square(2); // => 4

技巧7: 不要相信自動插入分號

JS 中的以下語句列表必須以分號(;)結尾:

  • 空語句

  • letconstvarimportexport聲明

  • 表達語句

  • debugger 語句

  • continue 語句,break 語句

  • throw 語句

  • return 語句

如果使用上述聲明之一,請盡量務必在結尾處指明分號:

function getNum() {   let num = 1;    return num; } getNum(); // => 1

let 聲明和 return 語句結束時,強制性寫分號。

當你不想寫這些分號時會發生什么? 例如,咱們想要減小源文件的大小。

在這種情況下,ECMAScript 提供自動分號插入(ASI)機制,為你插入缺少的分號

ASI 的幫助下,可以從上一個示例中刪除分號

function getNum() {   // Notice that semicolons are missing   let num = 1   return num } getNum() // => 1

上面的代碼是有效的JS代碼,缺少的分號ASI會自動為我們插入。

乍一看,它看起來很 nice。 ASI 機制允許你少寫不必要的分號,可以使JS代碼更小,更易于閱讀。

ASI 創建了一個小而煩人的陷阱。 當換行符位于returnreturn n expression之間時,ASI 會在換行符之前自動插入分號(return; n expression)。

函數內部return; ? 即該函數返回undefined。 如果你不詳細了解ASI的機制,則意外返回的undefined會產生意想不到的問題。

getPrimeNumbers()調用返回的值:

function getPrimeNumbers() {   return      [ 2, 3, 5, 7, 11, 13, 17 ] } getPrimeNumbers() // => undefined

return語句和數組之間存在一個換行,JS 在return后自動插入分號,解釋代碼如下:

function getPrimeNumbers() {   return;    [ 2, 3, 5, 7, 11, 13, 17 ]; } getPrimeNumbers(); // => undefined

return; 使函數getPrimeNumbers() 返回undefined而不是期望的數組。

這個問題通過刪除return和數組文字之間的換行來解決:

function getPrimeNumbers() {   return [      2, 3, 5, 7, 11, 13, 17    ]; } getPrimeNumbers(); // => [2, 3, 5, 7, 11, 13, 17]

我的建議是研究自動分號插入的確切方式,以避免這種情況。

當然,永遠不要在return和返回的表達式之間放置換行符。

2.5 void 操作符

void <expression>計算表達式無論計算結果如何都返回undefined

void 1;                    // => undefined void (false);              // => undefined void {name: 'John Smith'}; // => undefined void Math.min(1, 3);       // => undefined

void操作符的一個用例是將表達式求值限制為undefined,這依賴于求值的一些副作用。

3、 未定義的數組

訪問越界索引的數組元素時,會得到undefined

const colors = ['blue', 'white', 'red']; colors[5];  // => undefined colors[-1]; // => undefined

colors數組有3個元素,因此有效索引為012

因為索引5-1沒有數組元素,所以訪問colors[5]colors[-1]值為undefined

JS 中,可能會遇到所謂的稀疏數組。這些數組是有間隙的數組,也就是說,在某些索引中,沒有定義元素。

當在稀疏數組中訪問間隙(也稱為空槽)時,也會得到一個undefined

下面的示例生成稀疏數組并嘗試訪問它們的空槽

const sparse1 = new Array(3); sparse1;       // => [<empty slot>, <empty slot>, <empty slot>] sparse1[0];    // => undefined sparse1[1];    // => undefined const sparse2 = ['white',  ,'blue'] sparse2;       // => ['white', <empty slot>, 'blue'] sparse2[1];    // => undefined

使用數組時,為了避免獲取undefined,請確保使用有效的數組索引并避免創建稀疏數組。

4、 undefined和null之間的區別

一個合理的問題出現了:undefinednull之間的主要區別是什么?這兩個特殊值都表示為空狀態。

主要區別在于undefined表示尚未初始化的變量的值,null表示故意不存在對象。

讓咱們通過一些例子來探討它們之間的區別。

number 定義了但沒有賦值。

let number; number; // => undefined

number 變量未定義,這清楚地表明未初始化的變量。

當訪問不存在的對象屬性時,也會發生相同的未初始化概念

const obj = { firstName: 'Dmitri' }; obj.lastName; // => undefined

因為obj中不存在lastName屬性,所以JS正確地將obj.lastName計算為undefined

在其他情況下,你知道變量期望保存一個對象或一個函數來返回一個對象。但是由于某些原因,你不能實例化該對象。在這種情況下,null是丟失對象的有意義的指示器。

例如,clone()是一個克隆普通JS對象的函數,函數將返回一個對象

function clone(obj) {   if (typeof obj === 'object' && obj !== null) {     return Object.assign({}, obj);   }   return null; } clone({name: 'John'}); // => {name: 'John'} clone(15);             // => null clone(null);           // => null

但是,可以使用非對象參數調用clone(): 15null(或者通常是一個原始值,nullundefined)。在這種情況下,函數不能創建克隆,因此返回null—— 一個缺失對象的指示符。

typeof操作符區分了這兩個值

typeof undefined; // => 'undefined' typeof null;      // => 'object'

嚴格相等運算符===可以正確區分undefinednull

let nothing = undefined; let missingObject = null; nothing === missingObject; // => false

總結

undefined的存在是JS的允許性質的結果,它允許使用:

  • 未初始化的變量

  • 不存在的對象屬性或方法

  • 訪問越界索引的數組元素

  • 不返回任何結果的函數的調用結果

大多數情況下直接與undefined進行比較是一種不好的做法。一個有效的策略是減少代碼中undefined關鍵字的出現:

  • 減少未初始化變量的使用

  • 使變量生命周期變短并接近其使用的位置

  • 盡可能為變量分配初始值

  • 多敷衍 const 和 let

  • 使用默認值來表示無關緊要的函數參數

  • 驗證屬性是否存在或使用默認屬性填充不安全對象

  • 避免使用稀疏數組

贊(0)
分享到: 更多 (0)
?
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
久久精品五月,日韩不卡视频在线观看,国产精品videossex久久发布 ,久久av综合
热久久久久久| 欧美午夜精彩| 秋霞影视一区二区三区| 国产精品久久久一区二区| 亚洲精品欧洲| 欧美在线看片| 国产精品一区亚洲| 欧美激情在线精品一区二区三区| 国产va免费精品观看精品视频| 久久av资源| 久久久91麻豆精品国产一区| 欧美久久香蕉| 国产精品调教| 国产不卡精品在线| 99精品视频在线| 黑丝一区二区三区| 一区二区高清| 国产精品一区二区99| 九九99久久精品在免费线bt| 综合日韩av| 国产综合精品| 亚洲一区观看| 国产欧美在线| 日韩国产欧美一区二区| 亚洲福利专区| 午夜久久av| 日韩精品欧美精品| 久久99久久人婷婷精品综合| 中文字幕在线官网| 国产日韩专区| 国产探花一区| 日韩欧美三级| 日韩影院在线观看| 国产精成人品2018| 亚洲v在线看| 久久不射网站| 国产精品久久久久久模特 | 国产精品视区| 国产精品一线天粉嫩av| 国产一区二区三区日韩精品 | 久久久精品日韩| 老司机久久99久久精品播放免费| 91亚洲精品视频在线观看 | 中文在线中文资源| 久久性天堂网| 美女久久久久久| 久久高清免费| 欧美日韩国产一区二区在线观看| 高清av不卡| 亚洲一区二区三区久久久| 日本视频一区二区| 日本久久综合| 久久国产99| japanese国产精品| 欧美精品激情| 99精品电影| 成人片免费看| 亚洲香蕉视频| 久久男人天堂| 香蕉久久久久久久av网站| 久久国内精品视频| 激情丁香综合| 国产精品国产三级在线观看| 欧美va天堂在线| 国产精品一区毛片| 精品亚洲美女网站| 亚洲色图国产| 日韩毛片视频| 日本综合精品一区| 欧美午夜精彩| 欧美激情99| 中文字幕一区二区三区日韩精品 | 日本不卡高清| 欧美 日韩 国产一区二区在线视频| 久久国际精品| 99视频+国产日韩欧美| 日韩国产欧美| 日韩在线麻豆| 日韩福利一区| 美国欧美日韩国产在线播放| 国产网站在线| 国产精品自拍区| 免费人成在线不卡| 日韩中文视频| 精品亚洲a∨| 日韩中文字幕一区二区高清99| 一区二区三区四区日本视频| 日韩高清国产一区在线| 九九综合在线| 中文字幕成在线观看| 日韩国产在线一| 国产精品日本欧美一区二区三区| 水蜜桃久久夜色精品一区| 欧美在线日韩| 亚洲综合另类| 亚洲成人国产| 亚洲最新无码中文字幕久久 | 国产精品嫩模av在线| 亚洲影院天堂中文av色| 99热精品在线观看| 亚洲午夜精品久久久久久app| 激情综合婷婷| 免费在线播放第一区高清av| 日韩精品免费视频一区二区三区| 国产精品美女久久久| 欧美久久精品一级c片| 国产盗摄——sm在线视频| **爰片久久毛片| 日日摸夜夜添夜夜添国产精品| 在线一区欧美| 日韩视频一区二区三区在线播放免费观看 | 精品亚洲a∨一区二区三区18| 奇米色欧美一区二区三区| 六月天综合网| 欧美日韩国产传媒| 国产综合色产| 国产综合亚洲精品一区二| 日韩精品看片| 久久国产亚洲| 中文字幕在线高清| 国产h片在线观看| 日韩国产一区二区三区| 日本а中文在线天堂| 日本在线高清| 桃色一区二区| 一区二区小说| 午夜电影亚洲| 国产视频一区欧美| 久久xxxx| 亚洲毛片一区| 少妇精品在线| 91精品尤物| 国产精品玖玖玖在线资源| 国产精品久久久久久久久久妞妞 | 韩国女主播一区二区三区| 国产精品久久久久蜜臀| 亚洲性色av| 免费观看久久av| 久久高清国产| 人人精品久久| 久久精品国产久精国产| 最新中文字幕在线播放| 欧美三区四区| 国产伊人精品| 蜜桃视频在线观看一区| 天堂av一区| 久久精品国产99国产精品| 你懂的亚洲视频| 欧产日产国产精品视频| 婷婷综合五月| 日韩中出av| 久久久久97| 日韩精品首页| 免费久久99精品国产| 国产伦理久久久久久妇女| 国产成人精品一区二区三区视频| 婷婷激情久久| 婷婷亚洲成人| 日产精品一区二区| 伊人久久亚洲美女图片| 日本伊人午夜精品| 国产一区二区三区亚洲综合| 另类中文字幕国产精品| 免费的成人av| 欧美xxxx性| 香蕉久久精品| 日韩不卡在线观看日韩不卡视频| 国产精品99久久免费| 国产白浆在线免费观看| 亚洲综合精品四区| 国产精品入口久久| 一本大道色婷婷在线| 亚洲综合色婷婷在线观看| 乱一区二区av| 黄色成人在线网址| 国产欧美日韩视频在线| 免费看av不卡| 亚洲自拍另类| 精品亚洲成人| 欧美在线综合| 国产精品a级| 在线亚洲国产精品网站| 国产精品日本一区二区不卡视频| 久久久久国产精品一区二区| 亚洲精品无播放器在线播放| 福利一区和二区| 中文字幕中文字幕精品| 国产在视频一区二区三区吞精| 91精品二区| 麻豆精品在线| 欧美综合二区| zzzwww在线看片免费| 视频一区欧美精品| 国语精品一区| 日韩亚洲精品在线观看| 欧美精品资源| 国产欧美大片| 奶水喷射视频一区| 亚洲免费福利| 国产精品2区|