摘要:這種構(gòu)造函數(shù)與原型混合模式,是目前在中使用最廣泛認(rèn)同度最高的一種創(chuàng)建自定義類(lèi)型的方法。
JavaScript 創(chuàng)建對(duì)象
原文鏈接
亂七八糟的概念總是阻礙我們對(duì)知識(shí)更進(jìn)一步的理解,所以我們先來(lái)搞清楚幾個(gè)概念之間的關(guān)系。
創(chuàng)建一個(gè)對(duì)象在 JavaScript 中,引用類(lèi)型的值被稱(chēng)為對(duì)象(或?qū)嵗?/strong>
強(qiáng)調(diào):對(duì)象、實(shí)例、實(shí)例對(duì)象、對(duì)象實(shí)例 等意。
實(shí)例 百度百科
What is the difference between an Instance and an Object?
沒(méi)對(duì)象怎么辦?找一個(gè)唄,額,是創(chuàng)建一個(gè)。
初學(xué)者最常見(jiàn)到的就是使用這兩種方法來(lái)創(chuàng)建單個(gè)對(duì)象:1. 使用 Object 構(gòu)造函數(shù)創(chuàng)建,2. 使用對(duì)象字面量直接創(chuàng)建
其實(shí)還可以用以下的方法創(chuàng)建一個(gè)對(duì)象:
通過(guò)構(gòu)造函數(shù)來(lái)創(chuàng)建特定類(lèi)型的對(duì)象(見(jiàn)后文構(gòu)造函數(shù)模式)
通過(guò)原型創(chuàng)建對(duì)象(見(jiàn)后文原型模式)
通過(guò) Object.create() 方法創(chuàng)建【MDN】
// 方法 1 var obj1 = new Object(); // 創(chuàng)建空對(duì)象 obj1.name = "percy"; // 為對(duì)象添加屬性 obj1.getName = function(){ // 為對(duì)象添加方法 return this.name; }; // 方法 2 var obj2 = { name: "percy", getName: function(){ return this.name; } };
使用這兩種方式創(chuàng)建對(duì)象有個(gè)明顯的缺點(diǎn):即只創(chuàng)建了一個(gè)特定的對(duì)象,不便于創(chuàng)建多個(gè)擁有相同屬性和方法的不同對(duì)象。為了解決這個(gè)問(wèn)題,人們便開(kāi)始使用工廠(chǎng)模式。
工廠(chǎng)模式(The Factory Pattern)
擴(kuò)展鏈接:
What is the difference between new Object() and object literal notation?
[Why use {} instead of new Object() and use [] instead of new Array() and true/false instead of new Boolean()?](http://stackoverflow.com/ques...
優(yōu)點(diǎn):解決了創(chuàng)建多個(gè)相似對(duì)象的問(wèn)題
缺點(diǎn):無(wú)法判斷工廠(chǎng)模式創(chuàng)建的對(duì)象的具體類(lèi)型,因?yàn)樗鼊?chuàng)建的對(duì)象都是 Object 整出來(lái)的
工廠(chǎng)模式抽象了創(chuàng)建具體對(duì)象的過(guò)程
由于 ES6 之前,ECMAScript 沒(méi)有類(lèi)(class)這個(gè)概念,所以開(kāi)發(fā)人員用函數(shù)封裝了以特定接口創(chuàng)建對(duì)象的細(xì)節(jié)。
ES6 中引入了類(lèi)(class)這個(gè)概念,作為對(duì)象的模板。【傳送門(mén)】
舉例如下:
function Person(name,age,job){ var obj = new Object(); obj.name = name; obj.age = age; obj.job = job; obj.getName = function(){ return this.name; }; return obj; } var person1 = Person("percy",21,"killer"); var person2 = Person("zyj",20,"queen"); console.log(person1); // Object {name: "percy", age: 21, job: "killer"} console.log(person2); // Object {name: "zyj", age: 20, job: "queen"} console.log(person1.constructor); // function Object() { [native code] } console.log(person1.constructor); // function Object() { [native code] } console.log(person1 instanceof Object); // true console.log(person1 instanceof Person); // false構(gòu)造函數(shù)模式(The Constructor Pattern)
優(yōu)點(diǎn):它可以將它創(chuàng)建的對(duì)象標(biāo)識(shí)為一種特定的類(lèi)型
缺點(diǎn):不同實(shí)例無(wú)法共享相同的屬性或方法
constructor 屬性始終指向創(chuàng)建當(dāng)前對(duì)象的構(gòu)造(初始化)函數(shù)
使用構(gòu)造函數(shù)模式將前面的例子進(jìn)行重寫(xiě)如下:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = function(){ return this.name; } } var person1 = new Person("percy",21,"killer"); var person2 = new Person("zyj",20,"queen"); console.log(person1); // Object {name: "percy", age: 21, job: "killer"} console.log(person2); // Object {name: "zyj", age: 20, job: "queen"} console.log(person1.constructor); // function Person() { ... } console.log(person1.constructor); // function Person() { ... } console.log(person1 instanceof Object); // true console.log(person1 instanceof Person); // true
要?jiǎng)?chuàng)建 Person 的新實(shí)例,必須使用 new 操作符。以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷以下 4 個(gè)步驟:
1.創(chuàng)建一個(gè)新對(duì)象(新實(shí)例) 2.將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此 this 就指向了這個(gè)對(duì)象) 3.執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性和方法) 4.返回新對(duì)象
任何函數(shù),只要通過(guò) new 操作符來(lái)調(diào)用,那么它就可以作為構(gòu)造函數(shù),否則就和普通函數(shù)沒(méi)什么兩樣
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = function(){ return this.name; } } Person("percy",22,"無(wú)"); window.getName(); // percy
從這一小節(jié)最開(kāi)始的代碼中,你可能注意到了,person1 和 person2 這兩個(gè)對(duì)象擁有相同的方法,但是它們相等嗎?
person1.getName === person2.getName // false
調(diào)用同一個(gè)方法,卻聲明了不同的對(duì)象,實(shí)在是浪費(fèi)資源,所以就引進(jìn)了接下來(lái)的主角:原型模式。
原型模式(The Prototype Pattern)優(yōu)點(diǎn):它實(shí)現(xiàn)了不同實(shí)例可以共享屬性或方法
缺點(diǎn):它省略了構(gòu)造函數(shù)初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實(shí)例在默認(rèn)情況下都取得了相同的屬性值。并且如果如果原型對(duì)象中有屬性的值為引用類(lèi)型的,要是實(shí)例重寫(xiě)了這個(gè)屬性,那么所有實(shí)例都會(huì)使用這個(gè)重寫(xiě)的屬性。
我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè) prototype(原型) 屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)原型對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類(lèi)型的所有實(shí)例共享的屬性和方法。
上面的特定類(lèi)型可以是通過(guò) new Person() 形成的 Person 類(lèi)型。
好,把上面的例子改寫(xiě)成原型模式:
function Person(){ } Person.prototype.name = "percy"; Person.prototype.age = 21; Person.prototype.job = "killer"; Person.prototype.getName = function(){ return this.name; }; var person1 = new Person(); var person2 = new Person(); console.log(person1.name); // percy console.log(person2.name); // percy console.log(person1.getName === person2.getName); // true
構(gòu)造函數(shù)的 prototype 屬性指向它的原型對(duì)象
所有原型對(duì)象都具備一個(gè) constructor 屬性,這個(gè)屬性指向包含 prototype 屬性的函數(shù)
[[Prototype]] 是實(shí)例指向構(gòu)造函數(shù)的原型對(duì)象的指針,目前不是標(biāo)準(zhǔn)的屬性,但 Firefox、Safari 和 Chrome 在每個(gè)對(duì)象上都支持一個(gè) __proto__ 屬性,用來(lái)實(shí)現(xiàn) [[Prototype]]。
ECMAScript 5 增加的新方法:Object.getPrototypeOf(),它可以返回 [[Prototype]] 的值,即返回實(shí)例對(duì)象的原型。
Person.prototype.constructor === Person; // true person1.constructor === Person; // true Object.getPrototypeOf(person1) === Person.prototype; // true
當(dāng)我們?cè)L問(wèn)一個(gè)對(duì)象中的屬性時(shí),首先會(huì)詢(xún)問(wèn)實(shí)例對(duì)象中有沒(méi)有該屬性,如果沒(méi)有則繼續(xù)查找其原型對(duì)象有沒(méi)有該屬性。所以要是實(shí)例對(duì)象中定義了與原型對(duì)象中相同名字的屬性,則優(yōu)先調(diào)用實(shí)例對(duì)象中的屬性。
var p1 = new Person(); var p2 = new Person(); p1.name = "zyj"; console.log(p1.name); // zyj console.log(p2.name); // percy
Object.prototype.hasOwnProperty(prop):檢測(cè)一個(gè)屬性是存在于對(duì)象實(shí)例中,還是存在于原型中,若存在于實(shí)例中,則返回 true,否則返回 false。
var p1 = new Person(); var p2 = new Person(); p1.name = "zyj"; console.log(p1.hasOwnProperty("name")); // true console.log(p2.hasOwnProperty("name")); // false
in 操作符(prop in objectName ):判斷對(duì)象實(shí)例是否能夠訪(fǎng)問(wèn)某個(gè)屬性(無(wú)論這個(gè)屬性是自己的還是在原型對(duì)象上的),若能訪(fǎng)問(wèn)則返回 true,否則返回 false。
var p1 = new Person(); var p2 = new Person(); p1.name = "zyj"; console.log("name" in p1); // true console.log("name" in p2); // true
Object.keys(obj):返回對(duì)象上所有可枚舉的實(shí)例屬性
Object.getOwnPropertyNames(obj):返回對(duì)象上的所有實(shí)例屬性(不管能不能枚舉)
var p1 = new Person(); var p2 = new Person(); p1.name = "zyj"; p1.age = 22; Object.defineProperty(p1,"age",{ enumerable: false }); // 將 age 設(shè)置為不可枚舉 console.log(Object.keys(p1)); // ["name"] console.log(Object.keys(p2)); // [] console.log(Object.getOwnPropertyNames(p1)); // ["name","age"] console.log(Object.getOwnPropertyNames(p2)); // [] console.log(Object.keys(Person.prototype)); // ["name", "age", "job", "getName"] console.log(Object.getOwnPropertyNames(Person.prototype)); // ["constructor", "name", "age", "job", "getName"]更簡(jiǎn)潔的原型語(yǔ)法
也許你已經(jīng)注意到了,這一節(jié)最前面的原型寫(xiě)法是不是有點(diǎn)啰嗦,為什么每次都要寫(xiě)一遍 Person.prototype 呢?好,那我們現(xiàn)在用更簡(jiǎn)潔的原型語(yǔ)法如下:
function Person(){ } Person.prototype = { name: "percy", age: 21, job: "killer", getName: function(){ return this.name; } };
是不是簡(jiǎn)潔了許多?但是這里也出現(xiàn)了一個(gè)問(wèn)題,constructor 屬性不再指向 Person了,而是指向了 Object 構(gòu)造函數(shù)。記得我們?cè)谏厦嫣岬搅?Person.prototype 指向的是一個(gè)對(duì)象(原型對(duì)象),而現(xiàn)在我們完全重寫(xiě)了這個(gè)原型對(duì)象,所以這個(gè)原型對(duì)象的 constructor 指向了最廣泛的 Object。
var p3 = new Person(); console.log(p3 instanceof Person); // true console.log(p3 instanceof Object); // true console.log(Person.prototype.constructor === Person); // false console.log(Person.prototype.constructor === Object); // true
所以改寫(xiě)上面的代碼,使 constructor 指向 Person:
function Person(){ } Person.prototype = { constructor: Person, name: "percy", age: 21, job: "killer", getName: function(){ return this.name; } };
注意,以這種方式重設(shè)constructor 屬性會(huì)導(dǎo)致它的 [[Enumerable]] 特性被設(shè)置為 false,從而 constructor 屬性變得可以枚舉了,但是原生的 constructor 屬性是不可枚舉的,所以我們利用 Object.defineProperty() 再改寫(xiě)一下代碼:
function Person(){ } Person.prototype = { name: "percy", age: 21, job: "killer", getName: function(){ return this.name; } }; Object.defineProperty(Person.prototype,"constructor",{ enumerable: false, value: Person });
var p3 = new Person(); console.log(p3 instanceof Person); // true console.log(p3 instanceof Object); // true console.log(Person.prototype.constructor === Person); // true console.log(Person.prototype.constructor === Object); // false
重寫(xiě)原型對(duì)象應(yīng)該在創(chuàng)建實(shí)例之前完成,否則會(huì)出現(xiàn)不可預(yù)知的錯(cuò)誤
function Person(){ } var p3 = new Person(); Person.prototype = { name: "percy", age: 21, job: "killer", getName: function(){ return this.name; } }; Object.defineProperty(Person.prototype,"constructor",{ enumerable: false, value: Person }); p3.getName(); // 報(bào)錯(cuò),TypeError: p3.getName is not a function(…)
當(dāng)原型對(duì)象中有屬性的值為引用類(lèi)型時(shí)...
function Person(){ } Person.prototype = { name: "percy", age: 21, job: "killer", friends: ["zyj","Shelly","Dj Aligator"], // 添加 getName: function(){ return this.name; } }; Object.defineProperty(Person.prototype,"constructor",{ enumerable: false, value: Person }); var p1 = new Person(); var p2 = new Person(); p1.job = "programmer"; p1.friends.push("Mary","Iris"); console.log(p1.job); // programmer console.log(p2.job); // killer console.log(p1.friends); // ["zyj", "Shelly", "Dj Aligator", "Mary", "Iris"] console.log(p2.friends); // ["zyj", "Shelly", "Dj Aligator", "Mary", "Iris"] console.log(p1.friends === p2.friends); // true console.log(Person.prototype.friends); // ["zyj", "Shelly", "Dj Aligator", "Mary", "Iris"]
看出問(wèn)題來(lái)了嗎?當(dāng)原型對(duì)象中有屬性的值為引用類(lèi)型時(shí),要是一個(gè)實(shí)例重寫(xiě)了這個(gè)屬性,那么所有的實(shí)例都會(huì)使用這個(gè)重寫(xiě)后的屬性。要是還不了解的話(huà),可以看看我以前的文章,談的是基本類(lèi)型和引用類(lèi)型在內(nèi)存中的存儲(chǔ)方式,以及改變它們的值時(shí),內(nèi)存中是如何變化的。
組合使用構(gòu)造函數(shù)模式和原型模式(Combination Constructor/Prototype Pattern)原理:構(gòu)造函數(shù)模式用于實(shí)例自己的屬性,而原型模式用于定義方法和需要共享的屬性。結(jié)果,每個(gè)實(shí)例都會(huì)有自己的一份實(shí)例屬性的副本,但同時(shí)又共享著對(duì)方法的引用,最大限度地節(jié)省了內(nèi)存。
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.friends = ["zyj"]; } Person.prototype = { getName: function(){ return this.name; } }; Object.defineProperty(Person.prototype,"constructor",{ enumerable: false, value: Person }); var person1 = new Person("percy","21","killer"); var person2 = new Person("Bob","26","developer"); person1.friends.push("Iris","Alice"); console.log(person1.name); // percy console.log(person2.name); // Bob console.log(person1.friends); // ["zyj", "Iris", "Alice"] console.log(person2.friends); // ["zyj"] console.log(person1.friends === person2.friends); // false console.log(person1.getName === person2.getName); // true
這種構(gòu)造函數(shù)與原型混合模式,是目前在 ECMAScript 中使用最廣泛、認(rèn)同度最高的一種創(chuàng)建自定義類(lèi)型的方法。
為上面的代碼補(bǔ)一張圖吧 :)!
動(dòng)態(tài)原型模式(Dynamic Prototype Pattern)原理:將所有信息封裝到構(gòu)造函數(shù)中。
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.friends = ["zyj"]; if(typeof this.getName != "function" ){ Person.prototype.getName = function(){ return this.name; }; Person.prototype.getJob = function(){ return this.job; }; } } var person = new Person("percy",21,"programmer"); console.log(person.getName()); // percy console.log(person.getJob()); // programmer
將所有信息封裝到構(gòu)造函數(shù)里,很完美,有木有?
這里使用 if 語(yǔ)句檢查原型方法是否已經(jīng)初始化,從而防止多次初始化原型方法。
這種模式下,不能使用對(duì)象自面量重寫(xiě)原型對(duì)象。因?yàn)樵谝呀?jīng)創(chuàng)建了實(shí)例的情況下再重寫(xiě)原型對(duì)象的話(huà),會(huì)切斷現(xiàn)有實(shí)例與新原型對(duì)象之間的聯(lián)系。
看這里,有更詳細(xì)的對(duì)上面代碼的解釋?zhuān)?strong>鏈接
寄生構(gòu)造函數(shù)模式(Parasitic Constructor Pattern)似曾相識(shí)哈!
一句話(huà)闡明:除了使用 new 操作符并把包裝函數(shù)叫做構(gòu)造函數(shù)之外,這個(gè)模式跟工廠(chǎng)模式其實(shí)是一模一樣的。
function Person(name,age,job){ var obj = new Object(); obj.name = name; obj.age = age; obj.job = job; obj.getName = function(){ return this.name; }; return obj; } var person1 = new Person("percy",21,"killer"); var person2 = new Person("zyj",20,"queen"); person1.getName(); // percy person2.getName(); // zyj
建議在可以使用其他模式的情況下,不要使用這種模式。
穩(wěn)妥構(gòu)造函數(shù)模式(Durable Constructor Pattern)
穩(wěn)妥構(gòu)造函數(shù)遵循與寄生構(gòu)造函數(shù)類(lèi)似的模式,但是有 2 點(diǎn)不同:
一是新創(chuàng)建對(duì)象的實(shí)例方法不引用 this
二是不使用 new 操作符調(diào)用構(gòu)造函數(shù)
function Person(name,age,job){ var obj = new Object(); // 可以在這里定義私有變量和函數(shù) obj.getName = function(){ return name; }; return obj; } var person1 = new Person("percy",21,"killer"); var person2 = new Person("zyj",20,"queen"); person1.getName(); // percy person2.getName(); // zyj
注意,在這種模式下創(chuàng)建的對(duì)象中,除過(guò)調(diào)用 getName() 方法外,沒(méi)有其他方法訪(fǎng)問(wèn) name 的值。
我想問(wèn)個(gè)問(wèn)題,最后的這個(gè)模式可以用在哪些地方呢?希望有經(jīng)驗(yàn)的朋友解答一下。
參考資料【書(shū)】《JavaScript 高級(jí)程序設(shè)計(jì)(第三版)》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/80295.html
摘要:對(duì)象在中,除了數(shù)字字符串布爾值這幾個(gè)簡(jiǎn)單類(lèi)型外,其他的都是對(duì)象。那么在函數(shù)對(duì)象中,這兩個(gè)屬性的有什么區(qū)別呢表示該函數(shù)對(duì)象的原型表示使用來(lái)執(zhí)行該函數(shù)時(shí)這種函數(shù)一般成為構(gòu)造函數(shù),后面會(huì)講解,新創(chuàng)建的對(duì)象的原型。這時(shí)的函數(shù)通常稱(chēng)為構(gòu)造函數(shù)。。 本文原發(fā)于我的個(gè)人博客,經(jīng)多次修改后發(fā)到sf上。本文仍在不斷修改中,最新版請(qǐng)?jiān)L問(wèn)個(gè)人博客。 最近工作一直在用nodejs做開(kāi)發(fā),有了nodejs,...
摘要:注意句柄棧并不是調(diào)用棧中的一部分,但句柄域卻在棧中。一個(gè)依賴(lài)于構(gòu)造函數(shù)和析構(gòu)函數(shù)來(lái)管理下層對(duì)象的生命周期。對(duì)象模板用來(lái)配置將這個(gè)函數(shù)作為構(gòu)造函數(shù)而創(chuàng)建的對(duì)象。 如果你已經(jīng)閱讀過(guò)了上手指南,那么你已經(jīng)知道了如何作為一個(gè)單獨(dú)的虛擬機(jī)使用 V8 ,并且熟悉了一些 V8 中的關(guān)鍵概念,如句柄,域 和上下文。在本文檔中,還將繼續(xù)深入討論這些概念并且介紹其他一些在你的 C++ 應(yīng)用中使用 V8 的...
摘要:對(duì)象的分類(lèi)內(nèi)置對(duì)象原生對(duì)象就是語(yǔ)言預(yù)定義的對(duì)象,在標(biāo)準(zhǔn)定義,有解釋器引擎提供具體實(shí)現(xiàn)宿主對(duì)象指的是運(yùn)行環(huán)境提供的對(duì)象。不過(guò)類(lèi)型是中所有類(lèi)型的父級(jí)所有類(lèi)型的對(duì)象都可以使用的屬性和方法,可以通過(guò)的構(gòu)造函數(shù)來(lái)創(chuàng)建自定義對(duì)象。 對(duì)象 javaScript中的對(duì)象,和其它編程語(yǔ)言中的對(duì)象一樣,可以比照現(xiàn)實(shí)生活中的對(duì)象來(lái)理解。在JavaScript中,一個(gè)對(duì)象可以是一個(gè)單獨(dú)擁有屬性和類(lèi)型的實(shí)體。和...
摘要:在最開(kāi)始的時(shí)候,原型對(duì)象的設(shè)計(jì)主要是為了獲取對(duì)象的構(gòu)造函數(shù)。同理數(shù)組通過(guò)調(diào)用函數(shù)通過(guò)調(diào)用原型鏈中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)繼承的主要方法。 對(duì)象的創(chuàng)建 在JavaScript中創(chuàng)建一個(gè)對(duì)象有三種方式。可以通過(guò)對(duì)象直接量、關(guān)鍵字new和Object.create()函數(shù)來(lái)創(chuàng)建對(duì)象。 1. 對(duì)象直接量 創(chuàng)建對(duì)象最直接的方式就是在JavaScript代碼中使用對(duì)象直接量。在ES5...
摘要:當(dāng)談到語(yǔ)言與其他編程語(yǔ)言相比時(shí),你可能會(huì)聽(tīng)到一些令人困惑東西,其中之一是工廠(chǎng)函數(shù)和構(gòu)造函數(shù)。好的,讓我們用構(gòu)造函數(shù)做同樣的實(shí)驗(yàn)。當(dāng)我們使用工廠(chǎng)函數(shù)創(chuàng)建對(duì)象時(shí),它的指向,而當(dāng)從構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),它指向它的構(gòu)造函數(shù)原型對(duì)象。 showImg(https://segmentfault.com/img/bVbr58T?w=1600&h=900); 當(dāng)談到JavaScript語(yǔ)言與其他編程語(yǔ)言...
摘要:在中函數(shù)是一等對(duì)象,它們不被聲明為任何東西的一部分,而所引用的對(duì)象稱(chēng)為函數(shù)上下文并不是由聲明函數(shù)的方式?jīng)Q定的,而是由調(diào)用函數(shù)的方式?jīng)Q定的。更為準(zhǔn)確的表述應(yīng)該為當(dāng)對(duì)象充當(dāng)函數(shù)的調(diào)用函數(shù)上下文時(shí),函數(shù)就充當(dāng)了對(duì)象的方法。 引言:當(dāng)理解了對(duì)象和函數(shù)的基本概念,你可能會(huì)發(fā)現(xiàn),在JavaScript中有很多原以為理所當(dāng)然(或盲目接受)的事情開(kāi)始變得更有意義了。 1.JavaScript...
閱讀 3018·2021-10-27 14:15
閱讀 3011·2021-09-07 10:18
閱讀 1328·2019-08-30 15:53
閱讀 1581·2019-08-26 18:18
閱讀 3382·2019-08-26 12:15
閱讀 3467·2019-08-26 10:43
閱讀 659·2019-08-23 16:43
閱讀 2216·2019-08-23 15:27