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

資訊專(zhuān)欄INFORMATION COLUMN

繼承&單體

HollisChuang / 501人閱讀

摘要:繼承在中繼承較為復(fù)雜比其它語(yǔ)言的繼承要復(fù)雜在大多數(shù)面向?qū)ο笳Z(yǔ)言中繼承一個(gè)類(lèi)只要使用一個(gè)關(guān)鍵字即可而要傳承公有成員的話需要使用靈活微妙的原型繼承或者標(biāo)準(zhǔn)的類(lèi)繼承本文第一部分將討論中創(chuàng)建子類(lèi)的各種技術(shù)以及它們的使用場(chǎng)合為什么需要繼承先看看繼承能

繼承
在 js 中繼承較為復(fù)雜,比其它語(yǔ)言的繼承要復(fù)雜.在大多數(shù)面向?qū)ο笳Z(yǔ)言中,繼承一個(gè)類(lèi)只要使用一個(gè)關(guān)鍵字即可,而 js 要傳承公有成員的話需要使用靈活微妙的原型繼承,或者標(biāo)準(zhǔn)的類(lèi)繼承.
本文第一部分將討論 js 中創(chuàng)建子類(lèi)的各種技術(shù)以及它們的使用場(chǎng)合.
為什么需要繼承

先看看繼承能帶來(lái)的好處.設(shè)計(jì)類(lèi)的時(shí)候,希望能減少重復(fù)性的代碼,盡量弱化對(duì)象間的耦合.使用繼承符合前一個(gè)原則.可以在現(xiàn)有類(lèi)的基礎(chǔ)上進(jìn)行設(shè)計(jì)并充分利用他們已經(jīng)具備的各種方法.
讓一個(gè)類(lèi)繼承另一個(gè)類(lèi)可能會(huì)導(dǎo)致二者產(chǎn)生強(qiáng)耦合,一個(gè)類(lèi)依賴(lài)于另一個(gè)類(lèi)的內(nèi)部實(shí)現(xiàn).接下來(lái)會(huì)講到如何避免.比如用摻元類(lèi)為其他類(lèi)提供方法...等等.

類(lèi)繼承

通過(guò)用函數(shù)來(lái)聲明類(lèi),用關(guān)鍵字 new 來(lái)創(chuàng)建實(shí)例,下面是一個(gè)簡(jiǎn)單的類(lèi)聲明:

// Class Person
function Person(name) {
    this.name = name;
}

Person.prototype.getName = function() {
    return this.name;
}

首先要做的事創(chuàng)建構(gòu)造函數(shù),名字就是類(lèi)名,首字母大寫(xiě).在構(gòu)造函數(shù)中,創(chuàng)建實(shí)例屬性要使用關(guān)鍵字 this.類(lèi)的方法則被添加到其 prototype 對(duì)象中.要?jiǎng)?chuàng)建該類(lèi)的實(shí)例,只需結(jié)合關(guān)鍵字 new 調(diào)用這個(gè)構(gòu)造函數(shù):

var reader = new Person("John Smith");
reader.getName();

然后你可以訪問(wèn)所有的實(shí)例屬性,也可以調(diào)用所有的實(shí)例方法.

原型鏈

創(chuàng)建繼承 Person 的類(lèi)要復(fù)雜一些:

// Class Author
function Author(name, books) {
    Person.call(this, name); // Call the superclass"s constructor in the scope of this.
    this.books = books; // Add an attribute to Author.
}

Author.prototype = new Person(); // Set up the person chain.
Author.prototype.constructor = Author; // Set the constructor attribute to Author.
Author.prototype.getBooks = function () { // Add to method to Author.
    return this.books;
}

讓一個(gè)類(lèi)繼承另一個(gè)類(lèi)需要用到許多行代碼(不像其他面向?qū)ο笳Z(yǔ)言只要一個(gè)關(guān)鍵字 extend 即可),首先要做的是創(chuàng)建一個(gè)構(gòu)造函數(shù),在構(gòu)造函數(shù)中,調(diào)用超類(lèi)的構(gòu)造函數(shù).并將 name 參數(shù)傳給他,在使用 new 運(yùn)算符時(shí),系統(tǒng)會(huì)為你做一些事,會(huì)創(chuàng)建一個(gè)空對(duì)象,然后調(diào)用構(gòu)造函數(shù),在此過(guò)程中這個(gè)空對(duì)象處于作用域鏈的最前端.
下一步是設(shè)置原型鏈,js 沒(méi)有 extend 關(guān)鍵字,但是每個(gè) js 對(duì)象中都有一個(gè)名為 prototype 的屬性,要么指向另一個(gè)對(duì)象,要么 Null.在訪問(wèn)對(duì)象的某個(gè)成員時(shí)(比如reader.getName),如果這個(gè)成員未見(jiàn)于當(dāng)前對(duì)象,那么 js 會(huì)在prototype屬性所指的對(duì)象中查找他,沒(méi)找到j(luò)s就會(huì)沿著原型鏈向上逐一訪問(wèn)每個(gè)原型對(duì)象,直到找到他(或者已經(jīng)查找過(guò)原型鏈最頂端的 Object.prototype 對(duì)象).
所以說(shuō)為了讓一個(gè)類(lèi)繼承另一個(gè)類(lèi),只需將子類(lèi)的 prototype 設(shè)置為指向超類(lèi)的一個(gè)實(shí)例即可.
為了讓Author 繼承 Person,必須手動(dòng)地將 Author 的 prototype 設(shè)置為 Person 的一個(gè)實(shí)例,最后一步是將 prototype 的 constructor 屬性重設(shè)為 Author(因?yàn)榘?prototype 屬性設(shè)置為 Person 的實(shí)例時(shí),其 constructor 屬性被抹掉了).
盡管本例中為實(shí)現(xiàn)繼承需要額外使用三行代碼,但是創(chuàng)建這個(gè)新的子類(lèi)的的實(shí)例與創(chuàng)建 Person 的實(shí)例沒(méi)有什么不同:

var author = [];
author[0] = new Author("Dustin Diaz", ["Javascript Design Patterns"]);
author[0] = new Author("Ross Harmes", ["Javascript Design Patterns"]);

author[1].getName();
author[1].getBooks();

所以說(shuō),類(lèi)式繼承的復(fù)雜性只局限于類(lèi)的聲明,創(chuàng)建新實(shí)例的過(guò)程仍然很簡(jiǎn)單.

extend 函數(shù)

為了簡(jiǎn)化類(lèi)的聲明,可以把派生子類(lèi)的整個(gè)過(guò)程包裝在一個(gè)名為 extend 的函數(shù)中,他的作用和其他語(yǔ)言的 extend 關(guān)鍵字類(lèi)似,即基于一個(gè)給定的類(lèi)結(jié)構(gòu)創(chuàng)建一個(gè)新類(lèi):

// Extend Function.

