国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

《JavaScript面向?qū)ο缶纷x書筆記

GitCafe / 3231人閱讀

摘要:解除引用的最佳手段是將對(duì)象變量設(shè)置為。字面形式允許你在不需要使用操作符和構(gòu)造函數(shù)顯示創(chuàng)建對(duì)象的情況下生成引用值。函數(shù)就是值可以像使用對(duì)象一樣使用函數(shù)因?yàn)楹瘮?shù)本來就是對(duì)象,構(gòu)造函數(shù)更加容易說明。

JavaScript(ES5)的面向?qū)ο缶?/b>

標(biāo)簽: JavaScript 面向?qū)ο?讀書筆記

2016年1月16日-17日兩天看完了《JavaScript面向?qū)ο缶罚▍⒓赢惒缴鐓^(qū)的活動(dòng)送的),這本書雖然不夠100頁,但都是精華,不愧是《JavaScript高級(jí)程序設(shè)計(jì)》作者 Nicholas C.Zakas 的最新力作。

下面是我的讀書筆記(ES5):

1.原始類型和引用類型 1.1 什么是類型

原始類型 保存為簡單數(shù)據(jù)值。
引用類型 保存為對(duì)象,其本質(zhì)是指向內(nèi)存位置的引用。

為了讓開發(fā)者能夠把原始類型和引用類型按相同的方式處理,JavaScript花費(fèi)了很大的努力來保證語言的一致性。

其他編程語言用棧存原始類型,用對(duì)存儲(chǔ)引用類型。而JavaScript則完全不同:它使用一個(gè)變量對(duì)象追蹤變量的生存期。原始值被直接保存在變量對(duì)象內(nèi),而引用值則作為一個(gè)指針保存在變量對(duì)象內(nèi),該指針指向?qū)嶋H對(duì)象在內(nèi)存中的存儲(chǔ)位置。

1.2 原始類型

原始類型代表照原樣保存的一些簡單數(shù)據(jù)。
JavaScript共有 5 種原始類型:

boolean 布爾,值為 true or false

number 數(shù)字,值為任何整型或浮點(diǎn)數(shù)值

string 字符串,值為由單引號(hào)或雙引號(hào)括住的單個(gè)字符或連續(xù)字符

null 空類型,僅有一個(gè)值:null

undefined 未定義,只有一個(gè)值:undefined(undefined會(huì)被賦給一個(gè)還沒有初始化的變量)

JavaScript和許多其他語言一樣,原始類型的變量直接保存原始值(而不是一個(gè)指向?qū)ο蟮闹羔槪?/p>

var color1 = "red";
var color2 = color1;

console.log(color1); // "red"
console.log(color2); // "red"

color1 = "blue";

console.log(color1); // "blue"
console.log(color2); // "red"
鑒別原始類型

鑒別原始類型的最佳方式是使用 typeof 操作符。

console.log(typeof "Nicholas"); // "string"
console.log(typeof 10);         // "number"
console.log(typeof true);       // "boolean"
console.log(typeof undefined);  // "undefined"

至于空類型(null)則有些棘手。

console.log(typeof null); // "object"

對(duì)于 typeof null,結(jié)果是"object"。(其實(shí)這已被設(shè)計(jì)和維護(hù)JavaScript的委員會(huì)TC39認(rèn)定是一個(gè)錯(cuò)誤。在邏輯上,你可以認(rèn)為 null 是一個(gè)空的對(duì)象指針,所以結(jié)果為"object",但這還是很令人困惑。)

判斷一個(gè)值是否為空類型(null)的最佳方式是直接和 null 比較:

console.log(value === null); // true or false

注意:以上這段代碼使用了三等號(hào)(全等===),因?yàn)槿忍?hào)(全等)不會(huì)將變量強(qiáng)制轉(zhuǎn)換為另一種類型。

console.log("5" == 5); // true
console.log("5" === 5); // false

console.log(undefined == null); // true
console.log(undefined === null); // false
原始方法

雖然字符串、數(shù)字和布爾值是原始類型,但是它們也擁有方法(null和undefined沒有方法)。

var name = "Nicholas";
var lowercaseName = name.toLowerCase(); // 轉(zhuǎn)為小寫

var count = 10;
var fixedCount = count.toFixed(2); // 轉(zhuǎn)為10.00

var flag = true;
var stringFlag = flag.toString(); // 轉(zhuǎn)為"true"

console.log("YIBU".charAt(0)); // 輸出"Y"

盡管原始類型擁有方法,但它們不是對(duì)象。JavaScript使它們看上去像對(duì)象一樣,以此來提高語言上的一致性體驗(yàn)。

1.3 引用類型

引用類型是指JavaScript中的對(duì)象,同時(shí)也是你在該語言中能找到最接近類的東西。
引用值是引用類型的實(shí)例,也是對(duì)象的同義詞(后面將用對(duì)象指代引用值)。對(duì)象是屬性的無序列表。屬性包含鍵(始終是字符串)和值。如果一個(gè)屬性的值是函數(shù),它就被稱為方法。除了函數(shù)可以運(yùn)行以外,一個(gè)包含數(shù)組的屬性和一個(gè)包含函數(shù)的屬性沒有什么區(qū)別。

創(chuàng)建對(duì)象

有時(shí)候,把JavaScript對(duì)象想象成哈希表可以幫助你更好地理解對(duì)象結(jié)構(gòu)。

JavaScript 有好幾種方法可以創(chuàng)建對(duì)象,或者說實(shí)例化對(duì)象。第一種是使用 new 操作符和構(gòu)造函數(shù)。
構(gòu)造函數(shù)就是通過 new 操作符來創(chuàng)建對(duì)象的函數(shù)——任何函數(shù)都可以是構(gòu)造函數(shù)。根據(jù)命名規(guī)范,JavaScript中的構(gòu)造函數(shù)用首字母大寫來跟非構(gòu)造函數(shù)進(jìn)行區(qū)分。

var object = new Object();

因?yàn)橐妙愋筒辉僮兞恐兄苯颖4鎸?duì)象,所以本例中的 object 變量實(shí)際上并不包含對(duì)象的實(shí)例,而是一個(gè)指向內(nèi)存中實(shí)際對(duì)象所在位置的指針(或者說引用)。這是對(duì)象和原始值之間的一個(gè)基本差別,原始值是直接保存在變量中。

當(dāng)你將一個(gè)對(duì)象賦值給變量時(shí),實(shí)際是賦值給這個(gè)變量一個(gè)指針。這意味著,將一個(gè)變量賦值給另外一個(gè)變量時(shí),兩個(gè)變量各獲得了一份指針的拷貝,指向內(nèi)存中的同一個(gè)對(duì)象。

var obj1 = new Object();
var obj2 = obj1;

對(duì)象引用解除

JavaScript語言有垃圾收集的功能,因此當(dāng)你使用引用類型時(shí)無需擔(dān)心內(nèi)存分配。但最好在不使用對(duì)象時(shí)將其引用解除,讓垃圾收集器對(duì)那塊內(nèi)存進(jìn)行釋放。解除引用的最佳手段是將對(duì)象變量設(shè)置為 null。

var obj1 = new Object();
// dosomething
obj1 = null; // dereference
添加刪除屬性

在JavaScript中,你可以隨時(shí)添加和刪除其屬性。

var obj1 = new Object();
var obj2 = obj1;

obj1.myCustomProperty = "Awsome!";
console.log(obj2.myCustomProperty); // "Awsome!" 因?yàn)閛bj1和obj2指向同一個(gè)對(duì)象。
1.4 內(nèi)建類型實(shí)例化

內(nèi)建類型如下:

Array 數(shù)組類型,以數(shù)字為索引的一組值的有序列表

Date 日期和時(shí)間類型

Error 運(yùn)行期錯(cuò)誤類型

Function 函數(shù)類型

Object 通用對(duì)象類型

RegExp 正則表達(dá)式類型

