<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          學(xué)會JavaScript手寫代碼秘籍14道常用api

          共 11814字,需瀏覽 24分鐘

           ·

          2022-07-26 12:31


          原文:https://juejin.cn/post/7124163407577612302

          愿意花更多的時間、傾注更多的精力來跟大家一起努力。

          目錄

          • apply

          • async await

          • bind

          • call

          • concurrent-request

          • debounce

          • deep-copy

          • event-bus

          • 繼承

          • instanceof

          • new

          • object-create

          • promise

          • throttle

          • 參與

          apply

          • 為函數(shù)綁定執(zhí)行上下文

          • 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文的方法

          • tx 指定的函數(shù)執(zhí)行上下文

          • args 剩余參數(shù)組成的數(shù)組

          • any 返回函數(shù)的執(zhí)行結(jié)果

          // 為函數(shù)綁定執(zhí)行上下文// 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文的方法// ctx 指定的函數(shù)執(zhí)行上下文// args 剩余參數(shù)組成的數(shù)組// any 返回函數(shù)的執(zhí)行結(jié)果Function.prototype.myApply = function (ctx, args) { // fn.myApply(ctx, [arg1, arg2])

          // this是正在執(zhí)行的函數(shù)
          const fn = this
          // 保證 ctx[key] 的唯一性,避免和用戶設(shè)置的 context[key] 沖突
          const key = Symbol() // 將執(zhí)行函數(shù)設(shè)置到指定的上下文對象上
          ctx[key] = fn // 執(zhí)行函數(shù)
          const res = ctx[key](...args) // 刪除上下文上的 fn 方法
          delete ctx[key] // 返回函數(shù)的執(zhí)行結(jié)果
          return res
          }復(fù)制代碼

          async await

          • async await 是 Generator 的語法糖,其本質(zhì)是 Generator + 自動執(zhí)行器

          • Generator 函數(shù)

          • 執(zhí)行 generator 函數(shù),拿到 yield 表達式的執(zhí)行結(jié)果 => { next: () => void }

          • 自動執(zhí)行器

          • { value: any, done: boolean }

          • 說明 yield 后面跟的是 Promise 實例

          // async await 是 Generator 的語法糖,其本質(zhì)是 Generator + 自動執(zhí)行器// Generator 函數(shù)module.exports = function asyncAwait(generatorFn) { // 執(zhí)行 generator 函數(shù),拿到 yield 表達式的執(zhí)行結(jié)果 => { next: () => void }
          const yieldExpRet = generatorFn() // 自動執(zhí)行器
          function autoActuator() { // { value: any, done: boolean }
          const ret = yieldExpRet.next() if (!ret.done) { if (object.prototype.toString.call(ret?.value?.then) === '[object Function]') { // 說明 yield 后面跟的是 Promise 實例
          ret.value.then(() => { autoActuator()
          })
          } else { // 同步
          autoActuator()
          }
          }
          } autoActuator()
          }復(fù)制代碼

          bind

          • 為函數(shù)綁定執(zhí)行上下文

          • 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文上的方法

          • ctx 指定的函數(shù)執(zhí)行上下文

          • args 剩余參數(shù)組成的數(shù)組

          • fn.myBind(ctx, [arg1, arg2])

          • this是正在執(zhí)行的函數(shù)

          • 保證 ctx[key] 的唯一性,避免和用戶設(shè)置的 context[key] 沖突

          • 將執(zhí)行函數(shù)設(shè)置到指定的上下文對象上

          • 返回一個可執(zhí)行函數(shù)

          • bind 方法支持預(yù)設(shè)一部分參數(shù),剩下的參數(shù)通過返回的函數(shù)設(shè)置,具有柯里化的作用

          • 執(zhí)行函數(shù)

          // 為函數(shù)綁定執(zhí)行上下文// 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文上的方法// ctx 指定的函數(shù)執(zhí)行上下文// args 剩余參數(shù)組成的數(shù)組Function.prototype.myBind = function (ctx, ...args) { // fn.myBind(ctx, [arg1, arg2])

          // this是正在執(zhí)行的函數(shù)
          const fn = this
          // 保證 ctx[key] 的唯一性,避免和用戶設(shè)置的 context[key] 沖突
          const key = Symbol() // 將執(zhí)行函數(shù)設(shè)置到指定的上下文對象上
          ctx[key] = fn // 返回一個可執(zhí)行函數(shù)
          // bind 方法支持預(yù)設(shè)一部分參數(shù),剩下的參數(shù)通過返回的函數(shù)設(shè)置,具有柯里化的作用
          return function(...otherArgs) { // 執(zhí)行函數(shù)
          return ctx[key](...args, ...otherArgs)
          }
          }復(fù)制代碼

          call

          • 為函數(shù)綁定指定上下文

          • 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文上的方法

          • ctx 指定的函數(shù)執(zhí)行上下文

          • args 剩余參數(shù)組成的數(shù)組

          • any 返回函數(shù)的執(zhí)行結(jié)果

          • fn.myCall(ctx, arg1, arg2)

          • this是正在執(zhí)行的函數(shù)

          • 保證 ctx[key] 的唯一性,避免和用戶設(shè)置的 context[key] 沖突

          • 將執(zhí)行函數(shù)設(shè)置到指定的上下文對象上

          • 執(zhí)行函數(shù)

          • 刪除上下文上的fn方法

          • 返回函數(shù)的執(zhí)行結(jié)果

          // 為函數(shù)綁定指定上下文// 原理:將函數(shù)設(shè)置為執(zhí)行上下文的一個方法,然后調(diào)用執(zhí)行上下文上的方法// ctx 指定的函數(shù)執(zhí)行上下文// args 剩余參數(shù)組成的數(shù)組// any 返回函數(shù)的執(zhí)行結(jié)果Function.prototype.myCall = function (ctx, ...args) { // fn.myCall(ctx, arg1, arg2)

          // this是正在執(zhí)行的函數(shù)
          const fn = this
          // 保證 ctx[key] 的唯一性,避免和用戶設(shè)置的 context[key] 沖突
          const key = Symbol() // 將執(zhí)行函數(shù)設(shè)置到指定的上下文對象上
          ctx[key] = fn // 執(zhí)行函數(shù)
          const res = ctx[key](...args) // 刪除上下文上的fn方法
          delete ctx[key] // 返回函數(shù)的執(zhí)行結(jié)果
          return res
          }復(fù)制代碼

          concurrent-request

          • 并發(fā)請求,控制請求并發(fā)數(shù)

          • taskQueues 一個個請求任務(wù)組成的數(shù)組

          • concurrentNum 請求的并發(fā)數(shù)

          • 存放所有任務(wù)的執(zhí)行結(jié)果

          • 開始先發(fā)送指定數(shù)量的并發(fā)請求

          • 當(dāng)每個請求完成后再遞歸的調(diào)用自身,發(fā)送任務(wù)隊列的下一個請求

          • 遞歸終止條件(任務(wù)隊列為空)

          • 從任務(wù)隊列中彈出一個任務(wù)

          • 執(zhí)行任務(wù)

          • 當(dāng)任務(wù)完成后遞歸調(diào)用 req, 發(fā)送隊列中的下一個請求

          • 并將任務(wù)結(jié)果 push 進結(jié)果數(shù)組中

          // 并發(fā)請求,控制請求并發(fā)數(shù)// taskQueues 一個個請求任務(wù)組成的數(shù)組// concurrentNum 請求的并發(fā)數(shù)module.exports = function concurrentRequest(taskQueues = [], concurrentNum = 1) { return new Promise(resolve => {  // 存放所有任務(wù)的執(zhí)行結(jié)果
          const taskRet = [] // 開始先發(fā)送指定數(shù)量的并發(fā)請求
          while (concurrentNum > 0) { req()
          concurrentNum--
          } // 當(dāng)每個請求完成后再遞歸的調(diào)用自身,發(fā)送任務(wù)隊列的下一個請求
          function req() { // 遞歸終止條件(任務(wù)隊列為空)
          if (!taskQueues.length) return Promise.allSettled(taskRet).then(res => { resolve(res)
          }) // 從任務(wù)隊列中彈出一個任務(wù)
          const task = taskQueues.shift() // 執(zhí)行任務(wù)
          const ret = task() // 當(dāng)任務(wù)完成后遞歸調(diào)用 req, 發(fā)送隊列中的下一個請求
          res.then(() => { req()
          }) // 并將任務(wù)結(jié)果 push 進結(jié)果數(shù)組中
          taskRet.push(ret)
          }
          })
          }復(fù)制代碼

          debounce

          • 防抖

          • 原理:事件被觸發(fā) wait 毫秒后執(zhí)行回調(diào)fn, 如果在wait期間再次觸發(fā)事件,則重新計時

          • fn 事件觸發(fā)后的回調(diào)函數(shù)

          • wait 延遲時間,wait 毫秒后執(zhí)行fn

          • 返回經(jīng)過包裝后的事件處理函數(shù)

          • 定時器,這里用到了閉包

          • 返回經(jīng)過包裝后的事件處理函數(shù)

          • 如果 timer 為不為空,則說明在 wait 時間內(nèi)已經(jīng)觸發(fā)過該事件了,而且事件處理函數(shù)仍未被調(diào)用

          • 說明在wait事件內(nèi)事件被重復(fù)觸發(fā)了,則需要進行防抖處理,即清除之前的定時器,這樣上一次事件觸發(fā)后的回調(diào)就不會被執(zhí)行

          • 定時器也會重新設(shè)置

          • 通過定時器來實現(xiàn)事件觸發(fā)后在 wait 毫秒后執(zhí)行事件處理函數(shù)

          • 需要給回調(diào)綁定上下文this,即觸發(fā)事件的目標對象

          // 防抖// 原理:事件被觸發(fā) wait 毫秒后執(zhí)行回調(diào)fn, 如果在wait期間再次觸發(fā)事件,則重新計時// fn 事件觸發(fā)后的回調(diào)函數(shù)// wait 延遲時間,wait 毫秒后執(zhí)行fn// 返回經(jīng)過包裝后的事件處理函數(shù)function debounce(fn, wait = 50) { // 定時器,這里用到了閉包
          let timer = null

          // 返回經(jīng)過包裝后的事件處理函數(shù)
          return function(...args) { // 如果 timer 為不為空,則說明在 wait 時間內(nèi)已經(jīng)觸發(fā)過該事件了,而且事件處理函數(shù)仍未被調(diào)用
          // 說明在wait事件內(nèi)事件被重復(fù)觸發(fā)了,則需要進行防抖處理,即清除之前的定時器,這樣上一次事件觸發(fā)后的回調(diào)就不會被執(zhí)行
          // 定時器也會重新設(shè)置
          if (timer) { clearTimeout(timer)
          } // 通過定時器來實現(xiàn)事件觸發(fā)后在 wait 毫秒后執(zhí)行事件處理函數(shù)
          timer = setTimeout(() => { // 需要給回調(diào)綁定上下文this,即觸發(fā)事件的目標對象
          fn.apply(this, args)
          timer = null
          }, wait)
          }
          }復(fù)制代碼

          deep-copy

          • 深拷貝

          • src 原數(shù)據(jù)

          • 返回拷貝后的數(shù)據(jù)

          • 拷貝原始值,直接返回原始值本身

          • 解決循環(huán)引用的問題

          • 拷貝數(shù)組

          • 拷貝 Map 對象

          • 拷貝函數(shù)

          • 拷貝對象

          • 判斷數(shù)據(jù)是否為原始值類型(Number, Boolean,String,Symbol ,BigInt ,Null ,Undefined)

          • Number,Boolean,String,Symbol,BigInt,Null,Undefined,Object,Array,Function,Date...

          // 深拷貝// src 原數(shù)據(jù)// 返回拷貝后的數(shù)據(jù)module.exports = function deepCopy(src, cache = new WeakMap()) { // 拷貝原始值,直接返回原始值本身
          if (isPrimitiveType(src)) return src // 解決循環(huán)引用的問題
          if (cache.has(src)) return src
          cache.set(src, true) // 拷貝數(shù)組
          if (isArray(src)) { const ret = [] for (let i = 0, len = src.length; i < len; i++) {
          ret.push(deepCopy(src[i], cache))
          } return ret
          } // 拷貝 Map 對象
          if (isMap(src)) { const ret = new Map()
          src.forEach((value, key) => {
          ret.set(key, deepCopy(value, cache))
          }) return ret
          } // 拷貝函數(shù)
          if (isFunction(src)) { copyFunction(src)
          } // 拷貝對象
          if (isObject(src)) { // 獲取對象上的所有key
          const keys = [...Object.keys(src), ...Object.getOwnPropertySymbols(src)] const ret = {} // 遍歷所有的key,遞歸調(diào)用 deepCopy 拷貝 obj[key] 的值
          keys.forEach(item => {
          ret[item] = deepCopy(src[item], cache)
          }) // 返回拷貝后的對象
          return ret
          }

          }// 判斷數(shù)據(jù)是否為原始值類型(Number, Boolean,String,Symbol ,BigInt ,Null ,Undefined)function isPrimitiveType(data) { const primitiveType = ['Number', 'Boolean', 'String', 'Symbol', 'BigInt', 'Null', 'Undefined'] return primitiveType.includes(getDataType(data))
          }// 判斷數(shù)據(jù)是否為Object類型function isObject(data) { return getDataType(data) === 'Object'}// 判斷數(shù)據(jù)是否為函數(shù)function isFunction(data) { return getDataType(data) === 'Function'}// 判斷數(shù)據(jù)是否為數(shù)組function isArray(data) { return getDataType(data) === 'Array'}// 判斷數(shù)據(jù)是否為Mapfunction isMap(data) { return getDataType(data) === 'Map'}// 獲取數(shù)據(jù)類型// Number,Boolean,String,Symbol,BigInt,Null,Undefined,Object,Array,Function,Date...function getDataType(data) { return Object.prototype.toString.apply(data).slice(8, -1)
          }// 拷貝函數(shù)function copyFunction(src) { const fnName = src.name
          let srcStr = src.toString() // 匹配function fnName, 比如 function fnName() {}
          const fnDecExp = new RegExp(`function (${fnName})?`) // 切除匹配內(nèi)容,srcStr = (xxx) {} 或 (xxx) => {}
          srcStr = srcStr.replace(fnDecExp, '') // 匹配函數(shù)參數(shù)
          const argsExg = /\((.*)\)/
          let args = argsExg.exec(srcStr) // 函數(shù)體
          const fnBody = srcStr.replace(argsExg, '').trim() // { return 'test' } => return 'test'
          const fnBodyCode = /^{(.*)}$/.exec(fnBody) // 得到了函數(shù)的名稱,參數(shù),函數(shù)體,重新聲明函數(shù)
          return new Function(...args[1].split(','), fnBodyCode[1])
          }復(fù)制代碼

          event-bus

          • Event bus

          • 發(fā)布訂閱設(shè)計模式的應(yīng)用,node.js 的基礎(chǔ)模塊,也是前端組件通信的一種手段,比如Vue的on和emit

          • 以事件名為key,事件處理函數(shù)組成的數(shù)組為value

          • 監(jiān)聽事件

          • eventName 事件名

          • cb 事件處理函數(shù)

          // Event bus// 發(fā)布訂閱設(shè)計模式的應(yīng)用,node.js 的基礎(chǔ)模塊,也是前端組件通信的一種手段,比如Vue的$on和$emitfunction EventBus() { // 以事件名為key,事件處理函數(shù)組成的數(shù)組為value
          this.events = {}
          }module.exports = EventBus// 監(jiān)聽事件// eventName 事件名// cb 事件處理函數(shù)EventBus.prototype.$on = function(eventName, cb) { if (!Array.isArray(cb)) {
          cb = [cb]
          } this.events[eventName] = (this.events[eventName] || []).concat(cb)
          }EventBus.prototype.$emit = function(eventName, ...args) { this.events[eventName].foEach(fn => {
          fn.apply(this, args)
          })
          }復(fù)制代碼

          繼承

          • JavaScript 的繼承方式有很多,比如簡單的基于 Object.create 實現(xiàn)的繼承,每種方式或多或少都有些缺陷

          • 這種缺陷是語言層面導(dǎo)致的,避免不了,即使是 class 語法(糖)。

          • 組合式繼承,class 語法糖的本質(zhì)

          • 在this上繼承父類的屬性

          • 繼承父類的方法

          • 恢復(fù)子類的構(gòu)造函數(shù),上面一行會將 Child.prototype.constructor 改為 Parent.prototype.constructor

          // JavaScript 的繼承方式有很多,比如簡單的基于 Object.create 實現(xiàn)的繼承,每種方式或多或少都有些缺陷,// 這種缺陷是語言層面導(dǎo)致的,避免不了,即使是 class 語法(糖)。// 組合式繼承,class 語法糖的本質(zhì)function Parent(...args) { this.name = 'Parent name'
          this.args = args
          }Parent.prototype.parentFn = function() { console.log('name = ', this.name) console.log('args = ', this.args)
          }function Chid(args1,args2) { // 在this上繼承父類的屬性
          Parent.call(this, args1, args2) this.childName = 'child name'}// 繼承父類的方法Child.prototype = Object.create(Parent.prototype)// 恢復(fù)子類的構(gòu)造函數(shù),上面一行會將 Child.prototype.constructor 改為 Parent.prototype.constructorChild.prototype.constructor = Childmodule.exports = Child復(fù)制代碼

          instanceof

          • instanceof運算符

          • 定義:判斷對象是否屬于某個構(gòu)造函數(shù)的實例

          • 原理:判斷構(gòu)造函數(shù)的原型對象是否出現(xiàn)在對象的原型鏈上

          // instanceof運算符// 定義:判斷對象是否屬于某個構(gòu)造函數(shù)的實例// 原理:判斷構(gòu)造函數(shù)的原型對象是否出現(xiàn)在對象的原型鏈上module.exports = function customINstanceof (ins, constructor) { const proto = Object.getPrototypeOf(ins) if (proto === constructor.prototype) return true
          if (!proto) return false

          return customINstanceof(proto, constructor)
          }復(fù)制代碼

          new

          • new 運算符

          • 作用:負責(zé)實例化一個類(構(gòu)造函數(shù))

          • 1.創(chuàng)建一個構(gòu)造函數(shù)原型對象為原型的對象

          • 2.以第一步的對象為上下文執(zhí)行構(gòu)造函數(shù)

          • 3.返回值,如果函數(shù)有返回值,則返回函數(shù)的返回值,否則返回第一步創(chuàng)建的對象。

          // new 運算符// 作用:負責(zé)實例化一個類(構(gòu)造函數(shù))// 原理:// 1.創(chuàng)建一個構(gòu)造函數(shù)原型對象為原型的對象// 2.以第一步的對象為上下文執(zhí)行構(gòu)造函數(shù)// 3.返回值,如果函數(shù)有返回值,則返回函數(shù)的返回值,否則返回第一步創(chuàng)建的對象。// Function 構(gòu)造函數(shù)// Array 構(gòu)造函數(shù)的其他參數(shù)組成的數(shù)組// 對象實例module.exports = function newOperator(constructor, ...args) { const ins = Object.create(constructor.prototype) const res = constructor.apply(ins, args) return res || ins
          }復(fù)制代碼

          object-create

          • proto 新對象的原型對象

          • props Object.defineProperties 的第二個參數(shù),要定義其可枚舉屬性或修改的屬性描述符的對象。對象中存在的屬性描述符:數(shù)據(jù)描述符和訪問器描述符

          // Object.create// proto 新對象的原型對象// props Object.defineProperties 的第二個參數(shù),要定義其可枚舉屬性或修改的屬性描述符的對象。對象中存在的屬性描述符:數(shù)據(jù)描述符和訪問器描述符Object.myCreate = function(proto, props) { if (typeof proto !== 'object') {  console.error('Object prototype may only be an Object or null')  return
          } // 創(chuàng)建的空對象
          const obj = {} // 設(shè)置原型對象
          Object.setPrototypeOf(obj, proto) // 設(shè)置對象的初始數(shù)據(jù)
          if (props) { Object.defineProperties(obj, props)
          } return obj
          }復(fù)制代碼

          promise

          • Promise,解決了回調(diào)地獄的問題

          • executor 同步執(zhí)行

          • promise 狀態(tài)不可逆

          • then 回調(diào)必須在 promise 狀態(tài)改變后執(zhí)行

          • promise 鏈式調(diào)用,后一個回調(diào)的參數(shù)是前一個回調(diào)的返回值

          • 實例化 Promise 時 executor 被同步執(zhí)行

          // Promise,解決了回調(diào)地獄的問題// executor 同步執(zhí)行// promise 狀態(tài)不可逆// then 回調(diào)必須在 promise 狀態(tài)改變后執(zhí)行// promise 鏈式調(diào)用,后一個回調(diào)的參數(shù)是前一個回調(diào)的返回值// 實例化 Promise 時 executor 被同步執(zhí)行function MyPromise(executor) { // 緩存this實例
          const _self = this
          this.status = 'pending'
          this.value = undefined
          this.reason = undefined
          this.fulfilledCb = () => {} this.rejectedCb = () => {} function resolve(value) { setTimeout(() => { // 狀態(tài)不可逆
          if (_self.status === 'pending') {
          _self.status = 'fulfilled'
          _self.value = value
          _self.fulfilledCb(value)
          }
          })
          } function reject(errMsg) { setTimeout(() => { // 狀態(tài)不可逆
          if (_self.status === 'pending') {
          _self.status = 'rejected'
          _self.reason = errMsg
          _self.rejectedCb(errMsg)
          }
          })
          } try { executor(resolve, reject)
          } catch (err) { reject(err)
          }

          }MyPromise.prototype.then = function(fulfilledCb, rejectedCb) { const _self = this
          return new MyPromise((resolve, reject) => {
          _self.fulfilledCb = function (value) { resolve(fulfilledCb(value))
          }
          _self.rejectedCb = function (reason) { reject(rejectedCb(reason))
          }
          })
          }MyPromise.race = function (promiseArr) { return new MyPromise((resolve, reject) => { for (let i = 0, len = promiseArr.length; i < len; i++) { const p = promiseArr[i]
          p.then(resolve, reject)
          }
          })
          }MyPromise.all = function (promiseArr) { return new MyPromise((resolve, reject) => { const len = promiseArr.length
          const result = [] for (let i = 0; i < len; i++) { const p = promiseArr[i]
          p.then((res) => {
          result.push(res) if (result.length === len) { resolve(result)
          }
          }, (errMsg) => { reject(errMsg)
          })
          }
          })
          }module.exports = MyPromise復(fù)制代碼

          throttle

          • 節(jié)流

          • 原理:事件被頻繁觸發(fā)時,事件回調(diào)函數(shù)會按照固定頻率執(zhí)行,比如1s 執(zhí)行一次,只有上個事件回調(diào)被執(zhí)行之后下一個事件回調(diào)才會執(zhí)行

          • 事件回調(diào)函數(shù)

          • wait 事件回調(diào)的執(zhí)行頻率,每wait毫秒執(zhí)行一次

          // 節(jié)流// 原理:事件被頻繁觸發(fā)時,事件回調(diào)函數(shù)會按照固定頻率執(zhí)行,比如1s 執(zhí)行一次,只有上個事件回調(diào)被執(zhí)行之后下一個事件回調(diào)才會執(zhí)行// 事件回調(diào)函數(shù)// wait 事件回調(diào)的執(zhí)行頻率,每wait毫秒執(zhí)行一次function throttle(fn, wait = 500) { let timer = null
          return function(...args) { if (timer) return

          timer = setTimeout(() => {
          fn.apply(this, args)
          timer = null
          }, wait)
          }
          }復(fù)制代碼

          go!go!go!

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产青青草 | www.鸡吧 | 爱操在线 | 欧美大操逼| 堆女郎松果浴室自慰正在播放 |