function extend(subClass, superClass) {
    var F = function () {};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructot = subClass;
}

這個(gè) extend 函數(shù)和先前我們做的一樣,它設(shè)置了 prototype.然后再重新設(shè)置其 constructor 為其本身.有一項(xiàng)改進(jìn),他添加了空函數(shù) F,并將用它創(chuàng)建的一個(gè)對(duì)象實(shí)例插入原型鏈中(這樣可以避免創(chuàng)建超類(lèi)的新實(shí)例).
使用了 extend 函數(shù)后:

// Class Person.
function Person(name) {
    this.name = name;
}

Person.prototype.getName = function () {
    return this.name;
}

// Class Author.
function Author(name, books) {
    Person.call(this.name);
    this.books = books;
}

extend(Author, Person);

Author.prototype.getBooks = getBooks() {
    return this.books;
}

上面的代碼不像之前那樣手動(dòng)地設(shè)置prototype 和 constructor 屬性,而是通過(guò)在類(lèi)聲明之后(在向 prototype 添加任何方法之前)立即調(diào)用 extend 函數(shù).唯一的問(wèn)題是超類(lèi)(Person)的名稱(chēng)被固化在 Author 類(lèi)的聲明之中,更好的做法是下面那樣:

// Extend function, improved.

function extend(subClass, superClass) {
    var F = function() {};
    subClass.prototype = new F();
    subClass.superclass = superclass.prototype;
    if (superClass.prototype.constructor == Object.prototype.constructor) {
        superClass.prototype.constructor = superClass;
    }
}

說(shuō)到這個(gè) 改進(jìn)版的extend函數(shù),我想起了以前的一個(gè)東東,說(shuō)要實(shí)現(xiàn)一個(gè)js類(lèi)繼承工具方法:

繼承

多態(tài)
現(xiàn)在想想,此處的 extend 函數(shù)已經(jīng)實(shí)現(xiàn)了第一步,還差第二步,暫不討論.

該版本要長(zhǎng)一點(diǎn),但是提供了superclass 屬性,這個(gè)屬性用來(lái)弱化 Author 和 Person 之間的耦合.該函數(shù)的最后3行代碼用來(lái)確保超類(lèi)的 constructor 屬性已被正確設(shè)置(即時(shí)超類(lèi)就是 Object 類(lèi)本身),在用這個(gè)新的superclass 屬性調(diào)用超類(lèi)的構(gòu)造函數(shù)時(shí)這個(gè)問(wèn)題很重要:

// Class Author.
function Author(name, books) {
    Author.superclass.constructor.call(this, name);
    this.books = books;
}
extend(Author, Person);

Author.prototype.getBooks = function () {
    return this.books;
};

有了 superclass 屬性,就可以直接調(diào)用超類(lèi)的方法,這在既要重定義超類(lèi)的某個(gè)方法又想訪問(wèn)其在超類(lèi)的實(shí)現(xiàn)時(shí)可以派上用場(chǎng).例如,為了用一個(gè)新的 getName 方法重定義 Person 類(lèi)中的同名方法,你可以先用Author.superclass.getName 獲得作者名字,然后在此基礎(chǔ)上添加其他信息:

Author.prototype.getName = function () {
    var name = Author.superclass.getName.call(this);
    return name + ",Author of" + this.getBooks(0.join(", ");
}
原型式繼承

它與類(lèi)式繼承截然不同,此刻最好忘掉類(lèi)和實(shí)例的一切知識(shí),只從對(duì)象的角度來(lái)思考.用基于類(lèi)的辦法來(lái)創(chuàng)建對(duì)象包括兩個(gè)步驟:首先,用一個(gè)類(lèi)的聲明定義對(duì)象結(jié)構(gòu);第二,實(shí)例化該類(lèi)以創(chuàng)建一個(gè)新對(duì)象.用這種方式創(chuàng)建的對(duì)象都有一套該類(lèi)的所有實(shí)例屬性的副本.每一個(gè)實(shí)例方法都只存在一份,但是每個(gè)對(duì)象都有一個(gè)指向他的鏈接.
使用原型式繼承并不需要用類(lèi)來(lái)定義對(duì)象的結(jié)構(gòu),只需直接創(chuàng)建一個(gè)對(duì)象即可.這個(gè)對(duì)象隨后可以被新的對(duì)象重用,這個(gè)得益于原型鏈查找的工作機(jī)制.該對(duì)象被稱(chēng)為原型對(duì)象.取原型式繼承這個(gè)名稱(chēng)是因?yàn)樗麨槠渌麑?duì)象應(yīng)有的模式提供了一個(gè)原型.
下面我們使用原型式繼承重新設(shè)計(jì) Person 和 Author:

// Person Prototype Object.
var Person = {
    name: "default name",
    getName: function () {
        return this.name;
    }
};

這里沒(méi)有使用一個(gè)名為 Person 的構(gòu)造函數(shù)來(lái)定義類(lèi)的結(jié)構(gòu),Person現(xiàn)在是一個(gè)對(duì)象字面量.他是所要?jiǎng)?chuàng)建的其他各種類(lèi) Person 對(duì)象的原型對(duì)象.其中定義了所有類(lèi) Person 對(duì)象都要具備的屬性和方法,并且有默認(rèn)值.方法的默認(rèn)值一般不會(huì)改變,但是屬性與此相反.

var reader = clone(Person);
alert(reader.getName()); // This will output "default name".
reader.name ="John Smith";
alert(reader.getName()); // This will output "John Smith".

clone 函數(shù)可以用來(lái)創(chuàng)建新的類(lèi) Person 對(duì)象.他會(huì)創(chuàng)建一個(gè)空對(duì)象,二該對(duì)象的原型對(duì)象被設(shè)置成 Person.也就是說(shuō)如果在這個(gè)新對(duì)象中查找不刀某個(gè)方法或者屬性時(shí),那么接下來(lái)會(huì)在其原型對(duì)象中繼續(xù)查找.
不必為創(chuàng)建A Author 而定義一個(gè)一個(gè) Person 的子類(lèi),只需執(zhí)行一次克隆即可.

// Author Prototype Object.
var Author = clone(Person);
Author.books = []; // Default value.
Author.getBooks = function () {
    return this.books;
}

然后你可以重定義該克隆中的方法和屬性.可以修改在 Person 中提供的默認(rèn)值.也可以添加新的屬性和方法.這樣一來(lái)就創(chuàng)建了一個(gè)新的原型對(duì)象.可以將其用于創(chuàng)建新的類(lèi) Author 對(duì)象:

var author = [];

author[0] = clone(Author);
author[0].name = "Dustin Diaz";
author[0].books = ["Javascript Design Patterns"];

author[1] = clone(Author);
author[1].name = "Ross Harmes";
author[1].books = ["Javascript Design Patterns"];

author[1].getName();
author[1].getBooks();
對(duì)繼承而來(lái)的成員的讀寫(xiě)不對(duì)等性