可使用 new 來實(shí)例化每一個(gè)內(nèi)建引用類型:

var items = new Array();
var new = new Date();
var error = new Error("Something bad happened.");
var func = new Function("console.log("HI");");
var object = new Object();
var re = new RegExp();
字面形式

內(nèi)建引用類型有字面形式。字面形式允許你在不需要使用 new 操作符和構(gòu)造函數(shù)顯示創(chuàng)建對(duì)象的情況下生成引用值。屬性的可以是標(biāo)識(shí)符或字符串(若含有空格或其他特殊字符)

var book = {
    name: "Book_name",
    year: 2016
}

上面代碼與下面這段代碼等價(jià):

var book = new Object();
book.name = "Book_name";
book.year = 2016;

雖然使用字面形式并沒有調(diào)用 new Object(),但是JavaScript引擎背后做的工作和 new Object()一樣,除了沒有調(diào)用構(gòu)造函數(shù)。其他引用類型的字面形式也是如此。

1.5 訪問屬性

可通過 .中括號(hào) 訪問對(duì)象的屬性。
中括號(hào)[]在需要?jiǎng)討B(tài)決定訪問哪個(gè)屬性時(shí),特別有用。因?yàn)槟憧梢杂?strong>變量而不是字符串字面形式來指定訪問的屬性。

1.6 鑒別引用類型

函數(shù)是最容易鑒別的引用類型,因?yàn)閷?duì)函數(shù)使用 typeof 操作符時(shí),返回"function"。

function reflect(value){
    return value;
}
console.log(typeof reflect); // "function"

對(duì)其他引用類型的鑒別則較為棘手,因?yàn)閷?duì)于所有非函數(shù)的引用類型,typeof 返回 object。為了更方便地鑒別引用類型,可以使用 JavaScript 的 instanceof 操作符。

var items = [];
var obj = {};
function reflect(value){
    return value;
}

console.log(items instanceof Array); // true;
console.log(obj instanceof Object); // true;
console.log(reflect instanceof Function); // true;

instanceof 操作符可鑒別繼承類型。這意味著所有對(duì)象都是 Oject 的實(shí)例,因?yàn)樗幸妙愋投祭^承自 Object。

雖然 instanceof 可以鑒別對(duì)象類型(如數(shù)組),但是有一個(gè)列外。JavaScript 的值可以在同一個(gè)網(wǎng)頁的不用框架之間傳來傳去。由于每個(gè)網(wǎng)頁擁有它自己的全局上下文——Object、Array以及其他內(nèi)建類型的版本。所以當(dāng)你把一個(gè)對(duì)象(如數(shù)組)從一個(gè)框架傳到另外一個(gè)框架時(shí),instanceof就無法識(shí)別它。

1.8 原始封裝類型

原始封裝類型有 3 種:String、Number 和 Boolean。
當(dāng)讀取字符串、數(shù)字或布爾值時(shí),原始封裝類型將被自動(dòng)創(chuàng)建。

var name = "Nicholas";
var firstChar = name.charAt(0); // "N"

這在背后發(fā)生的事情如下:

var name = "Nichola";
var temp = new String(name);
var firstChar = temp.charAt(0);
temp = null;

由于第二行把字符串當(dāng)成對(duì)象使用,JavaScript引擎創(chuàng)建了一個(gè)字符串的實(shí)體讓 charAt(0) 可以工作。字符串對(duì)象的存在僅用于該語句并在隨后銷毀(一種被稱為自動(dòng)打包的過程)。為了測試這一點(diǎn),試著給字符串添加一個(gè)屬性看看它是不是對(duì)象。

var name = "Nicholas";
name.last = "Zakas";

console.log(name.last); // undefined;

下面是在JavaScript引擎中實(shí)際發(fā)生的事情:

var name = "Nicholas";
var temp = new String(name);
temp.last = "Zakas";
temp = null; // temporary object destroyed

var temp = new String(name);
console.log(temp.last);
temp = null;

新屬性 last 實(shí)際上是在一個(gè)立刻就被銷毀的臨時(shí)對(duì)象上而不是字符串上添加。之后當(dāng)你試圖訪問該屬性時(shí),另一個(gè)不同的臨時(shí)對(duì)象被創(chuàng)建,而新屬性并不存在。

雖然原始封裝類型會(huì)被自動(dòng)創(chuàng)建,在這些值上進(jìn)行 instanceof 檢查對(duì)應(yīng)類型的返回值卻是 false。
這是因?yàn)?strong>臨時(shí)對(duì)象僅在值被讀取時(shí)創(chuàng)建。instanceof 操作符并沒有真的讀取任何東西,也就沒有臨時(shí)對(duì)象的創(chuàng)建。

當(dāng)然你也可以手動(dòng)創(chuàng)建原始封裝類型。

var str = new String("me");
str.age = 18;

console.log(typeof str); // object
console.log(str.age); // 18

如你所見,手動(dòng)創(chuàng)建原始封裝類型實(shí)際會(huì)創(chuàng)建出一個(gè) object。這意味著 typeof 無法鑒別出你實(shí)際保存的數(shù)據(jù)的類型。

另外,手動(dòng)創(chuàng)建原始封裝類型和使用原始值是有一定區(qū)別的。所以盡量避免使用。

var found = new Boolean(false);
if(found){
    console.log("Found"); // 執(zhí)行到了,盡管對(duì)象的值為 false
}

這是因?yàn)橐粋€(gè)對(duì)象(如 {} )在條件判斷語句中總被認(rèn)為是 true;

MDN:Any object whose value is not undefined or null, including a Boolean oject whose value is false, evaluates to true when passed to a conditional statement.

1.9 總結(jié)

第一章的東西都是我們一些比較熟悉的知識(shí)。但是也有一些需要注意的地方:

正確區(qū)分原始類型和引用類型

對(duì)于 5 種原始類型都可以用typeof來鑒別,而空類型必須直接跟 null 進(jìn)行全等比較。

函數(shù)也是對(duì)象,可用 typeof 鑒別。其它引用類型,可用 instanceof 和一個(gè)構(gòu)造函數(shù)來鑒別。(當(dāng)然可以用 Object.prototype.toString.call() 鑒別,它會(huì)返回[object Array]之類的)。

為了讓原始類型看上去更像引用類型,JavaScript提供了 3 種封裝類型。JavaScript會(huì)在背后創(chuàng)建這些對(duì)象使得你能夠像使用普通對(duì)象那樣使用原始值。但這些臨時(shí)對(duì)象在使用它們的語句結(jié)束時(shí)就立刻被銷毀。雖然可手動(dòng)創(chuàng)建,但不建議。

2. 函數(shù)

函數(shù)也是對(duì)象,使對(duì)象不同于其它對(duì)象的決定性特點(diǎn)是函數(shù)存在一個(gè)被稱為 [[Call]] 的內(nèi)部屬性。
內(nèi)部屬性無法通過代碼訪問而是定義了代碼執(zhí)行時(shí)的行為。ECMAScript為JavaScript的對(duì)象定義了多種內(nèi)部屬性,這些內(nèi)部屬性都用雙重中括號(hào)來標(biāo)注。

[[Call]]屬性是函數(shù)獨(dú)有的,表明該對(duì)象可以被執(zhí)行。由于僅函數(shù)擁有該屬性,ECMAScript 定義typeof操作符對(duì)任何具有[[Call]]屬性的對(duì)象返回"function"。過去因某些瀏覽器曾在正則表達(dá)式中包含 [[Call]] 屬性,導(dǎo)致正則表達(dá)式被錯(cuò)誤鑒別為函數(shù)。

2.1 聲明還是表達(dá)式

兩者的一個(gè)重要區(qū)別是:函數(shù)聲明會(huì)被提升至上下文(要么是該函數(shù)被聲明時(shí)所在的函數(shù)范圍,要么是全局范圍)的頂部。

2.2 函數(shù)就是值

