成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

【ES6】改變 JS 內(nèi)置行為的代理與反射

lushan / 2331人閱讀

摘要:通過(guò)對(duì)這些底層內(nèi)置對(duì)象的代理陷阱和反射函數(shù),讓開(kāi)發(fā)者能進(jìn)一步接近引擎的能力。顯然,與要求代理目標(biāo)對(duì)象必須是一個(gè)函數(shù),這兩個(gè)代理陷阱在函數(shù)的執(zhí)行方式上開(kāi)啟了很多的可能性,結(jié)合使用就可以完全控制任意的代理目標(biāo)函數(shù)的行為。

代理(Proxy)可以攔截并改變 JS 引擎的底層操作,如數(shù)據(jù)讀取、屬性定義、函數(shù)構(gòu)造等一系列操作。ES6 通過(guò)對(duì)這些底層內(nèi)置對(duì)象的代理陷阱和反射函數(shù),讓開(kāi)發(fā)者能進(jìn)一步接近 JS 引擎的能力。
一、代理與反射的基本概念

什么是代理和反射呢?
代理是用來(lái)替代另一個(gè)對(duì)象(target),JS 通過(guò)new Proxy()創(chuàng)建一個(gè)目標(biāo)對(duì)象的代理,該代理與該目標(biāo)對(duì)象表面上可以被當(dāng)作同一個(gè)對(duì)象來(lái)對(duì)待

當(dāng)目標(biāo)對(duì)象上的進(jìn)行一些特定的底層操作時(shí),代理允許你攔截這些操作并且覆寫(xiě)它,而這原本只是 JS 引擎的內(nèi)部能力。

如果你對(duì)些代理&反射的概念比較困惑的話,可以直接看后面的應(yīng)用示例,最后再重新看這些定義就會(huì)更清晰!

攔截行為使用了一個(gè)能夠響應(yīng)特定操作的函數(shù)( 被稱(chēng)為陷阱),每個(gè)代理陷阱對(duì)應(yīng)一個(gè)反射(Reflect)方法。

ES6 的反射 API 以 Reflect 對(duì)象的形式出現(xiàn),對(duì)象每個(gè)方法都與對(duì)應(yīng)的陷阱函數(shù)同名,并且接收的參數(shù)也與之一致。以下是 Reflect 對(duì)象的一些方法:

代理陷阱 覆寫(xiě)的特性 方法
get 讀取一個(gè)屬性的值 Reflect.get()
set 寫(xiě)入一個(gè)屬性 Reflect.set()
has in 運(yùn)算符 Reflect.has()
deleteProperty delete 運(yùn)算符 Reflect.deleteProperty()
getPrototypeOf Object.getPrototypeOf() Reflect.getPrototypeOf()
isExtensible Object.isExtensible() Reflect.isExtensible()
defineProperty Object.defineProperty() Reflect.defineProperty
apply 調(diào)用一個(gè)函數(shù) Reflect.apply()
construct 使用 new 調(diào)用一個(gè)函數(shù) Reflect.construct()

每個(gè)陷阱函數(shù)都可以重寫(xiě) JS 對(duì)象的一個(gè)特定內(nèi)置行為,允許你攔截并修改它。

綜合來(lái)說(shuō),想要控制或改變JS的一些底層操作,可以先創(chuàng)建一個(gè)代理對(duì)象,在這個(gè)代理對(duì)象上掛載一些陷阱函數(shù),陷阱函數(shù)里面有反射方法。通過(guò)接下來(lái)的應(yīng)用示例可以更清晰的明白代理的過(guò)程。

二、開(kāi)始一個(gè)簡(jiǎn)單的代理

當(dāng)你使用 Proxy 構(gòu)造器來(lái)創(chuàng)建一個(gè)代理時(shí),需要傳遞兩個(gè)參數(shù):目標(biāo)對(duì)象(target)以及一個(gè)處理器( handler),

創(chuàng)建一個(gè)僅進(jìn)行傳遞的代理如下:

// 目標(biāo)對(duì)象
let target = {}; 
// 代理對(duì)象
let proxy = new Proxy(target, {});

proxy.name = "hello";
console.log(proxy.name); // "hello"
console.log(target.name); // "hello"

