成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js知識(shí)梳理3:創(chuàng)建對象的模式探究

MrZONT / 443人閱讀

摘要:起因構(gòu)造函數(shù)對象字面量都可以用來創(chuàng)建單個(gè)對象,但有明顯缺點(diǎn)使用同一個(gè)接口創(chuàng)建很多對象,會(huì)產(chǎn)生大量的重復(fù)代碼。組合使用構(gòu)造函數(shù)模式和原型模式創(chuàng)建自定義類型的最常見方式,就是組合使用構(gòu)造函數(shù)模式與原型模式。

寫在前面

注:這個(gè)系列是本人對js知識(shí)的一些梳理,其中不少內(nèi)容來自書籍:Javascript高級程序設(shè)計(jì)第三版和JavaScript權(quán)威指南第六版,感謝它們的作者和譯者。有發(fā)現(xiàn)什么問題的,歡迎留言指出。

起因

Object構(gòu)造函數(shù)、對象字面量、Object.creat都可以用來創(chuàng)建單個(gè)對象,但有明顯缺點(diǎn):使用同一個(gè)接口創(chuàng)建很多對象,會(huì)產(chǎn)生大量的重復(fù)代碼。所以才開始了創(chuàng)建對象的模式的探索。

檢測對象的類

3種常見的檢測任意對象的類的技術(shù):instanceof運(yùn)算符、constructor屬性、構(gòu)造函數(shù)的名字。3種各有優(yōu)劣,適用于不同場景。(但往往我們更關(guān)注對象可以完成什么工作,對象屬于哪個(gè)類并不是最重要的)

1.instanceof運(yùn)算符

運(yùn)算符左邊是對象,右邊是構(gòu)造函數(shù),如o instanceof f,如果在o的原型鏈中查找到f,就返回true:
var date = new Date();
console.log(date instanceof Date);//true
console.log(date instanceof Object);//true
console.log(date instanceof Number);//false

這種方式的缺點(diǎn):①無法通過對象來獲得類名,只能檢測對象是否屬于指定的類名,②如果是不同的執(zhí)行上下文,如客戶端中每個(gè)窗口和框架子頁面都具有多帶帶的執(zhí)行上下文,其中一個(gè)框架頁面中的數(shù)組不是另一個(gè)框架頁面的Array()構(gòu)造函數(shù)的實(shí)例。

2.constructor屬性

console.log(date.constructor == Date);//true

缺點(diǎn):①和instance的第2點(diǎn)缺點(diǎn)一樣,執(zhí)行上下文的問題,②并不是所有的對象都有constrctor屬性,如果創(chuàng)建對象時(shí)把對象的prototype直接覆蓋了而又沒有指定constrctor屬性,就會(huì)沒有這個(gè)屬性。

3.構(gòu)造函數(shù)的名稱

//返回對象的類
function classof(o) {
    return Object.prototype.toString.call(o).slice(8,-1);
}
//返回函數(shù)的名字(可能是空字符串),不是函數(shù)就返回null
Function.prototype.getName = function () {
    if("name" in this) return this.name;
    return this.name = this.toString().match(/functions*([^()]*)(/)[1];
}
function type(o) {
    //type,class,name
    var t,c,n;

    //處理null值特殊情況
    if(o === null) return "null";
    //處理NaN和它自身不相等
    if(o !== o) return "nan";
    //識(shí)別出原始值的類型
    if((t = typeof o) !== "object") return t;
    //識(shí)別出大多數(shù)的內(nèi)置對象(類名除了"Object")
    if((c=classof(o)) !== "Object") return c;
    //如果對象構(gòu)造函數(shù)的名字存在,就返回它
    if(o.constructor && typeof o.constructor === "function" && (n = o.constructor.getName()) ) return n;
    //其他的類型無法判別,返回"Object"
    return "Object";
}

這種方法的問題:①并不是所有的構(gòu)造函數(shù)都有constructor屬性,②并不是所有的函數(shù)都有名字(name是非標(biāo)準(zhǔn)屬性),如果使用不帶名字的函數(shù)定義表達(dá)式定義一個(gè)構(gòu)造函數(shù),getName()方法會(huì)返回空字符串。

//這種情況下如果沒有name屬性,那么getName()方法就返回空字符串了
var Example = function (x, y) {
    this.x = x;this.y = y;
}
1.工廠模式
function creatPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        console.log(this.name);
    };
    return o;
}
var person1 = creatPerson("jaychou",34,"singer");
var person2 = creatPerson("xiaoming",15,"student");
//{name: "jaychou", age: 34, job: "singer", sayName: ?}
console.log(person1);
//{name: "xiaoming", age: 15, job: "student", sayName: ?}
console.log(person2);

工廠模式的最大缺點(diǎn):沒有解決對象識(shí)別的問題,不知道一個(gè)對象的類型。

2.構(gòu)造函數(shù)模式

顯然構(gòu)造函數(shù)可用來創(chuàng)建特定類型的對象,如Array,Date等,重寫上面的例子:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        console.log(this.name);
    }
}
//{name: "jaychou", age: 34, job: "singer", sayName: ?}
var person1 = new Person("jaychou",34,"singer");
//{name: "xiaoming", age: 15, job: "student", sayName: ?}
var person2 = new Person("xiaoming",15,"student");