可以像使用對(duì)象一樣使用函數(shù)(因?yàn)楹瘮?shù)本來就是對(duì)象,F(xiàn)unction構(gòu)造函數(shù)更加容易說明)。

2.3 參數(shù)

函數(shù)參數(shù)保存在類數(shù)組對(duì)象 argumentArray.isArray(arguments) 返回 false)中??梢越邮杖我鈹?shù)量的參數(shù)。
函數(shù)的 length 屬性表明其期望的參數(shù)個(gè)數(shù)。

2.4 重載

大多數(shù)面向?qū)ο笳Z言支持函數(shù)重載,它能讓一個(gè)函數(shù)具有多個(gè)簽名。函數(shù)簽名由函數(shù)的名字、參數(shù)的個(gè)數(shù)及其類型組成。
而JavaScript可以接收任意數(shù)量的參數(shù)且參數(shù)類型完全沒有限制。這說明JavaScript函數(shù)根本就沒有簽名,因此也不存在重載。

function sayMessage(message){
    console.log(message);
}
function sayMessage(){
    console.log("Default Message");
}

sayMessage("Hello!"); // 輸出"Default Message";

在Javscript里,當(dāng)你試圖定義多個(gè)同名的函數(shù)時(shí),只有最后的定義有效,之前的函數(shù)聲明被完全刪除(函數(shù)也是對(duì)象,變量只是存指針)。

var sayMessage = new Function("message", "console.log(message)");
var sayMessage = new Function("console.log("Default Message");");

sayMessage("Hello!"); 

當(dāng)然,你可以根據(jù)傳入?yún)?shù)的數(shù)量來模仿重載。

2.5 對(duì)象方法

對(duì)象的值是函數(shù),則該屬性被稱為方法。

2.5.1 this對(duì)象

JavaScript 所有的函數(shù)作用域內(nèi)都有一個(gè) this 對(duì)象代表調(diào)用該函數(shù)的對(duì)象。在全局作用域中,this 代表全局對(duì)象(瀏覽器里的window)。當(dāng)一個(gè)函數(shù)作為對(duì)象的方法調(diào)用時(shí),默認(rèn) this 的值等于該對(duì)象。
this在函數(shù)調(diào)用時(shí)才被設(shè)置。

function sayNameForAll(){
    console.log(this.name);
}

var person1 = {
    name: "Nicholas",
    sayName: sayNameForAll
}

var name = "Jack";

person1.sayName(); // 輸出 "Nicholas"
sayNameforAll(); // 輸出 "Jack"
2.5.2 改變this

3 種函數(shù)方法運(yùn)行你改變 this 值。

fun.call(thisArg[, arg1[, arg2[, ...]]]);

fun.apply(thisArg, [argsArray]);

fun.bind(thisArg[, arg1[, arg2[, ...]]])

使用 callapply 方法,就不需要將函數(shù)加入每個(gè)對(duì)象——你顯示地指定了 this 的值而不是讓JavaScript引擎自動(dòng)指定。

callapply 的不同地方是,call 需要把所有參數(shù)一個(gè)個(gè)列出來,而 apply 的參數(shù)需要一個(gè)數(shù)組或者類似數(shù)組的對(duì)象(如 arguments 對(duì)象)。

bind 是ECMAScript 5 新增的,它會(huì)創(chuàng)建一個(gè)新函數(shù)返回。其參數(shù)與 call 類似,而且其所有參數(shù)代表需要被永久設(shè)置在新函數(shù)中的命名參數(shù)(綁定了的參數(shù)(沒綁定的參數(shù)依然可以傳入),就算調(diào)用時(shí)再傳入其它參數(shù),也不會(huì)影響這些綁定的參數(shù))。

function sayNameForAll(label){
    console.log(label + ":" + this.name);
}
var person = {
    name: "Nicholas"
}

var sayNameForPerson = sayNameForAll.bind(person);
sayNameForPerson("Person"); // 輸出"Person:Nicholas"

var sayName = sayNameForAll.bind(person, "Jc");

sayName("change"); // 輸出"Jc:Nicholas" 因?yàn)榻壎ǖ男螀?,?huì)忽略調(diào)用時(shí)再傳入?yún)?shù)
2.6 總結(jié)

函數(shù)也是對(duì)象,所以它可以被訪問、復(fù)制和覆蓋。

函數(shù)與其他對(duì)象最大的區(qū)別在于它們有一個(gè)特殊的內(nèi)部屬性 [[Call]],包含了該函數(shù)的執(zhí)行指令。

函數(shù)聲明會(huì)被提升至上下文的頂部。

函數(shù)是對(duì)象,所以存在一個(gè) Function 構(gòu)造函數(shù)。但這會(huì)使你的代碼難以理解和調(diào)試,除非函數(shù)的真實(shí)形式要直到運(yùn)行時(shí)才能確定的時(shí)候才會(huì)利用它。

理解對(duì)象

JavaScript中的對(duì)象是動(dòng)態(tài)的,可在代碼執(zhí)行的任意時(shí)刻發(fā)生改變?;陬惖恼Z言會(huì)根據(jù)類的定義鎖定對(duì)象。

3.1 定義屬性

當(dāng)一個(gè)屬性第一次被添加到對(duì)象時(shí),JavaScript會(huì)在對(duì)象上調(diào)用一個(gè)名為 [[Put]] 的內(nèi)部方法。[[Put]] 方法會(huì)在對(duì)象上創(chuàng)建一個(gè)新節(jié)點(diǎn)來保存屬性。
當(dāng)一個(gè)已有的屬性被賦予一個(gè)新值時(shí),調(diào)用的是一個(gè)名為 [[Set]] 的方法。

3.2 屬性探測

檢查對(duì)象是否已有一個(gè)屬性。JavaScript開發(fā)新手錯(cuò)誤地使用以下模式檢測屬性是否存在。

if(person.age){
    // do something with ag
}

上面的問題在于JavaScript的類型強(qiáng)制會(huì)影響該模式的輸出結(jié)果。
當(dāng)if判斷中的值如下時(shí),會(huì)判斷為

對(duì)象

非空字符串

非零

true

當(dāng)if判斷中的值如下時(shí),會(huì)判斷為

null

undefined

0

false

NaN

空字符串

因此判斷屬性是否存在的方法是使用 in 操作符。
in 操作符會(huì)檢查自有屬性和原型屬性。
所有的對(duì)象都擁有的 hasOwnProperty() 方法(其實(shí)是 Object.prototype 原型對(duì)象的),該方法在給定的屬性存在且為自有屬性時(shí)返回 true。

var person = {
    name: "Nicholas"
}

console.log("name" in person); // true
console.log(person.hasOwnpropert("name")); // true

console.log("toString" in person); // true
console.log(person.hasOwnproperty("toString")); // false
3.3 刪除屬性

設(shè)置一個(gè)屬性的值為 null 并不能從對(duì)象中徹底移除那個(gè)屬性,這只是調(diào)用 [[Set]]null 值替換了該屬性原來的值而已。
delete 操作符針對(duì)單個(gè)對(duì)象屬性調(diào)用名為 [[Delete]] 的內(nèi)部方法。刪除成功時(shí),返回 true。

var person = {
    name: "Nicholas"
}

person.name = null;
console.log("name" in person); // true
delete person.name;
console.log(person.name); // undefined 訪問一個(gè)不存在的屬性將返回 undefined
console.log("name" in person); // false
3.4 屬性枚舉

所有人為添加的屬性默認(rèn)都是可枚舉的。可枚舉的內(nèi)部特征 [[Enumerable]] 都被設(shè)置為 true。
for-in 循環(huán)會(huì)枚舉一個(gè)對(duì)象所有的可枚舉屬性。

我的備注:在Chrome中,對(duì)象屬性會(huì)按ASCII表排序,而不是定義時(shí)的順序。

ECMAScript 5 的 Object() 方法可以獲取可枚舉屬性的名字的數(shù)組。

var person = {
    name: "Ljc",
    age: 18
}