target.name = "world";
console.log(proxy.name); // "world"
console.log(target.name); // "world

上例中的 proxy 代理對(duì)象將所有操作直接傳遞給 target 目標(biāo)對(duì)象,代理對(duì)象 proxy 自身并沒(méi)有存儲(chǔ)該屬性,它只是簡(jiǎn)單將值傳遞給 target 對(duì)象,proxy.name 與 target.name 的屬性值總是相等,因?yàn)樗鼈兌贾赶?target.name。

此時(shí)代理陷阱的處理器為空對(duì)象,當(dāng)然處理器可以定義了一個(gè)或多個(gè)陷阱函數(shù)。

2.1 set 驗(yàn)證對(duì)象屬性的存儲(chǔ)

假設(shè)你想要?jiǎng)?chuàng)建一個(gè)對(duì)象,并要求其屬性值只能是數(shù)值,這就意味著該對(duì)象的每個(gè)新增屬性
都要被驗(yàn)證,并且在屬性值不為數(shù)值類(lèi)型時(shí)應(yīng)當(dāng)拋出錯(cuò)誤。

這時(shí)需要使用 set 陷阱函數(shù)來(lái)攔截傳入的 value,該陷阱函數(shù)能接受四個(gè)參數(shù):

trapTarget :將接收屬性的對(duì)象( 即代理的目標(biāo)對(duì)象)

key :需要寫(xiě)入的屬性的鍵( 字符串類(lèi)型或符號(hào)類(lèi)型)

value :將被寫(xiě)入屬性的值;

receiver :操作發(fā)生的對(duì)象( 通常是代理對(duì)象)

set 陷阱對(duì)應(yīng)的反射方法和默認(rèn)特性是Reflect.set(),和陷阱函數(shù)一樣接受這四個(gè)參數(shù),并會(huì)基于操作是否成功而返回相應(yīng)的結(jié)果:

let targetObj = {};
let proxyObj = new Proxy(targetObj, {
  set: set
});

/* 定義 set 陷阱函數(shù) */
function set (trapTarget, key, value, receiver) {
  if (isNaN(value)) {
     throw new TypeError("Property " + key + " must be a number.");
  }
  return Reflect.set(trapTarget, key, value, receiver);
}

/* 測(cè)試 */
proxyObj.count = 123;
console.log(proxyObj.count); // 123
console.log(targetObj.count); // 123

proxyObj.anotherName = "proxy" // TypeError: Property anotherName must be a number.

示例中set 陷阱函數(shù)成功攔截傳入的 value 值,你可以嘗試一下,如果注釋或不return Reflect.set()會(huì)發(fā)生什么?,答案是攔截陷阱就不會(huì)有反射響應(yīng)。

需要注意的是,直接給 targetObj 目標(biāo)對(duì)象賦值時(shí)是不會(huì)觸發(fā) set 代理陷阱的,需要通過(guò)給代理對(duì)象賦值才會(huì)觸發(fā) set 代理陷阱與反射。

2.2 get 驗(yàn)證對(duì)象屬性的讀取

JS 非常有趣的特性之一,是讀取不存在的屬性時(shí)并不會(huì)拋出錯(cuò)誤,而會(huì)把undefined當(dāng)作該屬性的值。

對(duì)于大型的代碼庫(kù),當(dāng)屬性名稱(chēng)存在書(shū)寫(xiě)錯(cuò)誤時(shí)(不會(huì)拋錯(cuò))會(huì)導(dǎo)致嚴(yán)重的問(wèn)題。這時(shí)使用 get 代理陷阱驗(yàn)證對(duì)象結(jié)構(gòu)(Object Shape),訪問(wèn)不存在的屬性時(shí)就拋出錯(cuò)誤,使對(duì)象結(jié)構(gòu)驗(yàn)證變得簡(jiǎn)單。

get 陷阱函數(shù)會(huì)在讀取屬性時(shí)被調(diào)用,即使該屬性在對(duì)象中并不存在,它能接受三個(gè)參數(shù):

trapTarget :將會(huì)被讀取屬性的對(duì)象( 即代理的目標(biāo)對(duì)象)