在類(lèi)式繼承中,Author 的每一個(gè)實(shí)例都有一份自己的 books 數(shù)組副本,可以用代碼 author[1].books.push("New Book Title")為其添加元素.但是對(duì)于使用原型式繼承方式創(chuàng)建的類(lèi) Author 對(duì)象來(lái)說(shuō),由于原型鏈的工作方式,這種做法行不通.一個(gè)克隆并非其原型對(duì)象的一份完全獨(dú)立的副本,只是一個(gè)以那個(gè)對(duì)象為其原型對(duì)象的空對(duì)象而已.克隆剛被創(chuàng)建時(shí),author[1].name 其實(shí)是一個(gè)指向最初的Person.name 的鏈接,對(duì)于從原型對(duì)象繼承而來(lái)的成員,其讀和寫(xiě)具有內(nèi)在的不對(duì)等性.在讀取 author[1].name 的值時(shí),如果還沒(méi)有直接為 author[1]實(shí)例定義 name 屬性的話,那么所得到的事其原型對(duì)象的同名屬性值.而在寫(xiě)入 author[1].name 的值時(shí),你是在直接為 author[1]對(duì)象定義一個(gè)新屬性.下面這個(gè)實(shí)例顯示了這種不對(duì)等性:

var authorClone = clone(Author);
console.log(authorClone.name); // Linked to the primative Person.name, which is the string "default name".
authorClone.name = "new name";// A new primative is created and added to the authorClone object itself.
console.log(authorClone.name); // Now linked to the primative authorClone.name, which is the string "new name".

authorClone.books.push("new book"); // authorClone.books is linked to the arrayAuthor.books. We just modifiedthe prototype object"s default value, and all other objects that link to it will now have a new default value there.
authorClone.books = []; // A new array is created andadded to the authorClone object itself.
authorClone.books.push("new book"); // We are now modifying that new array.

上面的例子說(shuō)明了為什么必須通過(guò)引用傳遞的數(shù)據(jù)類(lèi)型的屬性創(chuàng)建新副本.向 authorClone.books 數(shù)組添加新元素實(shí)際上是把這個(gè)元素添加到Author.books 數(shù)組中,這樣的話值的修改會(huì)同時(shí)影響到Author 和所有繼承了 Author 但還未改寫(xiě)那個(gè)屬性的默認(rèn)值的對(duì)象.這種錯(cuò)誤必須盡量避免,調(diào)試起來(lái)會(huì)非常費(fèi)時(shí).在這類(lèi)場(chǎng)合,可以使用 hasOwnProperty 方法來(lái)區(qū)分對(duì)象的實(shí)際成員和繼承而來(lái)的成員.
有時(shí)原型對(duì)象自己也含有子對(duì)象.如果想覆蓋其子對(duì)象中的一個(gè)屬性值,不得不重新創(chuàng)建整個(gè)子對(duì)象.這可以通過(guò)將該子對(duì)象設(shè)置為一個(gè)空對(duì)象字面.然后對(duì)其重塑.但這意味著克隆出來(lái)的對(duì)象必須知道其原型對(duì)象的每一個(gè)子對(duì)象的確切結(jié)構(gòu).和默認(rèn)值.為了盡量弱化對(duì)象之間的耦合,任何復(fù)雜的子對(duì)象都應(yīng)該使用方法來(lái)創(chuàng)建:

var ComponoudObject = {
    string1: "default value",
    childObject: {
        bool: true,
        num: 10
    }
}

var CompoundObject = {
    string1: "default value",
    childObject: {
        bool: true,
        num: 10
    }
}
var compoundObjectClone = clone(CompoundObject);

// Bad! Changes the value of CompoundObject.childObject.num.
compoundObjectClone.childObject.num = 5;
// Better. Creates a new object, but compoundObject must know the structure of that object, and the defaults. This makes CompoundObject and compoundObjectClone tightly coupled.
compoundObjectClone.childObject = {
    bool: true,
    num: 5
};

在這個(gè)例子中,為 compoundObjectClone 必須知道 childObject 具有兩個(gè)默認(rèn)值分別為 true 和10的屬性.這里有一個(gè)更好的辦法: 用工廠辦法來(lái)創(chuàng)建 childObject:

// Best approach. Uses a method to create a new object, with the same structure and defaults as the original.

var CompoundObject = {};
CompoundObject.string1 = "default value";
CompoundObject.createChildObject = function () {
    return {
        bool: true,
        num: 10
    }
};
CompoundObject.childObject = CompoundObject.createChildObject();

var compoundObjectClone = clone(CompoundObject);
compoundObjectClone.childObject = CompoundObject.createChildObject();
compoundObjectClone.childObject.num = 5;
clone 函數(shù)

之前的例子用來(lái)創(chuàng)建克隆對(duì)象的 clone 函數(shù)究竟是什么樣呢:

// Clone function.

function clone(object) {
    function F() {}
    F.prototype = object;
    return new F;
}

clone 函數(shù)首先創(chuàng)建了一個(gè)新的空函數(shù) F,然后將 F 的 prototype 屬性設(shè)置作為參數(shù) object 傳入的原型對(duì)象.prototype 屬性就是用來(lái)指向原型對(duì)象的,通過(guò)原型鏈機(jī)制,它提供了到所有繼承而來(lái)的成員的鏈接.該函數(shù)最后通過(guò)把 new 運(yùn)算符作用于 F 創(chuàng)建出一個(gè)新對(duì)象.然后把這個(gè)新對(duì)象作為返回值返回.函數(shù)所返回的這個(gè)克隆結(jié)果是一個(gè)一給定對(duì)象為原型對(duì)象的空對(duì)象.

類(lèi)式繼承和原型式繼承的對(duì)比

類(lèi)式繼承和原型式繼承是大相徑庭的兩種繼承范型,他們生成的對(duì)象也有不同的行為方式.需要對(duì)兩者的優(yōu)缺點(diǎn)和特定使用場(chǎng)合進(jìn)行了解.
如果你設(shè)計(jì)的是一個(gè)眾人使用的 API,或者可能會(huì)有不熟悉原型式繼承的其他程序員基于你的代碼進(jìn)行改造.那么最好使用類(lèi)式繼承.

原型式繼承更能節(jié)約內(nèi)存.原型鏈讀取成員的方式使得所有克隆出來(lái)的對(duì)象都共享每個(gè)屬性和方法的唯一一份實(shí)例,只有在直接設(shè)置了某個(gè)克隆出來(lái)的對(duì)象的屬性和方法時(shí),情況才會(huì)變化.