Object.keys(person); // ["name", "age"];

for-inObject.keys() 的一個(gè)區(qū)別是:前者也會(huì)遍歷原型屬性,而后者返回自有(實(shí)例)屬性。

實(shí)際上,對(duì)象的大部分原生方法的 [[Enumerable]] 特征都被設(shè)置為 false??捎?propertyIsEnumerable() 方法檢查一個(gè)屬性是否為可枚舉的。

var arr = ["abc", 2];
console.log(arr.propertyIsEnumerable("length")); // false
3.5 屬性類型

屬性有兩種類型:數(shù)據(jù)屬性訪問器屬性
數(shù)據(jù)屬性包含一個(gè)值。[[Put]] 方法的默認(rèn)行為是創(chuàng)建數(shù)據(jù)屬性。
訪問器屬性不包含值而是定義了一個(gè)當(dāng)屬性被讀取時(shí)調(diào)用的函數(shù)(稱為getter)和一個(gè)當(dāng)屬性被寫入時(shí)調(diào)用的函數(shù)(稱為setter)。訪問器屬性僅需要 gettersetter 兩者中的任意一個(gè),當(dāng)然也可以兩者。

// 對(duì)象字面形式中定義訪問器屬性有特殊的語法:
var person = {
    _name: "Nicholas",
    
    get name(){
        console.log("Reading name");
        return this._name;
    },
    set name(value){
        console.log("Setting name to %s", value);
        this._name = value;
    }
};

console.log(person.name); // "Reading name" 然后輸出 "Nicholas"

person.name = "Greg";
console.log(person.name); // "Setting name to Greg" 然后輸出 "Greg"

前置下劃線_ 是一個(gè)約定俗成的命名規(guī)范,表示該屬性是私有的,實(shí)際上它還是公開的。

訪問器就是定義了我們在對(duì)象讀取或設(shè)置屬性時(shí),觸發(fā)的動(dòng)作(函數(shù)),_name 相當(dāng)于一個(gè)內(nèi)部變量。
當(dāng)你希望賦值(讀?。┎僮鲿?huì)觸發(fā)一些行為,訪問器就會(huì)非常有用。

當(dāng)只定義getter或setter其一時(shí),該屬性就會(huì)變成只讀或只寫。

3.6 屬性特征

在ECMAScript 5 之前沒有辦法指定一個(gè)屬性是否可枚舉。實(shí)際上根本沒有方法訪問屬性的任何內(nèi)部特征。為了改變這點(diǎn),ECMAScript 5引入了多種方法來和屬性特征值直接互動(dòng)。

3.6.1 通用特征

數(shù)據(jù)屬性和訪問器屬性均由以下兩個(gè)屬性特制:
[[Enumerable]] 決定了是否可以遍歷該屬性;
[[Configurable]] 決定了該屬性是否可配置。

所有人為定義的屬性默認(rèn)都是可枚舉、可配置的。

可以用 Object.defineProperty() 方法改變屬性特征。
其參數(shù)有三:擁有該屬性的對(duì)象、屬性名和包含需要設(shè)置的特性的屬性描述對(duì)象。

var person = {
    name: "Nicholas"
}
Object.defineProperty(person, "name", {
    enumerable: false
})

console.log("name" in person); // true
console.log(person.propertyIsEnumerable("name")); // false

var properties = Object.keys(person);
console.log(properties.length); // 0

Object.defineProperty(person, "name",{
    configurable: false
})

delete person.name; // false
console.log("name" in person); // true

Object.defineProperty(person, "name",{ // error! 
// 在 chrome:Uncaught TypeError: Cannot redefine property: name
    configurable: true
})

無法將一個(gè)不可配置的屬性變?yōu)榭膳渲?,相反則可以。

3.6.2 數(shù)據(jù)屬性特征

數(shù)據(jù)屬性額外擁有兩個(gè)訪問器屬性不具備的特征。
[[Value]] 包含屬性的值(哪怕是函數(shù))。
[[Writable]] 布爾值,指示該屬性是否可寫入。所有屬性默認(rèn)都是可寫的。

var person = {};

Object.defineProperty(person, "name", {
    value: "Nicholas",
    enumerable: true,
    configurable: true,
    writable: true
})

Object.defineProperty() 被調(diào)用時(shí),如果屬性本來就有,則會(huì)按照新定義屬性特征值去覆蓋默認(rèn)屬性特征(enumberable、configurablewritable 均為 true)。但如果用該方法定義新的屬性時(shí),沒有為所有的特征值指定一個(gè)值,則所有布爾值的特征值會(huì)被默認(rèn)設(shè)置為 false。即不可枚舉、不可配置、不可寫的。
當(dāng)你用 Object.defineProperty() 改變一個(gè)已有的屬性時(shí),只有你指定的特征會(huì)被改變。

3.6.3 訪問器屬性特征

訪問器屬性額外擁有兩個(gè)特征。[[Get]][[Set]],內(nèi)含 gettersetter 函數(shù)。
使用訪問其屬性特征比使用對(duì)象字面形式定義訪問器屬性的優(yōu)勢在于:可以為已有的對(duì)象定義這些屬性。而后者只能在創(chuàng)建時(shí)定義訪問器屬性。

var person = {
    _name: "Nicholas"
};

Object.defineProperty(person, "name", {
    get: function(){
        return this._name;
    },
    set: function(value){
        this._name = value;
    },
    enumerable: true,
    configurable: true
})

for(var x in person){
    console.log(x); // _name 
(換行) name(訪問器屬性)
}

設(shè)置一個(gè)不可配置、不可枚舉、不可以寫的屬性:

Object.defineProperty(person, "name",{
    get: function(){
        return this._name;
    }
})

對(duì)于一個(gè)新的訪問器屬性,沒有顯示設(shè)置值為布爾值的屬性,默認(rèn)為 false。

3.6.4 定義多重屬性

Object.defineProperties() 方法可以定義任意數(shù)量的屬性,甚至可以同時(shí)改變已有的屬性并創(chuàng)建新屬性。

var person = {};

Object.defineProperties(person, {
    
    // data property to store data
    _name: {
        value: "Nicholas",
        enumerable: true,
        configurable: true,
        writable: true
    },
    
    // accessor property
    name: {
        get: function(){
            return this._name;
        },
        set: function(value){
            this._name = value;
        }
    }
})
3.6.5 獲取屬性特征

Object.getOwnPropertyDescriptor() 方法。該方法接受兩個(gè)參數(shù):對(duì)象和屬性名。如果屬性存在,它會(huì)返回一個(gè)屬性描述對(duì)象,內(nèi)涵4個(gè)屬性:configurableenumerable,另外兩個(gè)屬性則根據(jù)屬性類型決定。

var person = {
    name: "Nicholas"
}

var descriptor = Object.getOwnPropertyDescriptor(person, "name");

console.log(descriptor.enumerable); // true
console.log(descriptor.configuable); // true
console.log(descriptor.value); // "Nicholas"
console.log(descriptor.wirtable); // true
3.7 禁止修改對(duì)象

對(duì)象和屬性一樣具有指導(dǎo)其行為的內(nèi)部特性。其中, [[Extensible]] 是布爾值,指明該對(duì)象本身是否可以被修改。默認(rèn)是 true。當(dāng)值為 false 時(shí),就能禁止新屬性的添加。

建議在 "use strict"; 嚴(yán)格模式下進(jìn)行。

3.7.1 禁止擴(kuò)展

Object.preventExtensions() 創(chuàng)建一個(gè)不可擴(kuò)展的對(duì)象(即不能添加新屬性)。
Object.isExtensible() 檢查 [[Extensible]] 的值。

var person = {
    name: "Nocholas"
}

Object.preventExtensions(person);

person.sayName = function(){
    console.log(this.name)
}

console.log("sayName" in person); // false
3.7.2 對(duì)象封印