key :需要讀取的屬性的鍵( 字符串類(lèi)型或符號(hào)類(lèi)型)

receiver :操作發(fā)生的對(duì)象( 通常是代理對(duì)象)

Reflect.get()方法接受與之相同的參數(shù),并返回默認(rèn)屬性的默認(rèn)值。

let proxyObj = new Proxy(targetObj, {
  set: set,
  get: get
});

/* 定義 get 陷阱函數(shù) */
function get(trapTarget, key, receiver) {
  if (!(key in receiver)) {
    throw new TypeError("Property " + key + " doesn"t exist.");
  }
  return Reflect.get(trapTarget, key, receiver);
}

console.log(proxyObj.count); // 123
console.log(proxyObj.newcount) // TypeError: Property newcount doesn"t exist.

這段代碼允許添加新的屬性,并且此后可以正常讀取該屬性的值,但當(dāng)讀取的屬性并
不存在時(shí),程序拋出了一個(gè)錯(cuò)誤,而不是將其默認(rèn)為undefined

還可以使用 has 陷阱驗(yàn)證in運(yùn)算符,使用 deleteProperty 陷阱函數(shù)避免屬性被delete刪除。

注:in運(yùn)算符用于判斷對(duì)象中是否存在某個(gè)屬性,如果自有屬性或原型屬性匹配這個(gè)名稱(chēng)字符串或Symbol,那么in運(yùn)算符返回 true。

targetObj = {
  name: "targetObject"
};
console.log("name" in targetObj); // true
console.log("toString" in targetObj); // true

其中 name 是對(duì)象自身的屬性,而 toString 則是原型屬性( 從 Object 對(duì)象上繼承而來(lái)),所以檢測(cè)結(jié)果都為 true。

has 陷阱函數(shù)會(huì)在使用in運(yùn)算符時(shí)被調(diào)用,并且會(huì)傳入兩個(gè)參數(shù)(同名反射Reflect.has()方法也一樣):

trapTarget :需要讀取屬性的對(duì)象( 代理的目標(biāo)對(duì)象)

key :需要檢查的屬性的鍵( 字符串類(lèi)型或 Symbol符號(hào)類(lèi)型)

deleteProperty 陷阱函數(shù)會(huì)在使用delete運(yùn)算符去刪除對(duì)象屬性時(shí)下被調(diào)用,并且也會(huì)被傳入兩個(gè)參數(shù)(Reflect.deleteProperty() 方法也接受這兩個(gè)參數(shù)):

trapTarget :需要?jiǎng)h除屬性的對(duì)象( 即代理的目標(biāo)對(duì)象) ;

key :需要?jiǎng)h除的屬性的鍵( 字符串類(lèi)型或符號(hào)類(lèi)型) 。

一些思考:分析過(guò) Vue 源碼的都了解過(guò),給一個(gè) Vue 實(shí)例中掛載的 data,是通過(guò)Object.defineProperty代理 vm._data 中的對(duì)象屬性,實(shí)現(xiàn)雙向綁定...... 同理可以考慮使用 ES6 的 Proxy 的 get 和 set 陷阱實(shí)現(xiàn)這個(gè)代理。
三、對(duì)象屬性陷阱 3.1 數(shù)據(jù)屬性與訪問(wèn)器屬性

ES5 最重要的特征之一就是引入了 Object.defineProperty() 方法定義屬性的特性。屬性的特性是為了實(shí)現(xiàn)javascript引擎用的,屬于內(nèi)部值,因此不能直接訪問(wèn)他們。

屬性分為數(shù)據(jù)屬性和訪問(wèn)器屬性。使用Object.defineProperty()方法修改數(shù)據(jù)屬性的特性值的示例如下:

let obj1 = {
  name: "myobj",
}
/* 數(shù)據(jù)屬性*/
Object.defineProperty(obj1,"name",{
  configurable: false, // default true
  writable: false,     // default true
  enumerable: true,    // default true
  value: "jenny"       // default undefined
})
console.log(obj1.name) // "jenny"