類(lèi)式繼承方式中創(chuàng)建的每一個(gè)對(duì)象在內(nèi)存中都有自己的一套屬性(和私有方法)德芙笨.所以說(shuō)原型式繼承更節(jié)約內(nèi)存,而且只使用一個(gè) clone 函數(shù)也更為簡(jiǎn)練,不需要像后者那樣需要為每一個(gè)想繼承的類(lèi)寫(xiě)上好幾行這樣的晦澀代碼:
`SuperClass.call(this, arg)和 SubClass.prototype = new SuperClass...`
不過(guò)也可以寫(xiě)到 extend 方法里面去,所以說(shuō)最后到底使用哪種繼承方式除了考慮實(shí)際情況之外還取決于你的口味.

繼承和封裝

現(xiàn)在來(lái)談?wù)劮庋b對(duì)繼承的影響.
從現(xiàn)有的類(lèi)派生出一個(gè)子類(lèi)時(shí),只有公有和特權(quán)成員會(huì)被繼承下來(lái),但是私有成員無(wú)法繼承下來(lái).
由于這個(gè)原因,門(mén)戶(hù)大開(kāi)型類(lèi)是最適合派生子類(lèi)的,它們的所有成員都是公開(kāi)的,可以被遺傳給子類(lèi),如果某個(gè)成員需要稍加隱藏,可以使用下劃線規(guī)范.
在派生具有真正的私有成員的類(lèi)時(shí),特權(quán)方法是公有的,所以會(huì)被遺傳下來(lái).所以可以在子類(lèi)中間接訪問(wèn)父類(lèi)的私有屬性.但是子類(lèi)的實(shí)例方法都不能直接訪問(wèn)這些私有屬性.父類(lèi)的私有成員只能通過(guò)這些既有的特權(quán)方法訪問(wèn)到,在子類(lèi)中添加新特權(quán)方法也訪問(wèn)不到.

摻元類(lèi)

這是一種沒(méi)有嚴(yán)格繼承,重用代碼的方法.是這樣,如果想把一個(gè)函數(shù)用到多個(gè)類(lèi)中,可以通過(guò)擴(kuò)充的方式讓這些類(lèi)共享該函數(shù).

先創(chuàng)建一個(gè)包含各種通用方法的類(lèi),然后再用它擴(kuò)充其他類(lèi),這種類(lèi)叫做摻元類(lèi)(mixin class),通常不會(huì)被實(shí)例化或者直接調(diào)用,只是向其他類(lèi)提供自己的方法(說(shuō)實(shí)話這個(gè) mixin 在各種場(chǎng)合是不是很熟悉呢...各種 js 框架,css 預(yù)處理,是不是都跟這個(gè)有關(guān)呢...):

// Mixin class.
var Mixin = function () {};
Mixin.prototype = {
    serialize: function () {
        var output = [];
        for (key in this) {
            output.push(key + ";" + this[key]);
        }
        retyurn output.join(", ");
    }
};

這個(gè) Mixin 類(lèi)只有一個(gè)名為 serialize 的方法,遍歷 this 對(duì)象的所有成員并輸出一個(gè)字符串.這種方法可能在許多不同類(lèi)型的類(lèi)中都會(huì)用到,但是沒(méi)有必要讓這些類(lèi)都繼承 Mixin,最好是用一個(gè)函數(shù) augment 把這個(gè)方法添加到每一個(gè)需要他的類(lèi)中:

augment(Author, Mixin);

var author = new Author("Ross Harmes", ["Javascript Design Patterns"]);
var serializaedString = author.serialize();

在此我們用 Mixin 類(lèi)中的所有方法擴(kuò)充了 Author 類(lèi),Author 類(lèi)的實(shí)例現(xiàn)在就可以調(diào)用 serialize 方法了,稱(chēng)為為多親繼承 multiple inheritance.盡管在 js 中一個(gè)對(duì)象只能用有一個(gè)原型對(duì)象,不允許子類(lèi)繼承多個(gè)超類(lèi),但是一個(gè)類(lèi)可以用多個(gè)摻元類(lèi)擴(kuò)充,實(shí)際上也就實(shí)現(xiàn)了多繼承.
augment 函數(shù)很簡(jiǎn)單,實(shí)際上是用一個(gè) for...in 循環(huán)遍歷 第二個(gè)參數(shù)(Mixin 類(lèi),予類(lèi) giving class)的 prototype 中的每一個(gè)成員,并將其添加到第一個(gè)參數(shù)(受類(lèi) receiving class)的 prototype 中,如果受類(lèi)中已經(jīng)存在同名成員,那么跳過(guò)它,這樣受類(lèi)中的成員就不會(huì)被改寫(xiě).如果你想達(dá)到這么一個(gè)目的: 只復(fù)制摻元類(lèi)當(dāng)中的一兩個(gè)方法,那么就可以給 augment 函數(shù)加上第三個(gè)及更多的可選參數(shù):

// Augment function, improved.
function augment(receivingClass, givingClass) {
    if (arguments[2]) { // Only give certain methods.
        for (var i = 2, len = arguments.length; i < len; i++) {
            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
        }
    }
    else { // Give all methods.
        for (methodName in givingClass.prototype) {
            if (!receivingClass.prototype[methodName]) {
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }
        }
    }
}

現(xiàn)在使用 augment(Author, Mixin, "serialize");z可以只為 Author 類(lèi)添加一個(gè) serialize 方法的目的了.
從條理性的角度來(lái)看,嚴(yán)格的繼承方案比擴(kuò)充方案更加清楚.摻元類(lèi)非常適合于組織那些彼此迥然不同的類(lèi)所共享的方法.

小結(jié)

繼承的好處主要表現(xiàn)在代碼的重用方面.通過(guò)建立類(lèi)或者對(duì)象之間的繼承關(guān)系,有些方法我們只需定義一次即可.如果需要修改這些方法或者排查其中錯(cuò)誤,那么由于其定義只出現(xiàn)在一個(gè)位置,所以非常節(jié)省時(shí)間.

各種繼承范型各有優(yōu)缺點(diǎn).

原型式繼承工作機(jī)制: 先創(chuàng)建一些對(duì)象然后再對(duì)其進(jìn)行克隆,從而得到創(chuàng)建子類(lèi)和實(shí)例的等效效果.用這種辦法創(chuàng)建的對(duì)象有很高的內(nèi)存效率,因?yàn)樗鼈儠?huì)共享那些未被改寫(xiě)的屬性和方法.

在內(nèi)存效率重要的場(chǎng)合原型式繼承(clone 函數(shù))是最佳選擇,如果你更容易接受其他面向?qū)ο笳Z(yǔ)言中的繼承機(jī)制,那么對(duì)于 js 繼承還是選用類(lèi)式繼承(extend 函數(shù))比較好.這兩種方法都適合于類(lèi)間差異較小的類(lèi)層次體系(hierarchy).
如果類(lèi)之間的差異較大,那么用摻元類(lèi)來(lái)擴(kuò)充這些類(lèi)會(huì)更合理.

------------ 分割線 ----------

單體

也叫單例模式singleton,js 中最基本也最有用.將代碼組織為一個(gè)邏輯單元,可以通過(guò)單一的變量進(jìn)行訪問(wèn).單體對(duì)象只存在一份實(shí)例,所有代碼使用的都是同樣的全局資源.
單體類(lèi)在 js 中有許多用途,可以用來(lái)劃分命名空間,減少網(wǎng)頁(yè)中全局變量的數(shù)量.它們還可以在一種名為分支的技術(shù)中用來(lái)封裝瀏覽器之間的差異(在使用各種常用的工具函數(shù)時(shí)就不必再操心瀏覽器嗅探的事).
最重要的是,可以把代碼組織的更為一致,可維護(hù)性提高.
在網(wǎng)頁(yè)上使用全局變量有很大的風(fēng)險(xiǎn),而用單體對(duì)象創(chuàng)建的命名空間是清除這些全局變量的最佳手段之一.

基本結(jié)構(gòu)

這里先討論最基本最簡(jiǎn)單的類(lèi)型,一個(gè)對(duì)象字面量,把一批有一定關(guān)聯(lián)的方法和屬性組織在一起:

// Basic Singleton.
var Singleton = {
    attribute1: true,
    attribute2, 10,

    method1: function () {

    },
    method2: function (arg) {

    }
};

示例中,所有成員都可以通過(guò)變量 Singleton 訪問(wèn).可以使用圓點(diǎn)運(yùn)算符.
這個(gè)單體對(duì)象可以被修改.可以為其添加新成員,也可以用 delete 運(yùn)算符刪除其現(xiàn)有成員.實(shí)際上違背了面向?qū)ο笤O(shè)計(jì)的一條原則:類(lèi)可以被擴(kuò)展,但不應(yīng)該被修改(道理有點(diǎn)像 css classes 的增減).區(qū)別于其他面向?qū)ο笳Z(yǔ)言js 中的所有對(duì)象都易變,如果某些變量需要保護(hù),那么可以將其定義在閉包之中.
你可能注意到了,剛剛的示例并不是單體,因?yàn)榘凑斩x,單體是一個(gè)只能被實(shí)例化一次并且可以通過(guò)一個(gè)訪問(wèn)點(diǎn)訪問(wèn)的類(lèi),而這個(gè)例子不是一個(gè)可實(shí)例化的類(lèi).我們可以把單體定義地更廣義一些:
單體是一個(gè)可以用來(lái)劃分命名空間并將一批相關(guān)方法和屬性組織到一起的對(duì)象,如果可以被實(shí)例化,那么它只能被實(shí)例化一次.
并非所有對(duì)象字面量都是單體,如果只是用來(lái)模仿關(guān)聯(lián)數(shù)組或者容納數(shù)據(jù)的話,那就不是單體;但是如果是用來(lái)組織一批相關(guān)方法和屬性的話就有可能是單體.

劃分命名空間

單體對(duì)象有兩部分: 包含著方法和屬性成員的對(duì)象自身,還有用于訪問(wèn)它的變量.這個(gè)變量通常是全局性的,這個(gè)變量通常是全局性的,一遍在網(wǎng)頁(yè)上任何地方都能直接訪問(wèn)到它所指向的單體對(duì)象.

雖然定義單體不必是全局性的,但是它應(yīng)該在各個(gè)地方都能被訪問(wèn),因?yàn)閱误w對(duì)象的所有內(nèi)部成員都被包裝在這個(gè)對(duì)象中,所以它們不是全局性的.

由于這些成員只能通過(guò)這個(gè)單體對(duì)象變量進(jìn)行訪問(wèn),所以可以說(shuō)它們被單對(duì)對(duì)象圈在了一個(gè)命名空間中.

// using a namespace.
var MyNameSpace = {
    findProduct: function(id) {
        ...
    },
    // Other methods can go there as well.
}

...

// Later in your page, another programmer adds...
var resetProduct = $("reset-product-button");
var findProduct = $("reset-product-button"); // NOthing was overwritten.

現(xiàn)在 findProduct 函數(shù)是MyNameSpace中的一個(gè)辦法,他不會(huì)被全局命名空間中聲明的任何新變量改寫(xiě).該方法仍然可以從各個(gè)地方訪問(wèn),但是現(xiàn)在調(diào)用方式不是 findProduct(id),而是 MyNameSpace.findProduct(id).

用作特定網(wǎng)頁(yè)專(zhuān)用代碼的包裝器的單體

已經(jīng)知道如何把單體作為命名空間使用,現(xiàn)在我們?cè)诮榻B單體的一個(gè)特殊用途.

有些 js 代碼是一個(gè)網(wǎng)站中所有網(wǎng)頁(yè)都要用到的,通常被存放在獨(dú)立的文件中;有些代碼則是某個(gè)網(wǎng)頁(yè)專(zhuān)用的,不會(huì)被用到其他地方,可以把這兩種代碼分別包裝到自己的單體對(duì)象中.

擁有私有成員的單體

之前我們討論過(guò)創(chuàng)建類(lèi)的私有成員的做法,使用真正私有方法一個(gè)缺點(diǎn)在于它們比較耗費(fèi)內(nèi)存,因?yàn)槊總€(gè)實(shí)例都具有方法的一份新副本,不過(guò)由于單體對(duì)象只會(huì)被實(shí)例化一次,所以定義真正的私有方法時(shí)不用考慮內(nèi)存.不過(guò)我們先談?wù)劯?jiǎn)單的創(chuàng)建偽私有成員的做法.

使用下劃線
// DataParser singleton, coverts character delimited strings into arrays.

GaintCorp.DAtaParser = {
    // private methods.
    _stripWhitespace: function (str) {
        return str.replace(/s+/, "");
    },
    _stringSplit: function(str, delimiter) {
        return str.splist(delimiter);
    },

    // Public method.
    stringToArray: function(str, delimiter, stripWS) {
        if (stripWS) {
            str = this._stripWhitespace(str);
        }
        var outputArray = this._stringSplit(str, delimiter);
        return outputArray;
    }
};
使用閉包

在單體對(duì)象中創(chuàng)建私有成員的第二種辦法需要借助閉包.與之前創(chuàng)建真正私有成員的做法非常相似.
但也有重要區(qū)別.先前的做法是把變量和函數(shù)定義在構(gòu)造函數(shù)體內(nèi)(不使用 this 關(guān)鍵字),此外還在構(gòu)造函數(shù)內(nèi)定義了所有的特權(quán)方法并用 this 關(guān)鍵字使其可被外界訪問(wèn).每生成一個(gè)該類(lèi)的實(shí)例時(shí),所有聲明在構(gòu)造函數(shù)內(nèi)的方法和屬性都會(huì)再次創(chuàng)建一份,可能會(huì)非常低效.
因?yàn)閱误w只會(huì)被實(shí)例化一次,所以構(gòu)造函數(shù)內(nèi)成員個(gè)數(shù)不是重點(diǎn).每個(gè)方法和屬性都只會(huì)被創(chuàng)建一次,所以可以把它們都聲明在構(gòu)造函數(shù)內(nèi)(位于同一個(gè)閉包內(nèi))