一個(gè)被封印的對(duì)象是不可擴(kuò)展的且其所有屬性都是不可配置的(即不能添加、刪除屬性或修改其屬性類型(從數(shù)據(jù)屬性變成訪問器屬性或相反))。只能讀寫它的屬性。
Object.seal()。調(diào)用此方法后,該對(duì)象的 [[Extensible]] 特征被設(shè)置為 false,其所有屬性的 [[configurable]] 特征被設(shè)置為 false。
Object.isSealed() 判斷一個(gè)對(duì)象是否被封印。

3.7.3 對(duì)象凍結(jié)

被凍結(jié)的對(duì)象不能添加或刪除屬性,不能修改屬性類型,也不能寫入任何數(shù)據(jù)屬性。簡言而之,被凍結(jié)對(duì)象是一個(gè)數(shù)據(jù)屬性都為只讀的被封印對(duì)象。
Object.freeze() 凍結(jié)對(duì)象。
Object.isFrozen() 判斷對(duì)象是否被凍結(jié)。

3.8 總結(jié)

in 操作符檢測自有屬性和原型屬性,而 hasOwnProperty() 只檢查自有屬性。

delete 操作符刪除對(duì)象屬性。

屬性有兩種類型:數(shù)據(jù)屬性和訪問器屬性。

所有屬性都有一些相關(guān)特征。[[Enumerable]][[Configurable]] 的兩種屬性都有的,而數(shù)據(jù)屬性還有 [[Value]][[Writable]],訪問器屬性還有 [[Get]][[Set]]??赏ㄟ^ Object.defineProperty()Object.defineProperties() 改變這些特征。用 Object.getOwnPropertyDescriptor() 獲取它們。

3 種可以鎖定對(duì)象屬性的方式。

4. 構(gòu)造函數(shù)和原型對(duì)象

由于JavaScript(ES5)缺乏類,但可用構(gòu)造函數(shù)和原型對(duì)象給對(duì)象帶來與類相似的功能。

4.1 構(gòu)造函數(shù)

構(gòu)造函數(shù)的函數(shù)名首字母應(yīng)大寫,以此區(qū)分其他函數(shù)。
當(dāng)沒有需要給構(gòu)造函數(shù)傳遞參數(shù),可忽略小括號(hào):

var Person = {
    // 故意留空
}
var person = new Person;

盡管 Person 構(gòu)造函數(shù)沒有顯式返回任何東西,但 new 操作符會(huì)自動(dòng)創(chuàng)建給定類型的對(duì)象并返回它們。

每個(gè)對(duì)象在創(chuàng)建時(shí)都自動(dòng)擁有一個(gè)構(gòu)造函數(shù)屬性(constructor,其實(shí)是它們的原型對(duì)象上的屬性),其中包含了一個(gè)指向其構(gòu)造函數(shù)的引用。
通過對(duì)象字面量形式({})或Object構(gòu)造函數(shù)創(chuàng)建出來的泛用對(duì)象,其構(gòu)造函數(shù)屬性(constructor)指向 Object;而那些通過自定義構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象,其構(gòu)造函數(shù)屬性指向創(chuàng)建它的構(gòu)造函數(shù)。

console.log(person.constructor === Person); // true
console.log(({}).constructor === Object); // true
console.log(([1,2,3]).constructor === Object); // true

// 證明 constructor是在原型對(duì)象上
console.log(person.hasOwnPrototype("constructor")); // false
console.log(person.constructor.prototype.hasOwnPrototype("constructor")); // true

盡管對(duì)象實(shí)例及其構(gòu)造函數(shù)之間存在這樣的關(guān)系,但還是建議使用 instanceof 來檢查對(duì)象類型。這是因?yàn)闃?gòu)造函數(shù)屬性可以被覆蓋。(person.constructor = "")。

當(dāng)你調(diào)用構(gòu)造函數(shù)時(shí),new 會(huì)自動(dòng)自動(dòng)創(chuàng)建 this 對(duì)象,且其類型就是構(gòu)造函數(shù)的類型(構(gòu)造函數(shù)就好像類,相當(dāng)于一種數(shù)據(jù)類型)。

你也可以在構(gòu)造函數(shù)中顯式調(diào)用 return。如果返回值是一個(gè)對(duì)象,它會(huì)代替新創(chuàng)建的對(duì)象實(shí)例而返回,如果返回值是一個(gè)原始類型,它會(huì)被忽略,新創(chuàng)建的對(duì)象實(shí)例會(huì)被返回。

始終確保要用 new 調(diào)用構(gòu)造函數(shù);否則,你就是在冒著改變?nèi)謱?duì)象的風(fēng)險(xiǎn),而不是創(chuàng)建一個(gè)新的對(duì)象。

var person = Person("Nicholas"); // 缺少 new

console.log(person instanceof Person); // false
console.log(person); // undefined,因?yàn)闆]用 new,就相當(dāng)于一個(gè)普通函數(shù),默認(rèn)返回 undefined
console.log(name); // "Nicholas"

當(dāng)Person不是被 new 調(diào)用時(shí),構(gòu)造函數(shù)中的 this 對(duì)象等于全局 this 對(duì)象。

在嚴(yán)格模式下,會(huì)報(bào)錯(cuò)。因?yàn)閲?yán)格模式下,并沒有為全局對(duì)象設(shè)置 this,this 保持為 undefined。

以下代碼,通過 new 實(shí)例化 100 個(gè)對(duì)象,則會(huì)有 100 個(gè)函數(shù)做相同的事。因此可用 prototype 共享同一個(gè)方法會(huì)更高效。

var person = {
    name: "Nicholas",
    sayName: function(){
        console.log(this.name);
    }
}
4.2 原型對(duì)象

可以把原型對(duì)象看作是對(duì)象的基類。幾乎所有的函數(shù)(除了一些內(nèi)建函數(shù))都有一個(gè)名為 prototype 的屬性,該屬性是一個(gè)原型對(duì)象用來創(chuàng)建新的對(duì)象實(shí)例。所有創(chuàng)建的對(duì)象實(shí)例(同一構(gòu)造函數(shù),當(dāng)然,可能訪問上層的原型對(duì)象)共享該原型對(duì)象,且這些對(duì)象實(shí)例可以訪問原型對(duì)象的屬性。例如,hasOwnProperty()定義在 Object 的原型對(duì)象中,但卻可被任何對(duì)象當(dāng)作自己的屬性訪問。

var book = {
    title : "book_name"
}

"hasOwnProperty" in book; // true
book.hasOwnProperty("hasOwnProperty"); // false
Object.property.hasOwnProperty("hasOwnProperty"); // true

鑒別一個(gè)原型屬性

function hasPrototypeProperty(object, name){
    return name in object && !object.hasOwnProperty(name);
}
4.2.1 [[Prototype]] 屬性

一個(gè)對(duì)象實(shí)例通過內(nèi)部屬性 [[Prototype]] 跟蹤其原型對(duì)象。該屬性是一個(gè)指向該實(shí)例使用的原型對(duì)象的指針。當(dāng)你用 new 創(chuàng)建一個(gè)新的對(duì)象時(shí),構(gòu)造函數(shù)的原型對(duì)象就會(huì)被賦給該對(duì)象的 [[Prototype]] 屬性。

由上圖可以看出,[[Prototype]] 屬性是如何讓多個(gè)對(duì)象實(shí)例引用同一個(gè)原型對(duì)象來減少重復(fù)代碼。

Object.getPrototypeOf() 方法可讀取 [[Prototype]] 屬性的值。

var obj = {};
var prototype = Object.getPrototypeOf(Object);

console.log(prototype === Object.prototype); // true

大部分JavaScript引擎在所有對(duì)象上都支持一個(gè)名為 _proto_ 的屬性。該屬性使你可以直接讀寫 [[Prototype]] 屬性。

isPrototypeOf() 方法會(huì)檢查某個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型對(duì)象,該方法包含在所有對(duì)象中。

var obj = {}
console.log(Object.prototype.isPrototypeOf(obj)); // true