其中[[Configurable]] 表示能否通過(guò) delete 刪除屬性從而重新定義為訪問(wèn)器屬性;[[Enumerable]] 表示能否通過(guò)for-in循環(huán)返回屬性;[[Writable]] 表示能否修改屬性的值; [[Value]] 包含這個(gè)屬性的數(shù)據(jù)值。

對(duì)于訪問(wèn)器屬性,該屬性不包含數(shù)據(jù)值,包含一對(duì)getter和setter函數(shù),定義訪問(wèn)器屬性必須使用Object.defineProperty()方法:

let obj2 = {
  age: 18
}
/* 訪問(wèn)器屬性 */
Object.defineProperty(obj2,"_age",{
  configurable: false, // default true
  enumerable: false,   // default true
  get () {             // default undefined
    return this.age
  },
  set (num) {          // default undefined
    this.age = num
  }
})
/* 修改訪問(wèn)器屬性調(diào)用 getter */
obj2._age = 20  
console.log(obj2.age)  // 20

/* 輸出訪問(wèn)器屬性 */
console.log(Object.getOwnPropertyDescriptor(obj2,"_age")) 
// { get: [Function: get],
//   set: [Function: set],
//   enumerable: false,
//   configurable: false }

[[Get]] 在讀取屬性時(shí)調(diào)用的函數(shù), [[Set]] 再寫(xiě)入屬性時(shí)調(diào)用的函數(shù)。使用訪問(wèn)器屬性的常用方式,是設(shè)置一個(gè)屬性的值導(dǎo)致其他屬性發(fā)生變化。

3.2 檢查屬性的修改

代理允許你使用 defineProperty 同名函數(shù)陷阱函數(shù)攔截Object.defineProperty()的調(diào)用,defineProperty 陷阱函數(shù)接受下列三個(gè)參數(shù):

trapTarget :需要被定義屬性的對(duì)象( 即代理的目標(biāo)對(duì)象);

key :屬性的鍵( 字符串類(lèi)型或符號(hào)類(lèi)型);

descriptor :為該屬性準(zhǔn)備的描述符對(duì)象。

defineProperty 陷阱函數(shù)要求在操作后返回一個(gè)布爾值用于判斷操作是否成功,如果返回了 false 則拋出錯(cuò)誤,故可以使用該功能來(lái)限制哪些屬性可以被Object.defineProperty() 方法定義。

例如,如果想阻止定義Symbol符號(hào)類(lèi)型的屬性,你可以檢查傳入的屬性值,若是則返回 false:

/* 定義代理 */
let proxy = new Proxy({}, {
  defineProperty(trapTarget, key, descriptor) {
    if (typeof key === "symbol") {
      return false;
    }
    return Reflect.defineProperty(trapTarget, key, descriptor);
  }
});

Object.defineProperty(proxy, "name", {
  value: "proxy"
});
console.log(proxy.name); // "proxy"

let nameSymbol = Symbol("name");
// 拋出錯(cuò)誤
Object.defineProperty(proxy, nameSymbol, {
  value: "proxy"
})
四、函數(shù)代理 4.1 構(gòu)造函數(shù) & 立即執(zhí)行

函數(shù)的兩個(gè)內(nèi)部方法:[[Call]][[Construct]]會(huì)在函數(shù)被調(diào)用時(shí)調(diào)用,通過(guò)代理函數(shù)來(lái)為這兩個(gè)內(nèi)部方法設(shè)置陷阱,從而控制函數(shù)的行為。

[[Construct]]會(huì)在函數(shù)被使用new運(yùn)算符調(diào)用時(shí)執(zhí)行,代理觸發(fā)construct()陷阱函數(shù),并和Reflect.construct()一樣接收到下列兩個(gè)參數(shù):

trapTarget :被執(zhí)行的函數(shù)( 即代理的目標(biāo)對(duì)象) ;

argumentsList :被傳遞給函數(shù)的參數(shù)數(shù)組。

[[Call]]會(huì)在函數(shù)被直接調(diào)用時(shí)執(zhí)行,代理觸發(fā)apply()陷阱函數(shù),它和Reflect.apply()都接收三個(gè)參數(shù):

trapTarget :被執(zhí)行的函數(shù)( 代理的目標(biāo)函數(shù)) ;

thisArg :調(diào)用過(guò)程中函數(shù)內(nèi)部的 this 值;