// Singleton as an Object Literal.
MyNamespace.Singleton = {};

// Singleton with Private Members, step 1.
MyNamespace.Singleton = function () {
    return {};
}();

上面兩個(gè) MyNamespace.Singleton 完全相同.對(duì)于第二個(gè),并沒(méi)有把一個(gè)匿名函數(shù)賦給 MyNamespce.Singleton而是返回一個(gè)對(duì)象再賦值.函數(shù)定義后的大括號(hào)是為了立即執(zhí)行該函數(shù).還可以像下面那樣再套上一對(duì)圓括號(hào).

現(xiàn)在大概可以知道,談到單體,有兩個(gè)關(guān)鍵詞:閉包+大括號(hào)
再回顧一下,可以把公有成員添加到單體所返回的那個(gè)對(duì)象字面量:

//Singleton with Private Members, step 2.

MyNamespace.Singleton = (function () {
    return { // Public members.
        publicAttribute0: true,
        publicAttribute2: 99,

        publicMethod1: function () {
            ...
        }
    };
})();

使用閉包和使用一個(gè)對(duì)象字面量的區(qū)別在于:
對(duì)于前者,任何聲明在匿名函數(shù)中(但不是在那個(gè)對(duì)象字面量中)的變量或者函數(shù)都只能被在同一個(gè)閉包中聲明的其他函數(shù)訪問(wèn).這個(gè)閉包在匿名函數(shù)結(jié)束執(zhí)行后依然存在,所以在其中聲明的函數(shù)和變量總能從匿名函數(shù)所返回的對(duì)象內(nèi)部訪問(wèn).

單體模式跟js模塊化有一定關(guān)聯(lián),所以又稱(chēng)模塊模式,意指他可以把一批相關(guān)方法和屬性組織為模塊并起到劃分命名空間.

比較

現(xiàn)在我們不再為每個(gè)私有方法名稱(chēng)的開(kāi)頭添加一個(gè)下劃線,而是把這些方法定義在閉包中:

// DataParser singleton, converts character delimited strings into arrays.
// Now using true private methods.

CiantCorp.DataPraser = (function () {
    // Private attributes.
    var whitespaceRegex =/s+/;

    // Private methods.
    function stripWhitespace(str) {
        return str.repalce(whitespaceRegex, "");
    }
    function stringSplit(str, delimiter) {
        return str.split(delimiter);
    }

    // Everything returned in the object literal is public, but can access the members in the closure created above.

    return {
        // Public method.
        stringToArray: function(str, delimiter, stringWS) {
        if (stringWS) {
            str = stripWhitespace(str);
        }
        var outputArray = stringSplit(str, delimiter);
        return outputArray;
        }
    };
})();
// Invoke the functio nand assign the returned object literal to GiantCorp.DataParser.

現(xiàn)在這些私有方法和屬性可以直接用其名稱(chēng)訪問(wèn),不必在其前面加上 this.或者GaintCorp.DataParser,這些前綴只用于訪問(wèn)單體對(duì)象的公有成員.
單體相比于下劃線表示法有幾點(diǎn)優(yōu)勢(shì):

把私有成員放到閉包中可以確保其不會(huì)在單體對(duì)象之外被使用.

可以任意改變對(duì)象實(shí)現(xiàn)細(xì)節(jié),不破壞其他代碼.

還可以對(duì)數(shù)據(jù)進(jìn)行保護(hù)和封裝.

使用單體時(shí),可以享受真正的私有成員帶來(lái)的好處,單體類(lèi)只會(huì)被實(shí)例化一次,可以節(jié)省內(nèi)存.這是單體模式成為受歡迎,應(yīng)用廣泛的模式之一的原因.

注意事項(xiàng):公有成員和私有成員的聲明語(yǔ)法不一樣,前者被聲明在對(duì)象字面量?jī)?nèi)部而后者并不是這樣.私有屬性必須用 var 聲明,否則它將成為全局性的,私有方法是按 `function funcName (args) {...}` 這樣的形式聲明,在最后一個(gè)大括號(hào)之后不需要使用分號(hào),公有屬性和方法分別按照 attributeName: attributeValue 和 `methodName: function (args) {...}`這樣的形式聲明.如果后面還要聲明別的成員的話,那么該聲明的后面應(yīng)該加上一個(gè)逗號(hào).
惰性實(shí)例化

之前的單體模式的各種實(shí)現(xiàn)方式有一個(gè)共同點(diǎn):單體對(duì)象都是在腳本加載時(shí)被加載出來(lái),如果單體資源密集或者配置開(kāi)銷(xiāo)大,那么更合理的做法是將其實(shí)例化推遲到需要使用它的時(shí)候.被稱(chēng)為惰性加載,最常用于那些必須加載大量數(shù)據(jù)的單體.那些被用做命名空間,特定網(wǎng)頁(yè)專(zhuān)用代碼包裝器,組織相關(guān)實(shí)用方法的工具的單體最好還是立即實(shí)例化.
惰性加載單體的特別之處在于對(duì)他們的訪問(wèn)必須借助于一個(gè)靜態(tài)方法.這樣調(diào)用: Singleton.getInstance().methodName(),而不是這樣調(diào)用: Singleton.methodName().getInstance()方法薈兼差單體是否已經(jīng)被實(shí)例化,如果還沒(méi)有,那么將創(chuàng)建并且返回實(shí)例.如果實(shí)例化過(guò),那么它將返回現(xiàn)有實(shí)例.下面我們從前面那個(gè)擁有真正私有成員的單體出發(fā)將普通單體轉(zhuǎn)化為惰性加載單體(轉(zhuǎn)化工作第一步是把單體的所有代碼移到一個(gè)叫做 constructor 的方法中:

// General skeleton for a lazy loading singleton, step 1.

MyNamespace.Singleton = (function() {

    function constructor () { // All of the normal singleton code goes here.
        // Private members.
        var privateAttribute1 = false;
        var privateAttribute2 = [1, 2, 3];

        function privateMethod1 () {
            ...
        }
        function privateMethod2 () {
            ...
        }

        return  { // Public members.
            publicAttribute1: true,
            publicAttribute2: 2,

            publicMethod1: function () {
                ...
            }
        }
    }
})();

這個(gè)方法不能從閉包外部訪問(wèn)這是件好事,因?yàn)槲覀兿肟刂普{(diào)用時(shí)機(jī).公有方法 getInstance 就是要這么做,為了使其成為公有方法,只需要將其放到一個(gè)對(duì)象字面量中并且返回該對(duì)象即可:

// General skeleton for a lazy loading singleton, step 2.

MyNamespace.Singleton = (fucntion () {
    function constructor() { // All of the normal singleton code goes here.
        ...
    }

    return  {
        getInstance: function () {
            // Control code goes here.
        }
    }
})();

現(xiàn)在討論如何編寫(xiě)控制實(shí)例化時(shí)機(jī)的代碼.首先,必須知道該類(lèi)是否被實(shí)例化過(guò);其次,如果該類(lèi)被實(shí)例化過(guò),那么他需要掌握其實(shí)例的情況,以便能返回這個(gè)示例;要做到這兩點(diǎn),需要用到一個(gè)私有屬性和已有的私有方法constructor:

// General skeleton for a lazy loading singleton, step 3.
MyNamespace.Singleton = (function () {
    var uniqueInstance; // Private attribute that holds the single instance.

    function constructor () { // All of the normal singleton code goes here.
        ...
    }

    return {
        getInstance: function () {
            if (!uniqueInstance) { // Intantiate only if the instance doesn"t exist.
                uniqueInstance = constructor();
            }
            return uniqueInstance;
        }
    }
})();

惰性加載單體缺點(diǎn)在于復(fù)雜性,用于創(chuàng)建這種類(lèi)型的單體代碼并不直觀,不易理解.如果你需要?jiǎng)?chuàng)建一個(gè)延遲加載實(shí)例化的單體,那么最好為其寫(xiě)注釋,以免別人把其簡(jiǎn)化為普通單體.