當(dāng)讀取一個(gè)對(duì)象的屬性時(shí),JavaScript 引擎首先在該對(duì)象的自有屬性查找屬性名。如果找到則返回。否則會(huì)搜索 [[Prototype]] 中的對(duì)象,找到則返回,找不到則返回 undefined。

var obj = new Object();
console.log(obj.toString()); // "[object Object]"

obj.toString = function(){
    return "[object Custom]";
}
console.log(obj.toString()); // "[object Custom]"

delete obj.toString; // true
console.log(obj.toString()); // "[object Object]"

delete obj.toString; // 無效,delete不能刪除一個(gè)對(duì)象從原型繼承而來的屬性
cconsole.log(obj.toString()); // // "[object Object]"

MDN:delete 操作符不能刪除的屬性有:①顯式聲明的全局變量不能被刪除,該屬性不可配置(not configurable); ②內(nèi)置對(duì)象的內(nèi)置屬性不能被刪除; ③不能刪除一個(gè)對(duì)象從原型繼承而來的屬性(不過你可以從原型上直接刪掉它)。

一個(gè)重要概念:無法給一個(gè)對(duì)象的原型屬性賦值。我認(rèn)為是無法直接添加吧,在chrome和Edge中,都無法讀取_proto_屬性,但我們可以通過 obj.constructor.prototype.sayHi = function(){console.log("Hi!")} 向原型對(duì)象添加屬性。


(圖片中間可以看出,為對(duì)象obj添加的toString屬性代替了原型屬性)

4.2.2 在構(gòu)造函數(shù)中使用原型對(duì)象
在原型對(duì)象上定義公用方法
在原型對(duì)象上定義數(shù)據(jù)類型

開發(fā)中需要注意原型對(duì)象的數(shù)據(jù)是否共享。

function Person(name){
    this.name = name
}

Person.prototype.sayName = function(){
    console.log(this.name);
}

Person.prototype.position = "school";
Person.prototype.arr = [];

var person1 = new Person("xiaoming");
var person2 = new Person("Jc");

console.log("原始類型")
console.log(person1.position); // "school"
console.log(person2.position); // "school"

person1.position = 2; // 這是在當(dāng)前屬性設(shè)置position,引用類型同理
console.log(person1.hasOwnProperty("position")); // true
console.log(person2.hasOwnProperty("position")); // false

console.log("引用類型");
person1.arr.push("pizza"); // 這是在原型對(duì)象上設(shè)置,而不是直接在對(duì)象上
person2.arr.push("quinoa"); // 這是在原型對(duì)象上設(shè)置
console.log(person1.hasOwnProperty("arr")); // false
console.log(person2.hasOwnProperty("arr")); // false
console.log(person1.arr); // ["pizza", "quinoa"]
console.log(person2.arr); // ["pizza", "quinoa"]

上面是在原型對(duì)象上一一添加屬性,下面一種更簡潔的方式:以一個(gè)對(duì)象字面形式替換原型對(duì)象

function Person(name){
    this.name
}

Person.prototype = {
    sayName: function(){
        console.log(this.name);
    },
    toString: function(){
        return "[Person ]" + this.name + "]";
    }
}

這種方式有一種副作用:因?yàn)樵蛯?duì)象上具有一個(gè) constructor 屬性,這是其他對(duì)象實(shí)例所沒有的。當(dāng)一個(gè)函數(shù)被創(chuàng)建時(shí),它的 prototype 屬性也會(huì)被創(chuàng)建,且該原型對(duì)象的 constructor 屬性指向該函數(shù)。當(dāng)使用字面量時(shí),因沒顯式設(shè)置原型對(duì)象的 constructor 屬性,因此其 constructor 屬性是指向 Object 的。
因此,當(dāng)通過此方式設(shè)置原型對(duì)象時(shí),可手動(dòng)設(shè)置 constructor 屬性。

function Person(name){
    this.name
}

// 建議第一個(gè)屬性就是設(shè)置其 constructor 屬性。
Person.prototype = {
    constructor: Person,

    sayName: function(){
        console.log(this.name);
    },
    toString: function(){
        return "[Person ]" + this.name + "]";
    }
}

構(gòu)造函數(shù)、原型對(duì)象和對(duì)象實(shí)例之間的關(guān)系最有趣的一方面也許是:
對(duì)象實(shí)例和構(gòu)造函數(shù)直接沒有直接聯(lián)系。(對(duì)象實(shí)例只有 [[Prototype]] 屬性(自己測試時(shí)不能讀?。?b>_proto_
))指向其相應(yīng)的原型對(duì)象,而原型對(duì)象的 constructor 屬性指向構(gòu)造函數(shù),而構(gòu)造函數(shù)的 prototype 指向原型對(duì)象)

4.2.3 改變原型對(duì)象

因?yàn)槊總€(gè)對(duì)象的 [[Prototype]] 只是一個(gè)指向原型對(duì)象的指針,所以原型對(duì)象的改動(dòng)會(huì)立刻反映到所有引用它的對(duì)象。
當(dāng)對(duì)一個(gè)對(duì)象使用封印 Object.seal() 或凍結(jié) Object.freeze() 時(shí),完全是在操作對(duì)象的自有屬性,但任然可以通過在原型對(duì)象上添加屬性來擴(kuò)展這些對(duì)象實(shí)例。

4.2.4 內(nèi)建對(duì)象(如Array、String)的原型對(duì)象
String.prototype.capitalize = function(){
    return this.charAt(0).toUpperCase() + this.substring(1);
}
總結(jié)

構(gòu)造函數(shù)就是用 new 操作符調(diào)用的普通函數(shù)??捎眠^ instanceof 操作符或直接訪問 constructor(實(shí)際上是原型對(duì)象的屬性) 來鑒別對(duì)象是被哪個(gè)構(gòu)造函數(shù)所創(chuàng)建的。

每個(gè)函數(shù)都有一個(gè) prototype 對(duì)象,它定義了該構(gòu)造函數(shù)創(chuàng)建的所有對(duì)象共享的屬性。而 constructor 屬性實(shí)際上是定義在原型對(duì)象里,供所有對(duì)象實(shí)例共享。

每個(gè)對(duì)象實(shí)例都有 [[Prototype]] 屬性,它是指向原型對(duì)象的指針。當(dāng)訪問對(duì)象的某個(gè)屬性時(shí),先從對(duì)象自身查找,找不到的話就到原型對(duì)象上找。

內(nèi)建對(duì)象的原型對(duì)象也可被修改

5. 繼承 5.1 原型對(duì)象鏈和 Object.prototype

JavaScript內(nèi)建的繼承方法被稱為 原型對(duì)象鏈(又叫原型對(duì)象繼承)。
原型對(duì)象的屬性可經(jīng)由對(duì)象實(shí)例訪問,這就是繼承的一種形式。對(duì)象實(shí)例繼承了原型對(duì)象的屬性,而原型對(duì)象也是一個(gè)對(duì)象,它也有自己的原型對(duì)象并繼承其屬性,以此類推。這就是原型對(duì)象鏈。

所有對(duì)象(包括自義定的)都自動(dòng)繼承自 Object,除非你另有指定。更確切地說,所有對(duì)象都繼承自 Object.prototype。任何以對(duì)象字面量形式定義的對(duì)象,其 [[Prototype]] 的值都被設(shè)為 Object.prototype,這意味著它繼承 Object.prototype 的屬性。

5.1.1 繼承自 Object.prototype 的方法

Object.prototype 一般有以下幾個(gè)方法

hasOwnProperty() 檢測是否存在一個(gè)給定名字的自有屬性

propertyIsemumerable() 檢查一個(gè)自有屬性是否可枚舉

isPrototypeOf 檢查一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型對(duì)象

valueOf() 返回一個(gè)對(duì)象的值表達(dá)

toString() 返回一個(gè)對(duì)象的字符串表達(dá)