與之前對比:①?zèng)]有顯式地創(chuàng)建對象,②直接將屬性和方法賦給了 this 對象,③沒有 return 語句

使用new操作符調(diào)用Person構(gòu)造函數(shù)后:

創(chuàng)建一個(gè)新對象

將構(gòu)造函數(shù)的作用域賦給新對象(this指向了這個(gè)新對象)

執(zhí)行構(gòu)造函數(shù)中的代碼(為新對象添加屬性和方法)

返回新對象

類型的標(biāo)識(shí)有了,構(gòu)造函數(shù)模式的主要缺點(diǎn)是:每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍(函數(shù)也是對象的一種,導(dǎo)致重復(fù)創(chuàng)建對象了)

3.原型模式

①基本

function Person() {

}
Person.prototype.name = "jaychou";
Person.prototype.age = 34;
Person.prototype.job = "singer";
Person.prototype.sayName = function () {
    console.log(this.name);
}
var person1 = new Person();
person1.sayName();//jaychou
var person2 = new Person();
person2.sayName();//jaychou
console.log(person1.sayName == person2.sayName);//true

創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對象,而這個(gè)對象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。

//{name: "jaychou", age: 34, job: "singer", sayName: ?, constructor: ?}
console.log(Person.prototype);

我們打印了Person.prototype,默認(rèn)情況下會(huì)有一個(gè)constructor屬性,這個(gè)屬性指向函數(shù)(在這里就是指向構(gòu)造函數(shù)),其他的name,age,job,sayName屬性和方法都是通過Person.prototype.添加進(jìn)去的,所以由構(gòu)造函數(shù)Person創(chuàng)建的實(shí)例都會(huì)包含這些屬性和方法,它們是共享的。**

而且實(shí)例的內(nèi)部包含一個(gè)指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對象:[[Prototype]],在Firefox、Safari和Chrome中支持屬性__proto__:

//{name: "jaychou", age: 34, job: "singer", sayName: ?, constructor: ?}
console.log(person1.__proto__);
console.log(Person.prototype == person1.__proto__);//true

//打印對象的constructor:在這里person1的constructor就是Person
console.log(person1.constructor == Person);//true

另外,可以通過 isPrototypeOf 方法來確定對象之間是否存在原型關(guān)系:

console.log(Person.prototype.isPrototypeOf(person1));//true

還有,可以通過 Object.getPrototype 返回對象的原型:

//打印對象的原型:{name: "jaychou", age: 34, job: "singer", sayName: ?, constructor: ?}
console.log(Object.getPrototypeOf(person1));
console.log(Object.getPrototypeOf(person1).name);//jaychou

之前也有提及,查詢屬性和方法時(shí)先在當(dāng)前實(shí)例中找,沒有的話就到實(shí)例的原型鏈中找,而在實(shí)例中添加的屬性會(huì)屏蔽原型中的同名屬性。

②更簡單的原型語法

上一個(gè)例子中每增加一個(gè)屬性和方法就要敲一遍Person.prototype,比較麻煩,所以更簡單的做法是用一個(gè)包含所有屬性和方法的對象字面量來重寫整個(gè)原型對象:

Person.prototype = {
    name:"jaychou",
    age:34,
    job:"singer",
    sayName:function () {
        console.log(this.name);
    }
}
var person3 = new Person();
console.log(Person.prototype.constructor == Person);//false
console.log(Person.prototype.constructor == Object);//true
console.log(person3.constructor == Person);//false
console.log(person3.constructor == Object);//true

上面的代碼將 Person.prototype設(shè)置為等于一個(gè)以對象直接量形式創(chuàng)建的新對象,結(jié)果就導(dǎo)致了constructor 屬性不再指向 Person 了,因?yàn)楸举|(zhì)上完全重寫了默認(rèn)的prototype對象,因此constructor 屬性也就變成了新對象的constructor屬性(指向Object構(gòu)造函數(shù)),不再指向 Person函數(shù)。看下面的例子就更清楚了:

function Teacher(){

};
var tea1 = new Teacher();
Person.prototype = tea1;
//true:因?yàn)镻erson.prototype被重寫成tea1,tea1的constructor自然指向了Teacher構(gòu)造函數(shù)
console.log(Person.prototype.constructor == Teacher);

所以,如果constrctor的值很重要,可以在上面代碼的基礎(chǔ)上手動(dòng)加回去,最好仿照原生的把constructor屬性設(shè)置成不可枚舉的:

Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});
console.log(Person.prototype.constructor == Person);//true    

注意一個(gè)問題: 如果某個(gè)實(shí)例已經(jīng)被創(chuàng)建了之后,再直接重寫了構(gòu)造函數(shù)的原型對象,那么之前已被創(chuàng)建好的對象內(nèi)部的原型指針還是指向舊的原型,如果舊實(shí)例調(diào)用了新原型里面定義的方法,就會(huì)報(bào)錯(cuò)了。所以重寫函數(shù)的原型對象時(shí)要特別注意這個(gè)問題。

③原型對象模式的問題

以上的創(chuàng)建對象在原型里共享了所有的屬性和方法,對于方法還好,對于大多數(shù)情況下屬性共享帶來的問題就顯而易見了。

4.組合使用構(gòu)造函數(shù)模式和原型模式

創(chuàng)建自定義類型的最常見方式,就是組合使用構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享的屬性。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
Person.prototype = {
    constructor:Person,
    sayName:function () {
        console.log(this.name);
    }
}
var person1 = new Person("jaychou",34,"singer");
var person2 = new Person("xiaoming",15,"student");
person1.sayName();//jaychou
person2.sayName();//xiaoming
console.log(person1.sayName === person2.sayName);//true
5.動(dòng)態(tài)原型模式(最常用)

對上面的例子進(jìn)行視覺上的美化,希望把所有的內(nèi)容都放在構(gòu)造函數(shù)里面,可以通過檢查某個(gè)應(yīng)該存在的方法或?qū)傩允欠翊嬖?,來決定是否需要初始化原型:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;

    //添加共享的方法或?qū)傩?    if(typeof this.sayName != "function"){
        Person.prototype.sayName = function () {
            console.log(this.name);
        };
        Person.prototype.sayJob = function () {
            console.log(this.job);
        }
    }
}

注意: 使用動(dòng)態(tài)原型模式時(shí),不能使用對象直接量重寫原型,原因上面已經(jīng)解釋過了,重寫會(huì)切斷了舊實(shí)例和新原型之間的聯(lián)系。

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/108370.html

相關(guān)文章

  • js知識(shí)梳理4.繼承模式探究

    摘要:而且在超類型的原型中定義的方法,對子類型而言也是不可見的,結(jié)果所有類型都只能使用構(gòu)造函數(shù)模式。在主要考慮對象而不是自定義類型和構(gòu)造函數(shù)的情況下,這個(gè)模式也不錯(cuò)。 寫在前面 注:這個(gè)系列是本人對js知識(shí)的一些梳理,其中不少內(nèi)容來自書籍:Javascript高級程序設(shè)計(jì)第三版和JavaScript權(quán)威指南第六版,感謝它們的作者和譯者。有發(fā)現(xiàn)什么問題的,歡迎留言指出。 1.原型鏈 將原型鏈作...

    wenshi11019 評論0 收藏0
  • javasscript - 收藏集 - 掘金

    摘要:跨域請求詳解從繁至簡前端掘金什么是為什么要用是的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。異步編程入門道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 jsonp 跨域請求詳解——從繁至簡 - 前端 - 掘金什么是jsonp?為什么要用jsonp?JSONP(JSON with Padding)是JSON的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題...

    Rango 評論0 收藏0
  • 理解JavaScript核心知識(shí)點(diǎn):This

    摘要:關(guān)鍵字計(jì)算為當(dāng)前執(zhí)行上下文的屬性的值。毫無疑問它將指向了這個(gè)前置的對象。構(gòu)造函數(shù)也是同理。嚴(yán)格模式無論調(diào)用位置,只取顯式給定的上下文綁定的,通過方法傳入的第一參數(shù),否則是。其實(shí)并不屬于特殊規(guī)則,是由于各種事件監(jiān)聽定義方式本身造成的。 this 是 JavaScript 中非常重要且使用最廣的一個(gè)關(guān)鍵字,它的值指向了一個(gè)對象的引用。這個(gè)引用的結(jié)果非常容易引起開發(fā)者的誤判,所以必須對這個(gè)關(guān)...

    TerryCai 評論0 收藏0
  • JS核心知識(shí)點(diǎn)梳理——原型、繼承(上)

    摘要:同理,原型鏈也是實(shí)現(xiàn)繼承的主要方式的只是語法糖。原型對象也可能擁有原型,并從中繼承方法和屬性,一層一層以此類推。利用構(gòu)造函數(shù)小明張三張三小明缺點(diǎn)每次實(shí)例化都需要復(fù)制一遍函數(shù)到實(shí)例里面。寄生構(gòu)造函數(shù)模式只有被類出來的才能用。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 最近又攀登了一下JS三座大山中的第二...

    villainhr 評論0 收藏0
  • 夯實(shí)基礎(chǔ)-作用域與閉包

    摘要:作用域分類作用域共有兩種主要的工作模型。換句話說,作用域鏈?zhǔn)腔谡{(diào)用棧的,而不是代碼中的作用域嵌套。詞法作用域詞法作用域中,又可分為全局作用域,函數(shù)作用域和塊級作用域。 一篇鞏固基礎(chǔ)的文章,也可能是一系列的文章,梳理知識(shí)的遺漏點(diǎn),同時(shí)也探究很多理所當(dāng)然的事情背后的原理。 為什么探究基礎(chǔ)?因?yàn)槟悴蝗ッ嬖嚹憔筒恢阑A(chǔ)有多重要,或者是說當(dāng)你的工作經(jīng)歷沒有亮點(diǎn)的時(shí)候,基礎(chǔ)就是檢驗(yàn)?zāi)愫脡牡囊豁?xiàng)...

    daydream 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<