分支

一種用來(lái)將瀏覽器間的差異封裝到在運(yùn)行期間進(jìn)行設(shè)置的動(dòng)態(tài)方法中的技術(shù).假設(shè)我們需要?jiǎng)?chuàng)建一個(gè)返回 XHR 對(duì)象的方法,這個(gè)XHR對(duì)象在大多數(shù)瀏覽器中是 XMLHttpRequest 類(lèi)的實(shí)例,而在 IE 早期版本中則是某種 ActiveX 類(lèi)的實(shí)例.我們要?jiǎng)?chuàng)建的方法通常會(huì)進(jìn)行某種瀏覽器嗅探或者對(duì)象檢測(cè).如果不用分支技術(shù),那么每次調(diào)用時(shí),所有那些瀏覽器嗅探代碼都要再次運(yùn)行.如果調(diào)用頻繁,那么會(huì)很低效.
更有效的做法是只在腳本加載時(shí)一次性地確定針對(duì)特定瀏覽器的代碼,遮掩的話,在初始化后,每種瀏覽器都會(huì)只執(zhí)行針對(duì)他的 js 實(shí)現(xiàn)而設(shè)計(jì)的代碼.能夠在運(yùn)行時(shí)動(dòng)態(tài)確定函數(shù)代碼的能力,是 js 的高度靈活性和強(qiáng)大表現(xiàn)能力的一種體現(xiàn),提高了調(diào)用這些函數(shù)的效率.
在之前,單體對(duì)象的所有代碼都是在運(yùn)行時(shí)確定的,這在鄙薄創(chuàng)建私有成員的模式中很容易看出來(lái):

MyNamespace.Singleton = (function () {
    return {};
})();

這個(gè)匿名函數(shù)在運(yùn)行時(shí)執(zhí)行,返回的對(duì)象字面量賦值給 MyNamespace.Singleton 變量.

示例: 創(chuàng)建 XHR 對(duì)象

現(xiàn)在我們要?jiǎng)?chuàng)建一個(gè)單體,他有一個(gè)用來(lái)生成 XHR 對(duì)象實(shí)例的方法.
首先判斷分支數(shù)量,因?yàn)樗袑?shí)例化的對(duì)象只有3種不同類(lèi)型,所以需要3個(gè)分支,分別按照其返回的XHR 對(duì)象類(lèi)型命名:

// SimpleXhrFactory singleton.
var SimpleXhrFactory = (function () {

    // Three branches.
    var standard = {
        createXhrObject: function () {
            return new XMLHttpRequest();
        }
    };
    var activeXNew = {
        createXhrObject: function () {
            return new ActiveXObject("Msxml2.XMLHTTP");
        }
    };
    var activeXOld = {
        createXhrObject: function () {
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    };

    // To assign the branch, try each method,
    var testObject;
    try {
        testObject = standard.createXhrObject();
        return standard; // Return this if no error was thrown.
    }
    catch(e) {
        try {
            testObject = activeXNew.createObject();
            return activeNew;
        }
        catch(e) {
            try {
                testObject = activeXOld.createXhrObject();
                return activeXOld;
            }
            catch(e) {
                throw new Error("No XHR object found in the environment.");
            }
        }
    }

})();

上面的示例代碼創(chuàng)建了三個(gè)對(duì)象字面量,它們有相同一個(gè)方法 createXhrObject(),它是用來(lái)返回一個(gè)可以執(zhí)行異步請(qǐng)求的新對(duì)象,很明顯名字雖然一樣,方法內(nèi)部代碼不一樣,分支之間作出選擇的判斷條件值是在運(yùn)行時(shí)確定.這種條件通常是某種能力檢測(cè)的結(jié)果,目的在于確保運(yùn)行代碼的 js 環(huán)境準(zhǔn)確地實(shí)現(xiàn)了所需要的條件特性.
本例中,具體的條件判斷步驟是這樣的: 使用 try{...} catch{...} 來(lái)逐一嘗試每種 XHR 對(duì)象,直到遇到一個(gè)當(dāng)前 js 環(huán)境所支持的對(duì)象為止.

使用該 API,只要調(diào)用SimpleXhtFacyory.createXhtObject();就能得到適合特定的運(yùn)行時(shí)環(huán)境的 XHR 對(duì)象.用了分支技術(shù),所有那些特性嗅探代碼只會(huì)執(zhí)行一次,不是每生成一個(gè)對(duì)象就要執(zhí)行一次.

單體的使用場(chǎng)合

使用單體,一則,提供命名空間,二則,增強(qiáng)其模塊性.
單體模式幾乎適用于所有大大小小的項(xiàng)目,在簡(jiǎn)單快開(kāi)發(fā)的項(xiàng)目中,可以只把單體用作命名空間,將自己的所有代碼組織在一個(gè)全局變量名下;在稍大稍復(fù)雜的項(xiàng)目中,把單體用來(lái)把相關(guān)代碼組織在一起以便日后維護(hù);在大型項(xiàng)目中:那些開(kāi)銷(xiāo)較大卻很少使用的組件可以被包裝到惰性加載單體中,而針對(duì)特定環(huán)境的代碼可以被包裝到分支型單體中.
幾乎所有項(xiàng)目都會(huì)用到某種形式的單體,js 的靈活性使得單體可以被用于多種不同任務(wù),它在 js 當(dāng)中的重要性大大超過(guò)他在其他語(yǔ)言中的重要性.因?yàn)樗梢杂脕?lái)創(chuàng)建命名空間以減少全局變量的數(shù)目.由于全局變量在 js 中很容易被其他人重寫(xiě),所以相當(dāng)危險(xiǎn),單體模式可以很好的解決這種問(wèn)題.