argumentsList :被傳遞給函數(shù)的參數(shù)數(shù)組。

每個(gè)函數(shù)都包含call()apply()方法,用于重置函數(shù)運(yùn)行的作用域即 this 指向,區(qū)別只是接收參數(shù)的方式不同:call()的參數(shù)需要逐個(gè)列舉、apply()是參數(shù)數(shù)組。

顯然,apply 與 construct 要求代理目標(biāo)對(duì)象必須是一個(gè)函數(shù),這兩個(gè)代理陷阱在函數(shù)的執(zhí)行方式上開(kāi)啟了很多的可能性,結(jié)合使用就可以完全控制任意的代理目標(biāo)函數(shù)的行為。

4.2 驗(yàn)證函數(shù)的參數(shù)

看到apply()construct()陷阱的參數(shù)都有被傳遞給函數(shù)的參數(shù)數(shù)組argumentsList,所以可以用來(lái)驗(yàn)證函數(shù)的參數(shù)。

例如需要保證所有參數(shù)都是某個(gè)特定類(lèi)型的,并且不能通過(guò) new 構(gòu)造使用,示例如下:

/* 定義 sum 目標(biāo)函數(shù) */
function sum(...values) {
  return values.reduce((previous, current) => previous + current, 0);
}
/* 定義 apply 陷阱函數(shù) */
function applyRef (trapTarget, thisArg, argumentList) {
  argumentList.forEach((arg) => {
    if (typeof arg !== "number") {
      throw new TypeError("All arguments must be numbers.");
    }
  });
  return Reflect.apply(trapTarget, thisArg, argumentList);
}
/* 定義 construct 陷阱函數(shù) */
function constructRef () {
  throw new TypeError("This function can"t be called with new.");
}
/* 定義 sumProxy 代理函數(shù) */
let sumProxy = new Proxy(sum, {
  apply: applyRef,
  construct: constructRef
});

console.log(sumProxy(1, 2, 3, 4)); // 10

// console.log(sumProxy(1, "2", 3, 4)); // TypeError: All arguments must be numbers.
// let result = new sumProxy() // TypeError: This function can"t be called with new.

sum() 函數(shù)會(huì)將所有傳遞進(jìn)來(lái)的參數(shù)值相加,此代碼通過(guò)將 sum() 函數(shù)封裝在 sumProxy() 代理中,如果傳入?yún)?shù)的值不是數(shù)值類(lèi)型,該函數(shù)仍然會(huì)嘗試加法操作,但在函數(shù)運(yùn)行之前攔截了函數(shù)調(diào)用,觸發(fā)apply陷阱函數(shù)以保證每個(gè)參數(shù)都是數(shù)值。

出于安全的考慮,這段代碼使用 construct 陷阱拋出錯(cuò)誤,以確保該函數(shù)不會(huì)被使用 new 運(yùn)算符調(diào)用

實(shí)例對(duì)象 instance 對(duì)象會(huì)被同時(shí)判定為 proxy 與 target 對(duì)象的實(shí)例,是因?yàn)?instanceof 運(yùn)算符使用了原型鏈來(lái)進(jìn)行推斷,而原型鏈查找并沒(méi)有受到這個(gè)代理的影響,因此 proxy 對(duì)象與 target 對(duì)象對(duì)于 JS 引擎來(lái)說(shuō)就有同一個(gè)原型。
4.3 調(diào)用類(lèi)的構(gòu)造函數(shù)

ES6 中新引入了class類(lèi)的概念,類(lèi)使用constructor構(gòu)造函數(shù)封裝數(shù)據(jù),并規(guī)定必須始終使用 new 來(lái)調(diào)用,原因是類(lèi)構(gòu)造器的內(nèi)部方法 [[Call]] 被明
確要求拋出錯(cuò)誤。

代理可以攔截對(duì)于 [[Call]] 方法的調(diào)用,你可以借助代理調(diào)用的類(lèi)構(gòu)造器。例如在缺少 new 的情況下創(chuàng)建一個(gè)新實(shí)例,就使用 apply 陷阱函數(shù)實(shí)現(xiàn):