這 5 種方法經(jīng)由繼承出現(xiàn)在所有對(duì)象中。
因?yàn)樗袑?duì)象都默認(rèn)繼承自 Object.prototype,所以改變它就會(huì)影響所有的對(duì)象。所以不建議。

5.2 繼承

對(duì)象繼承是最簡單的繼承類型。你唯需要做的是指定哪個(gè)對(duì)象是新對(duì)象的 [[Prototype]]。對(duì)象字面量形式會(huì)隱式指定 Object.prototype 為其 [[Protoype]]。當(dāng)然我們可以用 ES5 的 Object.create() 方法顯式指定。該方法接受兩個(gè)參數(shù),第一個(gè)是新對(duì)象的的 [[Prototype]] 所指向的對(duì)象。第二個(gè)參數(shù)是可選的一個(gè)屬性描述對(duì)象,其格式與 Object.definePrototies()一樣。

var obj = {
    name: "Ljc"
};

// 等同于
var obj = Object.create(Object.prototype, {
    name: {
        value: "Ljc",
        configurable: true,
        enumberable: true,
        writable: true
    }
});

下面是繼承其它對(duì)象:

var person = {
    name: "Jack",
    sayName: function(){
        console.log(this.name);
    }
}

var student = Object.create(person, {
    name:{
        value: "Ljc"
    },
    grade: {
        value: "fourth year of university",
        enumerable: true,
        configurable: true,
        writable: true
    }
});

person.sayName(); // "Jack"
student.sayName(); // "Ljc"

console.log(person.hasOwnProperty("sayName")); // true
console.log(person.isPrototypeOf(student)); // true
console.log(student.hasOwnProperty("sayName")); // false
console.log("sayName" in student); // true

當(dāng)訪問一個(gè)對(duì)象屬性時(shí),JavaScript引擎會(huì)執(zhí)行一個(gè)搜索過程。如果在對(duì)象實(shí)例存在該自有屬性,則返回,否則,根據(jù)其私有屬性 [[Protoype]] 所指向的原型對(duì)象進(jìn)行搜索,找到返回,否則繼承上述操作,知道繼承鏈末端。末端通常是 Object.prototype,其 [[Prototype]]null。

當(dāng)然,也可以用 Object.create() 常見一個(gè) [[Prototype]]null 的對(duì)象。

var obj = Object.create(null);

console.log("toString" in obj); // false

該對(duì)象是一個(gè)沒有原型對(duì)象鏈的對(duì)象,即是一個(gè)沒有預(yù)定義屬性的白板。

5.3 構(gòu)造函數(shù)繼承

JavaScript 中的對(duì)象繼承也是構(gòu)造函數(shù)繼承的基礎(chǔ)。
第四章提到,幾乎所有函數(shù)都有 prototype 屬性,它可被修改或替換。該 prototype 屬性被自動(dòng)設(shè)置為一個(gè)新的繼承自 Object.prototype 的泛用對(duì)象,該對(duì)象(原型對(duì)象)有一個(gè)自有屬性 constructor。實(shí)際上,JavaScript 引擎為你做了下面的事情。

// 你寫成這樣
function YourConstructor(){
    // initialization
}

// JavaScript引擎在背后為你做了這些處理
YourConstructor.prototype = Object.create(Object.prototype, {
    constructor: {
        configurable: true,
        enumerable: true,
        value: YourConstructor,
        writable: true
    }
})

你不需要做額外的工作,這段代碼幫你把構(gòu)造函數(shù)的 prototype 屬性設(shè)置為一個(gè)繼承自 Object.prototype 的對(duì)象。這意味著 YourConstructor 創(chuàng)建出來的任何對(duì)象都繼承自 Object.prototype。

由于 prototype 可寫,你可以通過改變它來改變原型對(duì)象鏈。

MDN:instanceof 運(yùn)算符可以用來判斷某個(gè)構(gòu)造函數(shù)的 prototype 屬性是否存在另外一個(gè)要檢測對(duì)象的原型鏈上。

function Rectangle(length, width){
    this.length = length;
    this.width = width
}

Rectangle.prototype.getArea = function(){
    return this.length * this.width
}

Rectangle.prototype.toString = function(){
    return "[Rectangle " + this.length + "x" + this.width + "]";
}
// inherits from Rectangle
function Square(size){
    this.length = size;
    this.width = size;
}

Square.prototype = new Rectangle(); // 盡管是 Square.prototype 是指向了 Rectangle 的對(duì)象實(shí)例,即Square的實(shí)例對(duì)象也能訪問該實(shí)例的屬性(如果你提前聲明了該對(duì)象,且給該對(duì)象新增屬性)。
// Square.prototype = Rectangle.prototype; // 這種實(shí)現(xiàn)沒有上面這種好,因?yàn)镾quare.prototype 指向了 Rectangle.prototype,導(dǎo)致修改Square.prototype時(shí),實(shí)際就是修改Rectangle.prototype。
console.log(Square.prototype.constructor); // 輸出 Rectangle 構(gòu)造函數(shù)

Square.prototype.constructor = Square; // 重置回 Square 構(gòu)造函數(shù)
console.log(Square.prototype.constructor); // 輸出 Square 構(gòu)造函數(shù)

Square.prototype.toString = function(){
    return "[Square " + this.length + "x" + this.width + "]";
}

var rect = new Rectangle(5, 10);
var square = new Square(6);

console.log(rect.getArea()); // 50
console.log(square.getArea()); // 36

console.log(rect.toString()); // "[Rectangle 5 * 10]", 但如果是Square.prototype = Rectangle.prototype,則這里會(huì)"[Square 5 * 10]"
console.log(square.toString()); // "[Square 6 * 6]"

console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
console.log(square instanceof Object); // true

Square.prototype 并不真的需要被改成為一個(gè) Rectangle 對(duì)象。事實(shí)上,是 Square.prototype 需要指向 Rectangle.prototype 使得繼承得以實(shí)現(xiàn)。這意味著可以用 Object.create() 簡化例子。

// inherits from Rectangle
function Square(size){
    this.length = size;
    this.width = size;
}

Square.prototype= Object.create(Rectangle.prototype, {
    constructor: {
        configurable: true,
        enumerable: true,
        value: Square,
        writable: true
    }
})

在對(duì)原型對(duì)象添加屬性前要確保你已經(jīng)改成了原型對(duì)象,否則在改寫時(shí)會(huì)丟失之前添加的方法(因?yàn)槔^承是將被繼承對(duì)象賦值給需要繼承的原型對(duì)象,相當(dāng)于重寫了需要繼承的原型對(duì)象)。

5.4 構(gòu)造函數(shù)竊取

由于JavaScript中的繼承是通過原型對(duì)象鏈來實(shí)現(xiàn)的,因此不需要調(diào)用對(duì)象的父類的構(gòu)造函數(shù)。如果確實(shí)需要在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),那就可以在子類的構(gòu)造函數(shù)中利用 call、apply方法調(diào)用父類的構(gòu)造函數(shù)。

// 在上面的代碼基礎(chǔ)上作出修改
// inherits from Rectangle
function Square(size){
    Rectangle.call(this, size, size);
    
    // optional: add new properties or override existing ones here
}

一般來說,需要修改 prototyp 來繼承方法并用構(gòu)造函數(shù)竊取來設(shè)置屬性,由于這種做法模仿了那些基于類的語言的類繼承,所以這通常被稱為偽類繼承。

5.5 訪問父類方法

其實(shí)也是通過指定 callapply 的子對(duì)象調(diào)用父類方法。

6 對(duì)象模式 6.1 私有成員和特權(quán)成員

JavaScipt 對(duì)象的所有屬性都是公有的,沒有顯式的方法指定某個(gè)屬性不能被外界訪問。

6.1.1 模塊模式

模塊模式是一種用于創(chuàng)建擁有私有數(shù)據(jù)的單件對(duì)象的模式。
基本做法是使用立即調(diào)用函數(shù)表達(dá)式(IIFE)來返回一個(gè)對(duì)象。原理是利用閉包。