主要好處在于對(duì)代碼的組織.
把相關(guān)方法和屬性組織在一個(gè)不會(huì)被多次實(shí)例化的單體中,可以使得代碼的調(diào)試和維護(hù)更輕松.單體可以把你的代碼和第三方庫(kù)代碼,廣告代碼哥離開(kāi),提高網(wǎng)頁(yè)的穩(wěn)定性.
單體的一些高級(jí)變體可以在開(kāi)發(fā)周期的后期用于對(duì)腳本進(jìn)行優(yōu)化,提升性能.
惰性實(shí)例化,可以直到需要一個(gè)對(duì)象的時(shí)候才創(chuàng)建它,從而減少哪些不需要他的用戶(hù)承受的不必要的內(nèi)存消耗.
分支技術(shù)可以根據(jù)運(yùn)行時(shí)條件確定賦給單體變量的對(duì)象字面量,創(chuàng)建出為特定環(huán)境量身定制的方法,不會(huì)在每次調(diào)用時(shí)都一再浪費(fèi)時(shí)間去檢查運(yùn)行環(huán)境.

主要的客觀缺點(diǎn):
單體提供的是一種單點(diǎn)訪問(wèn),所以可能導(dǎo)致模塊間強(qiáng)耦合,不利于單元測(cè)試.無(wú)法多帶帶測(cè)試一個(gè)調(diào)用了來(lái)自單體的方法的類(lèi),只能把她與那個(gè)單體作為一個(gè)單元一起測(cè)試.
而對(duì)于劃分命名空間,實(shí)現(xiàn)分支型方法這些用途,耦合不是什么問(wèn)題.
有時(shí)候某些其他更高級(jí)的模式比單體高級(jí)變體更符合任務(wù)需要.
虛擬代理與惰性加載單體,可以給予你對(duì)類(lèi)實(shí)例化方式更多的控制;還可以是用一個(gè)對(duì)象工廠來(lái)取代分支型單體.

小結(jié)

作為 js 中最基本的模式,它不僅可以多帶帶使用,還能和大多數(shù)其他模式配合使用.
例如,對(duì)象工廠可以被設(shè)計(jì)為單體,組合對(duì)象的所有子對(duì)象也可以被封裝進(jìn)一個(gè)單體命名空間中.
本書(shū)講的是如何創(chuàng)建可重用的模塊化代碼,單體對(duì)全局變量的減少具有重要作用.

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

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

相關(guān)文章

  • 鏈?zhǔn)秸{(diào)用&amp;工廠

    摘要:鏈?zhǔn)秸{(diào)用精髓在于重用一個(gè)初始操作可以把方法的鏈?zhǔn)秸{(diào)用技術(shù)寫(xiě)到自己所寫(xiě)的整個(gè)庫(kù)中把自己喜歡的方法串起來(lái)調(diào)用兩個(gè)部分一個(gè)創(chuàng)建代表元素的對(duì)象的工廠還有一批對(duì)這個(gè)元素執(zhí)行某些操作的方法每一個(gè)這種方法都可以在方法名前附加一個(gè)圓點(diǎn)后加入調(diào)用鏈中方法的鏈 鏈?zhǔn)秸{(diào)用 精髓在于重用一個(gè)初始操作. 可以把方法的鏈?zhǔn)秸{(diào)用技術(shù)寫(xiě)到自己所寫(xiě)的整個(gè) js 庫(kù)中,把自己喜歡的方法串起來(lái)調(diào)用.兩個(gè)部分:一個(gè)創(chuàng)建代表 ...

    Lemon_95 評(píng)論0 收藏0
  • K8s、DevOps &amp; 微服務(wù)三駕馬車(chē),帶您走上云原生轉(zhuǎn)型之路

    摘要:針對(duì)這樣的客戶(hù),靈雀云除了提供容器云,還會(huì)基于容器云提供工具鏈和咨詢(xún)服務(wù)。第三階段,是上云原生。靈雀云建議,先做邊緣應(yīng)用系統(tǒng)的微服務(wù)化,或者單體直接應(yīng)用上云。靈雀云會(huì)幫助客戶(hù)成立專(zhuān)家組,實(shí)踐敏捷活動(dòng)和工具鏈一整套的解決方案。 今天很榮幸能在這里跟大家一起分享下靈雀云在金融行業(yè)的云原生解決方案。 CNCF的云原生核心理念是快速交付業(yè)務(wù)價(jià)值,而云原生時(shí)代,主要由三駕馬車(chē)驅(qū)動(dòng):容器、DevO...

    godiscoder 評(píng)論0 收藏0
  • 7個(gè) Javascript 面試題及回答策略

    摘要:使用異步編程,有一個(gè)事件循環(huán)。它作為面向?qū)ο缶幊痰奶娲桨?,其中?yīng)用狀態(tài)通常與對(duì)象中的方法搭配并共享。在用面向?qū)ο缶幊虝r(shí)遇到不同的組件競(jìng)爭(zhēng)相同的資源的時(shí)候,更是如此。 翻譯:瘋狂的技術(shù)宅原文:https://www.indeed.com/hire/i... 本文首發(fā)微信公眾號(hào):jingchengyideng歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 不管你是面試官還是求職者,里面...

    李義 評(píng)論0 收藏0
  • SegmentFault 社區(qū)訪談 | mcfog:自由進(jìn)出 Coding &amp;&amp; A

    摘要:上次的訪談,介紹了下可愛(ài)的依云醬,回憶傳送門(mén)。這里簡(jiǎn)單地介紹下龍女仆,全名小林家的龍女仆,為什么介紹這部劇呢因?yàn)樵O(shè)計(jì)獅顏值同學(xué)也安利了這部。劇情簡(jiǎn)介在獨(dú)身又勞累的小林劃重點(diǎn)一名程序員身邊突然出現(xiàn)的穿著女仆服裝的美少女托爾。 showImg(https://segmentfault.com/img/bVR6p5?w=900&h=385); 上次的訪談,介紹了下可愛(ài)的依云醬,回憶傳送門(mén)。不...

    neroneroffy 評(píng)論0 收藏0
  • SegmentFault 社區(qū)訪談 | mcfog:自由進(jìn)出 Coding &amp;&amp; A

    摘要:上次的訪談,介紹了下可愛(ài)的依云醬,回憶傳送門(mén)。這里簡(jiǎn)單地介紹下龍女仆,全名小林家的龍女仆,為什么介紹這部劇呢因?yàn)樵O(shè)計(jì)獅顏值同學(xué)也安利了這部。劇情簡(jiǎn)介在獨(dú)身又勞累的小林劃重點(diǎn)一名程序員身邊突然出現(xiàn)的穿著女仆服裝的美少女托爾。 showImg(https://segmentfault.com/img/bVR6p5?w=900&h=385); 上次的訪談,介紹了下可愛(ài)的依云醬,回憶傳送門(mén)。不...

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

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

0條評(píng)論

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