class Person {
  constructor(name) {
    this.name = name;
  }
}
let PersonProxy = new Proxy(Person, {
  apply: function(trapTarget, thisArg, argumentList) {
    return new trapTarget(...argumentList);
  }
});
let me = PersonProxy("Jenny");
console.log(me.name); // "Jenny"
console.log(me instanceof Person); // true
console.log(me instanceof PersonProxy); // true

類(lèi)構(gòu)造器即類(lèi)的構(gòu)造函數(shù),使用代理時(shí)它的行為就像函數(shù)一樣,apply陷阱函數(shù)重寫(xiě)了默認(rèn)的構(gòu)造行為。

關(guān)于類(lèi)的更多有趣的用法,可參考 【ES6】更易于繼承的類(lèi)語(yǔ)法

總結(jié)來(lái)說(shuō),代理的用途非常廣泛,因?yàn)樗峁┝诵薷?JS 內(nèi)置對(duì)象的所有行為的入口。上述例子只是簡(jiǎn)單的一些應(yīng)用入門(mén),還有更多復(fù)雜的示例,推薦閱讀《深入理解ES6》。

繼續(xù)加油鴨少年?。?!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/101528.html

相關(guān)文章

  • 深入理解ES6筆記(十一)代理(Proxy)和反射(Reflection)API(12)

    摘要:是陷阱函數(shù)對(duì)應(yīng)的反射方法,同時(shí)也是操作的默認(rèn)行為。對(duì)象外形指的是對(duì)象已有的屬性與方法的集合,由于該屬性驗(yàn)證只須在讀取屬性時(shí)被觸發(fā),因此只要使用陷阱函數(shù)。無(wú)論該屬性是對(duì)象自身的屬性還是其原型的屬性。 主要知識(shí)點(diǎn):代理和反射的定義、常用的陷阱函數(shù)、可被撤銷(xiāo)的代理、將代理對(duì)象作為原型使用、將代理作為類(lèi)的原型showImg(https://segmentfault.com/img/bVbfWr...

    explorer_ddf 評(píng)論0 收藏0
  • 《深入理解ES6》筆記——代理(Proxy)和反射(Reflection)API(12)

    摘要:方法與代理處理程序的方法相同。使用給目標(biāo)函數(shù)傳入指定的參數(shù)。當(dāng)然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入?yún)?shù),將返回值賦值給,這樣我們就能在需要讀取這個(gè)返回值的時(shí)候調(diào)用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當(dāng)你見(jiàn)到一個(gè)新的API,不明白的時(shí)候,就在瀏覽器打印出來(lái)看看它的樣子。 showImg(https://segmentfault....

    ZHAO_ 評(píng)論0 收藏0
  • 《深入理解ES6》筆記——代理(Proxy)和反射(Reflection)API(12)

    摘要:方法與代理處理程序的方法相同。使用給目標(biāo)函數(shù)傳入指定的參數(shù)。當(dāng)然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入?yún)?shù),將返回值賦值給,這樣我們就能在需要讀取這個(gè)返回值的時(shí)候調(diào)用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當(dāng)你見(jiàn)到一個(gè)新的API,不明白的時(shí)候,就在瀏覽器打印出來(lái)看看它的樣子。 showImg(https://segmentfault....

    shiina 評(píng)論0 收藏0
  • symbol

    摘要:元編程另一個(gè)方面是反射其用于發(fā)現(xiàn)和調(diào)整你的應(yīng)用程序結(jié)構(gòu)和語(yǔ)義。下的元編程帶來(lái)了三個(gè)全新的以及。它是語(yǔ)言的第七種數(shù)據(jù)類(lèi)型,前六種是布爾值字符串?dāng)?shù)值對(duì)象。等同于對(duì)象的屬性等于一個(gè)布爾值,表示該對(duì)象用于時(shí),是否可以展開(kāi)。 元編程 一系列優(yōu)秀的 ES6 的新特性都來(lái)自于新的元編程工具,這些工具將底層鉤子(hooks)注入到了代碼機(jī)制中。元編程(籠統(tǒng)地說(shuō))是所有關(guān)于一門(mén)語(yǔ)言的底層機(jī)制,而不是數(shù)據(jù)...

    jubincn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<