var yourObj = (function(){
    // private data variables
    
    return {
        // public methods and properties
    }
}());

模塊模式還有一個(gè)變種叫暴露模塊模式,它將所有的變量和方法都放在 IIFE 的頭部,然后將它們設(shè)置到需要被返回的對(duì)象上。

//  一般寫法
var yourObj = (function(){
    var age = 25;
    
    return {
        name: "Ljc",
        
        getAge: function(){
            return agel
        }
    }
}());

// 暴露模塊模式
var yourObj = (function(){
    var age = 25;
    function getAge(){
        return agel
    };
    return {
        name: "Ljc",
        getAge: getAge
    }
}());
6.1.2 構(gòu)造函數(shù)的私有成員(不能通過對(duì)象直接訪問)

模塊模式在定義單個(gè)對(duì)象的私有屬性十分有效,但對(duì)于那些同樣需要私有屬性的自定義類型呢?你可以在構(gòu)造函數(shù)中使用類似的模式來創(chuàng)建每個(gè)實(shí)例的私有數(shù)據(jù)。

function Person(name){
    // define a variable only accessible inside of the Person constructor
    var age = 22;
    
    this.name = name;
    this.getAge = function(){
        return age;
    };
    this.growOlder = function(){
        age++;
    }
}

var person = new Person("Ljc");

console.log(person.age); // undefined
person.age = 100;
console.log(person.getAge()); // 22

person.growOlder();
console.log(person.getAge()); // 23

這里有個(gè)問題:如果你需要對(duì)象實(shí)例擁有私有數(shù)據(jù),就不能將相應(yīng)方法放在 prototype 上。

如果你需要所有實(shí)例共享私有數(shù)據(jù)。則可結(jié)合模塊模式和構(gòu)造函數(shù),如下:

var Person = (function(){
    var age = 22;

    function InnerPerson(name){
        this.name = name;
    }

    InnerPerson.prototype.getAge = function(){
        return age;
    }
    InnerPerson.prototype.growOlder = function(){
        age++;
    };

    return InnerPerson;
}());

var person1 = new Person("Nicholash");
var person2 = new Person("Greg");

console.log(person1.name); // "Nicholash"
console.log(person1.getAge()); // 22

console.log(person2.name); // "Greg"
console.log(person2.getAge()); // 22

person1.growOlder();
console.log(person1.getAge()); // 23
console.log(person2.getAge()); // 23
6.2 混入

這是一種偽繼承。一個(gè)對(duì)象在不改變原型對(duì)象鏈的情況下得到了另外一個(gè)對(duì)象的屬性被稱為“混入”。因此,和繼承不同,混入讓你在創(chuàng)建對(duì)象后無法檢查屬性來源。
純函數(shù)實(shí)現(xiàn):

function mixin(receiver, supplier){
    for(var property in supplier){
        if(supplier.hasOwnProperty(property)){
            receiver[property] = supplier[property];
        }
    }
}

這是淺拷貝,如果屬性的值是一個(gè)引用,那么兩者將指向同一個(gè)對(duì)象。

6.3 作用域安全的構(gòu)造函數(shù)

構(gòu)造函數(shù)也是函數(shù),所以不用 new 也能調(diào)用它們來改變 this 的值。在非嚴(yán)格模式下, this 被強(qiáng)制指向全局對(duì)象。而在嚴(yán)格模式下,構(gòu)造函數(shù)會(huì)拋出一個(gè)錯(cuò)誤(因?yàn)閲?yán)格模式下沒有為全局對(duì)象設(shè)置 thisthis 保持為 undefined)。
而很多內(nèi)建構(gòu)造函數(shù),例如 Array、RegExp 不需要 new 也能正常工作,這是因?yàn)樗鼈儽辉O(shè)計(jì)為作用域安全的構(gòu)造函數(shù)。
當(dāng)用 new 調(diào)用一個(gè)函數(shù)時(shí),this 指向的新創(chuàng)建的對(duì)象是屬于該構(gòu)造函數(shù)所代表的自定義類型。因此,可在函數(shù)內(nèi)用 instanceof 檢查自己是否被 new 調(diào)用。

function Person(name){
    if(this instanceof Person){
        // called with "new"
    }else{
        // called without "new"
    }
}

具體案例:

function Person(name){
    if(this instanceof Person){
        this.name = name;
    }else{
        return new Person(name);
    }
}
總結(jié)

看了兩天的書,做了兩天的筆記。當(dāng)然這只是ES5的。過幾天 ES6 新書又來了。最后感謝 異步社區(qū) 送我這本好書 《JavaScript面向?qū)ο缶?,讓我的前端根基更加穩(wěn)固,希望自己的前端之路越走越順。

對(duì)應(yīng) GitHub。

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78521.html

相關(guān)文章

  • Javascript面向對(duì)象精要讀書筆記

    摘要:面向?qū)ο缶x書筆記下面代碼的實(shí)際執(zhí)行過程是什么使用原始值和原始封裝類型是有區(qū)別的因?yàn)槭潜唤馕龀梢粋€(gè)對(duì)象的,所以肯定是真的函數(shù)是對(duì)象,函數(shù)有兩種字面形式,第一種是函數(shù)聲明,以關(guān)鍵字開頭后面跟函數(shù)名字。 Javascript面向?qū)ο缶x書筆記 1、下面代碼的實(shí)際執(zhí)行過程是什么? var name = fan var str = name.charAt(0) console.l...

    roadtogeek 評(píng)論0 收藏0
  • JavaScript面向對(duì)象精要(二)

    摘要:使用時(shí),會(huì)自動(dòng)創(chuàng)建對(duì)象,其類型為構(gòu)造函數(shù)類型,指向?qū)ο髮?shí)例缺少關(guān)鍵字,指向全局對(duì)象。構(gòu)造函數(shù)本身也具有屬性指向原型對(duì)象。 在JavaScript面向?qū)ο缶?一)中講解了一些與面向?qū)ο笙嚓P(guān)的概念和方法,這篇講講原型和繼承。 構(gòu)造函數(shù)和原型對(duì)象 構(gòu)造函數(shù)也是函數(shù),用new創(chuàng)建對(duì)象時(shí)調(diào)用的函數(shù),與普通函數(shù)的一個(gè)區(qū)別是,其首字母應(yīng)該大寫。但如果將構(gòu)造函數(shù)當(dāng)作普通函數(shù)調(diào)用(缺少new關(guān)鍵字...

    wayneli 評(píng)論0 收藏0
  • JavaScript面向對(duì)象精要(一)

    摘要:使函數(shù)不同于其他對(duì)象的決定性特性是函數(shù)存在一個(gè)被稱為的內(nèi)部屬性。其中,是一個(gè)布爾值,指明改對(duì)象本身是否可以被修改值為。注意凍結(jié)對(duì)象和封印對(duì)象均要在嚴(yán)格模式下使用。 數(shù)據(jù)類型 在JavaScript中,數(shù)據(jù)類型分為兩類: 原始類型:保存一些簡單數(shù)據(jù),如true,5等。JavaScript共有5中原始類型: boolean:布爾,值為true或false number:數(shù)字,值...

    hiYoHoo 評(píng)論0 收藏0
  • JavaScript 權(quán)威指南》讀書筆記 1 - 簡介

    摘要:原文第一章主要介紹的大概情況基本語法。通過和來引用對(duì)象屬性或數(shù)組元素的值就構(gòu)成一個(gè)表達(dá)式。 原文:https://keelii.github.io/2016/06/16/javascript-definitive-guide-note-0/ 第一章 主要介紹 JavaScript 的大概情況、基本語法。之前沒有 JavaScript 基礎(chǔ)的看不懂也沒關(guān)系,后續(xù)章節(jié)會(huì)有進(jìn)一步的詳細(xì)說明...

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

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

0條評(píng)論

閱讀需要支付1元查看
<