W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
通常創(chuàng)建對象來表示真實世界中的實體,如用戶和訂單等:
let user = {
name: "John",
age: 30
};
并且,在現(xiàn)實世界中,用戶可以進行 操作:從購物車中挑選某物、登錄和注銷等。
在 JavaScript 中,行為(action)由屬性中的函數(shù)來表示。
剛開始,我們來教 user
說 hello:
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
這里我們使用函數(shù)表達式創(chuàng)建了一個函數(shù),并將其指定給對象的 user.sayHi
屬性。
隨后我們像這樣 user.sayHi()
調(diào)用它。用戶現(xiàn)在可以說話了!
作為對象屬性的函數(shù)被稱為 方法。
所以,在這我們得到了 user
對象的 sayHi
方法。
當(dāng)然,我們也可以使用預(yù)先聲明的函數(shù)作為方法,就像這樣:
let user = {
// ...
};
// 首先,聲明函數(shù)
function sayHi() {
alert("Hello!");
}
// 然后將其作為一個方法添加
user.sayHi = sayHi;
user.sayHi(); // Hello!
面向?qū)ο缶幊?/b>
當(dāng)我們在代碼中用對象表示實體時,就是所謂的 面向?qū)ο缶幊?/a>,簡稱為 “OOP”。
OOP 是一門大學(xué)問,本身就是一門有趣的科學(xué)。怎樣選擇合適的實體?如何組織它們之間的交互?這就是架構(gòu),有很多關(guān)于這方面的書,例如 E. Gamma、R. Helm、R. Johnson 和 J. Vissides 所著的《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,G. Booch 所著的《面向?qū)ο蠓治雠c設(shè)計》等。
在對象字面量中,有一種更短的(聲明)方法的語法:
// 這些對象作用一樣
user = {
sayHi: function() {
alert("Hello");
}
};
// 方法簡寫看起來更好,對吧?
let user = {
sayHi() { // 與 "sayHi: function(){...}" 一樣
alert("Hello");
}
};
如上所示,我們可以省略 "function"
,只寫 sayHi()
。
說實話,這種表示法還是有些不同。在對象繼承方面有一些細微的差別(稍后將會介紹),但目前它們并不重要。在幾乎所有的情況下,更短的語法是首選的。
通常,對象方法需要訪問對象中存儲的信息才能完成其工作。
例如,user.sayHi()
中的代碼可能需要用到 user
的 name 屬性。
為了訪問該對象,方法中可以使用 this
關(guān)鍵字。
this
的值就是在點之前的這個對象,即調(diào)用該方法的對象。
舉個例子:
let user = {
name: "John",
age: 30,
sayHi() {
// "this" 指的是“當(dāng)前的對象”
alert(this.name);
}
};
user.sayHi(); // John
在這里 user.sayHi()
執(zhí)行過程中,this
的值是 user
。
技術(shù)上講,也可以在不使用 this
的情況下,通過外部變量名來引用它:
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "user" 替代 "this"
}
};
……但這樣的代碼是不可靠的。如果我們決定將 user
復(fù)制給另一個變量,例如 admin = user
,并賦另外的值給 user
,那么它將訪問到錯誤的對象。
下面這個示例證實了這一點:
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // 導(dǎo)致錯誤
}
};
let admin = user;
user = null; // 重寫讓其更明顯
admin.sayHi(); // TypeError: Cannot read property 'name' of null
如果我們在 alert
中以 this.name
替換 user.name
,那么代碼就會正常運行。
在 JavaScript 中,this
關(guān)鍵字與其他大多數(shù)編程語言中的不同。JavaScript 中的 this
可以用于任何函數(shù),即使它不是對象的方法。
下面這樣的代碼沒有語法錯誤:
function sayHi() {
alert( this.name );
}
this
的值是在代碼運行時計算出來的,它取決于代碼上下文。
例如,這里相同的函數(shù)被分配給兩個不同的對象,在調(diào)用中有著不同的 “this” 值:
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// 在兩個對象中使用相同的函數(shù)
user.f = sayHi;
admin.f = sayHi;
// 這兩個調(diào)用有不同的 this 值
// 函數(shù)內(nèi)部的 "this" 是“點符號前面”的那個對象
user.f(); // John(this == user)
admin.f(); // Admin(this == admin)
admin['f'](); // Admin(使用點符號或方括號語法來訪問這個方法,都沒有關(guān)系。)
這個規(guī)則很簡單:如果 obj.f()
被調(diào)用了,則 this
在 f
函數(shù)調(diào)用期間是 obj
。所以在上面的例子中 this 先是 user
,之后是 admin
。
在沒有對象的情況下調(diào)用:?
this == undefined
?我們甚至可以在沒有對象的情況下調(diào)用函數(shù):
function sayHi() { alert(this); } sayHi(); // undefined
在這種情況下,嚴(yán)格模式下的
this
值為undefined
。如果我們嘗試訪問this.name
,將會報錯。
在非嚴(yán)格模式的情況下,
this
將會是 全局對象(瀏覽器中的window
,我們稍后會在 全局對象 一章中學(xué)習(xí)它)。這是一個歷史行為,"use strict"
已經(jīng)將其修復(fù)了。
通常這種調(diào)用是程序出錯了。如果在一個函數(shù)內(nèi)部有
this
,那么通常意味著它是在對象上下文環(huán)境中被調(diào)用的。
解除 ?
this
?綁定的后果如果你經(jīng)常使用其他的編程語言,那么你可能已經(jīng)習(xí)慣了“綁定
this
”的概念,即在對象中定義的方法總是有指向該對象的this
。
在 JavaScript 中,
this
是“自由”的,它的值是在調(diào)用時計算出來的,它的值并不取決于方法聲明的位置,而是取決于在“點符號前”的是什么對象。
在運行時對
this
求值的這個概念既有優(yōu)點也有缺點。一方面,函數(shù)可以被重用于不同的對象。另一方面,更大的靈活性造成了更大的出錯的可能。
這里我們的立場并不是要評判編程語言的這個設(shè)計是好是壞。而是要了解怎樣使用它,如何趨利避害。
箭頭函數(shù)有些特別:它們沒有自己的 this
。如果我們在這樣的函數(shù)中引用 this
,this
值取決于外部“正常的”函數(shù)。
舉個例子,這里的 arrow()
使用的 this
來自于外部的 user.sayHi()
方法:
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
這是箭頭函數(shù)的一個特性,當(dāng)我們并不想要一個獨立的 this
,反而想從外部上下文中獲取時,它很有用。在后面的 深入理解箭頭函數(shù) 一章中,我們將深入介紹箭頭函數(shù)。
object.doSomething()
? 這樣的“操作”。this
?。this
的值是在程序運行時得到的。
this
?,但是這個 ?this
?只有在函數(shù)被調(diào)用時才會有值。object.method()
?,調(diào)用過程中的 ?this
?值是 ?object
?。請注意箭頭函數(shù)有些特別:它們沒有 ?this
?。在箭頭函數(shù)內(nèi)部訪問到的 ?this
?都是從外部獲取的。
重要程度: 5
這里 makeUser
函數(shù)返回了一個對象。
訪問 ref
的結(jié)果是什么?為什么?
function makeUser() {
return {
name: "John",
ref: this
};
}
let user = makeUser();
alert( user.ref.name ); // 結(jié)果是什么?
答案:一個錯誤。
試一下:
function makeUser() {
return {
name: "John",
ref: this
};
}
let user = makeUser();
alert( user.ref.name ); // Error: Cannot read property 'name' of undefined
這是因為設(shè)置 this
的規(guī)則不考慮對象定義。只有調(diào)用那一刻才重要。
這里 makeUser()
中的 this
的值是 undefined
,因為它是被作為函數(shù)調(diào)用的,而不是通過點符號被作為方法調(diào)用。
this
的值是對于整個函數(shù)的,代碼段和對象字面量對它都沒有影響。
所以 ref: this
實際上取的是當(dāng)前函數(shù)的 this
。
我們可以重寫這個函數(shù),并返回和上面相同的值為 undefined
的 this
:
function makeUser(){
return this; // 這次這里沒有對象字面量
}
alert( makeUser().name ); // Error: Cannot read property 'name' of undefined
我們可以看到 alert( makeUser().name )
的結(jié)果和前面那個例子中 alert( user.ref.name )
的結(jié)果相同。
這里有個反例:
function makeUser() {
return {
name: "John",
ref() {
return this;
}
};
}
let user = makeUser();
alert( user.ref().name ); // John
現(xiàn)在正常了,因為 user.ref()
是一個方法。this
的值為點符號 .
前的這個對象。
重要程度: 5
創(chuàng)建一個有三個方法的 calculator
對象:
read()
? 提示輸入兩個值,并將其保存為對象屬性,屬性名分別為 ?a
? 和 ?b
?。sum()
? 返回保存的值的和。mul()
? 將保存的值相乘并返回計算結(jié)果。let calculator = {
// ……你的代碼……
};
calculator.read();
alert( calculator.sum() );
alert( calculator.mul() );
let calculator = {
sum() {
return this.a + this.b;
},
mul() {
return this.a * this.b;
},
read() {
this.a = +prompt('a?', 0);
this.b = +prompt('b?', 0);
}
};
calculator.read();
alert( calculator.sum() );
alert( calculator.mul() );
重要程度: 2
有一個可以上下移動的 ladder
對象:
let ladder = {
step: 0,
up() {
this.step++;
},
down() {
this.step--;
},
showStep: function() { // 顯示當(dāng)前的 step
alert( this.step );
}
};
現(xiàn)在,如果我們要按順序執(zhí)行幾次調(diào)用,可以這樣做:
ladder.up();
ladder.up();
ladder.down();
ladder.showStep(); // 1
ladder.down();
ladder.showStep(); // 0
修改 up
,down
和 showStep
的代碼,讓調(diào)用可以鏈接,就像這樣:
ladder.up().up().down().showStep().down().showStep(); // 展示 1,然后 0
這種方法在 JavaScript 庫中被廣泛使用。
解決方案就是在每次調(diào)用后返回這個對象本身。
let ladder = {
step: 0,
up() {
this.step++;
return this;
},
down() {
this.step--;
return this;
},
showStep() {
alert( this.step );
return this;
}
};
ladder.up().up().down().showStep().down().showStep(); // 展示 1,然后 0
我們也可以每行一個調(diào)用。對于長鏈接它更具可讀性:
ladder
.up()
.up()
.down()
.showStep() // 1
.down()
.showStep(); // 0
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: