前端進階-類和物件
物件可以表示資料和功能。
建構函式
建構函式:結構和語法
function SoftwareDeveloper() {
this.favoriteLanguage = 'JavaScript';
}
首先,建構函式並不宣告區域性變數
,而是使用 this 關鍵字來儲存資料
。以上函式將為所建立的任何物件新增一個 favoriteLanguage 屬性
,併為其分配一個預設值 'JavaScript'
。現在不用太在意建構函式中的 this
;只要知道 this
是指在建構函式前面使用 new 關鍵字建立的新物件
即可。
最後一點比較特別的是,這個函式似乎不會返回任何東西
!JavaScript 中的建構函式不應該有一個顯式的返回值
(即使用 return
語句)。
建立一個新的物件
function SoftwareDeveloper() {
this.favoriteLanguage = 'JavaScript';
}
let developer = new SoftwareDeveloper();
console.log(developer);
// SoftwareDeveloper {this.favoriteLanguage: "JavaScript"}
function SoftwareDeveloper(name) {
this.favoriteLanguage = 'JavaScript';
this.name = name;
}
let instructor = new SoftwareDeveloper('Andrew');
console.log(instructor);
// SoftwareDeveloper { favoriteLanguage: 'JavaScript', name: 'Andrew' }
注意,大寫建構函式名稱的第一個字母只是一個命名慣例
。雖然第一個字母應該大寫,但如果不小心用了小寫,也仍然還是建構函式(即當用 new 運算子呼叫時,等等)。
檢視物件的建構函式 (instanceOf
)
function Developer(name) {
this.name = name;
}
const dev = new Developer('Veronika');
typeof dev // "object"
dev instanceOf Developer; // true
當在物件上呼叫方法時,this
會被賦值,並且其值指向該物件。由於它是一個保留字,因此不應該用作任何變數名稱、函式名稱等。
關鍵字 this
根據 this
的呼叫方式,分析 this
指代的含義:
如果使用 new 運算子來呼叫建構函式,this
的值將被設定為新建立的物件。如果在物件上呼叫方法,this
將被設定為該物件本身。如果簡單地呼叫一個函式,this
將被設定為全域性物件:window
。
設定自己的 this
值
Javascript 提供了幾種設定 this
值的方法,這些方法分別是 call()
、apply()
和 bind()
。前兩種方法在函式上被呼叫
,會因為引數的傳入方式
不同而有所不同。第三種方法是返回新函式
的方法,每種方法都在不同的環境中使用。
call()
call()
是一個直接呼叫到函式上的方法。我們傳遞給它一個單一的值,以設定為 this
的值,然後逐個傳入該函式的任何引數,用逗號分隔。
function multiply(n1, n2) {
return n1 * n2;
}
multiply(3, 4);
// 12
multiply.call(window, 3, 4);
// 12
除了呼叫常規函式
之外,我們如何呼叫附加到物件上的函式
(即方法)呢?
使用 call()
來呼叫方法允許我們從物件中“借用”方法
,然後將其用於另一個物件
!
const mockingbird = {
title: 'To Kill a Mockingbird',
describe: function () {
console.log(`${this.title} is a classic novel`);
}
};
mockingbird.describe();
// 'To Kill a Mockingbird is a classic novel'
const pride = {
title: 'Pride and Prejudice'
};
mockingbird.describe.call(pride);
// 'Pride and Prejudice is a classic novel'
首先,call()
方法被呼叫到 mockingbird.describe
(它指向一個函式)上。然後,this
的值被傳遞給 call()
方法:pride
。
由於 mockingbird
的 describe()
方法引用了 this.title
,我們需要訪問 this
所指向的物件的 title
屬性。但是,由於我們已經設定了自己的 this
的值,this.title
的值將會從 pride
物件中被訪問!結果,mockingbird.describe.call(pride);
被執行,我們在控制檯中看到 'Pride and Prejudice is a classic novel'
。
apply()
multiply.apply(window, [3, 4]); // 將函式的引數放在一個陣列中
// 12
就像 call()
一樣,apply()
在一個函式上被呼叫,不僅可以呼叫該函式,而且還可以為它關聯一個特定的 this
值。但是,apply()
並不是逐個傳遞引數並用逗號分隔,而是將函式的引數放在一個陣列中
。
mockingbird.describe.apply(pride);
// 'Pride and Prejudice is a classic novel'
傳遞給 call()
和 apply()
的第一個引數是相同的(即繫結 this
值的物件)。由於 describe()
方法不接受任何引數,因此 mockingbird.describe.call(pride);
和 mockingbird.describe.apply(pride);
唯一的區別就是方法!這兩種方法都會產生相同的結果。
偏向選擇其中一種方法
如果你事先並不知道函式所需要的引數個數
,那麼 call()
的使用可能會受到限制。在這種情況下,apply()
是一個更好的選擇
,因為它只接受一個引數陣列,然後將其解包並傳遞給函式。請記住,解包
可能會略微影響效能,但這種影響並不顯著。
回撥和 this
function invokeTwice(cb) {
cb();
cb();
}
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
}
dog.growOneYear();
dog.age; // 6
invokeTwice(dog.growOneYear); //函式來呼叫它,因此 this 設成了全域性變數,而不是 dog 物件
dog.age; // 6 , dog 的 age 屬性沒有發生變化
使用匿名閉包來儲存 this
簡單地呼叫一個普通函式會將 this
的值設定為全域性物件
(即 window)。我們如何解決這個問題呢?
解決這個問題的一種方式就是使用一個匿名閉包來遮蔽 dog
物件:
invokeTwice(function () {
dog.growOneYear();
});
dog.age
// 7
使用 bind() 來儲存 this
與 call()
和 apply()
類似,bind()
方法也允許使用者直接為 this
定義一個值。bind()
也是一個在函式上呼叫的方法,但不同於 call()
或 apply()
,它們都會立即呼叫函式——bind()
會返回一個新的函式
。當被呼叫時,該函式會將 this
設定為我們賦給它的值。
function invokeTwice(cb) {
cb();
cb();
}
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
}
invokeTwice(dog.growOneYear);
const myGrow = dog.growOneYear.bind(dog);
invokeTwice(myGrow);
dog.age; // 7
bind()
是可以直接在函式中呼叫
的方法,返回該函式的副本
,並具有特定的 this
值。
原型繼承
Cat()
建構函式是使用 new
運算子來呼叫的,該運算子建立了 bailey
例項(物件)。請注意,meow()
方法是在 bailey
物件的建構函式的原型中定義的。原型只是一個物件,該建構函式所建立的所有物件均被祕密連結到該原型。因此,我們可以將 bailey.meow()
當作 bailey
自身的方法執行。
無論你是訪問屬性(例如 bailey.lives;
)還是呼叫方法(即 bailey.meow();
),JavaScript 直譯器都會按照特定的順序在原型鏈中查詢它們:
- 首先,JavaScript 引擎將檢視物件自身的屬性。這意味著,直接在該物件中定義的任何屬性和方法將優先於其他位置的任何同名屬性和方法(類似於作用域鏈中的變數陰影)。
- 如果找不到目標屬性,它將搜尋物件的建構函式的原型,以尋找匹配。
- 如果原型中不存在該屬性,則 JavaScript 引擎將沿著該鏈繼續查詢。
- 該鏈的最頂端是
Object()
物件,也就是頂級父物件。如果_仍然_找不到該屬性,則該屬性為未定義。
每個函式都有一個 prototype
屬性,它其實只是一個物件。當使用 new
運算子將該函式作為建構函式來呼叫時,它會建立並返回一個新的物件。該物件被祕密地連結到其建構函式的 prototype
,而這個祕密連結讓該物件可以訪問 prototype
的屬性和方法,就像它自己的一樣!
function Dog(age, weight, name) {
this.age = age;
this.weight = weight;
this.name = name;
/**
this.bark = function() {
console.log(`${this.name} says woof!`);
};
*/
}
// 在 dog 原型中定義 bark
Dog.prototype.bark = function() {
console.log(`${this.name} says woof!`);
};
dog = new Dog(2, 60, 'Java');
dog.bark(); // Java says woof!
當我們在 Dog
中叫 bark
方法時,JavaScript 引擎會檢視自己的屬性,嘗試找到與 bark
方法相匹配的名稱,由於 bark
沒有直接定義在這個 dog
上,它會看看 bark
方法的原型,最後我們不需要呼叫 dog.prototype.bark
,只需要呼叫 dog.bark
就可以了。因為這個 dog
物件已經通過它的原型與 bark
方法聯絡起來。
替換 prototype
物件
function Hamster() { this.hasFur = true; }
let waffle = new Hamster();
let pancake = new Hamster();
在建立新的物件 waffle
和 pancake
之後,我們仍然可以為 Hamster
的原型新增屬性,而且它仍然可以訪問這些新的屬性。
Hamster.prototype.eat = function () { console.log('Chomp chomp chomp!'); };
waffle.eat(); // 'Chomp chomp chomp!'
pancake.eat(); // 'Chomp chomp chomp!'
我們將 Hamster
的 prototype
物件完全替換為其他內容:
Hamster.prototype = {
isHungry: false,
color: 'brown'
};
先前的物件無法訪問更新後的原型的屬性;它們只會保留與舊原型的祕密連結:
console.log(waffle.color); // undefined
waffle.eat(); // 'Chomp chomp chomp!'
console.log(pancake.isHungry); // undefined
事實證明,此後建立的任何新的 Hamster
物件都會使用更新後的原型:
const muffin = new Hamster();
muffin.eat(); // TypeError: muffin.eat is not a function
console.log(muffin.isHungry); // false
console.log(muffin.color); // 'brown'
檢查物件的屬性
hasOwnProperty()
hasOwnProperty()
可以幫助你找到某個特定屬性的來源
。在向其傳入你要查詢的屬性名稱的字串
後,該方法會返回一個布林值
,指示該屬性是否屬於該物件本身
(即該屬性不是被繼承的)。
function Phone() {
this.operatingSystem = 'Android';
}
Phone.prototype.screenSize = 6;
const myPhone = new Phone();
const own = myPhone.hasOwnProperty('operatingSystem');
console.log(own);
//true
const inherited = myPhone.hasOwnProperty('screenSize');
console.log(inherited);
//false
isPrototypeOf()
物件還可以訪問 isPrototypeOf()
方法,該方法可以檢查某個物件是否存在於另一個物件的原型鏈中
。 使用這種方法,你可以確認某個特定的物件是否是另一個物件的原型
。
const rodent = {
favoriteFood: 'cheese',
hasTail: true
};
function Mouse() {
this.favoriteFood = 'cheese';
}
Mouse.prototype = rodent;
const ralph = new Mouse();
const result = rodent.isPrototypeOf(ralph)
console.log(result);
//true
建立一個新的 Mouse
物件,它的原型應該是 rodent
物件。isPrototypeOf()
是確認某個物件是否存在於另一個物件的原型鏈中
的好辦法。
Object.getPrototypeOf()
const myPrototype = Object.getPrototypeOf(ralph);
console.log(myPrototype);
//{ favoriteFood: "cheese", hasTail: true }
ralph
的原型與結果具有相同的屬性,因為它們就是同一個物件。 Object.getPrototypeOf()
很適合檢索給定物件的原型
。
const capitals = {
California: 'Sacramento',
Washington: 'Olympia',
Oregon: 'Salem',
Texas: 'Austin'
};
Object.getPrototypeOf(capitals) === Object.prototype
// true
物件字面量表示法建立的物件,它的建構函式就是內建的 Object()
建構函式本身!因此,它會保持一個對其建構函式原型的引用
。
isPrototypeOf()
可以檢查某個物件是否存在於另一個物件的原型鏈中isPrototypeOf()
會接受一個引數,一個原型鏈將被搜尋的物件getPrototypeOf()
會返回傳遞給它的物件的原型
constructor 屬性
每次建立一個物件時,都會有一個特殊的屬性被暗中分配給它:constructor
。訪問一個物件的 constructor
屬性會返回一個對建立該物件的建構函式的引用
!
function Longboard() {
this.material = 'bamboo';
}
const board = new Longboard();
console.log(board.constructor);
//function Longboard() {
// this.material = 'bamboo';
//}
如果某個物件是使用字面量表示法建立的,那麼它的建構函式就是內建的 Object()
建構函式!
const rodent = {
teeth: 'incisors',
hasTail: true
};
console.log(rodent.constructor);
//function Object() { [native code] }
- 訪問一個物件的
constructor
屬性會返回一個對建立該物件(例項)的建構函式的引用 - 每個物件都有一個
constructor
屬性 - 使用字面量表示法建立的物件是用
Object()
建構函式建立的
hasOwnProperty()
isPrototypeOf()
Object.getPrototypeOf()
.constructor
相關文章
- 前端進階-深入瞭解物件前端物件
- 前端進階課程之物件屬性特性詳解前端物件
- Python進階之物件導向(類的特殊方法)Python物件
- 高階前端進階(三)前端
- 高階前端進階(七)前端
- 高階前端進階(五)前端
- js物件建立進階JS物件
- 類和物件物件
- 高階前端進階系列 - webview前端WebView
- 前端進階-樣式前端
- 前端進階之困前端
- Java小白進階筆記(5)-進階物件導向Java筆記物件
- JavaScript進階知識點——函式和物件詳解JavaScript函式物件
- Java 物件和類Java物件
- Scala 類和物件物件
- 【Kotlin】類和物件Kotlin物件
- 類和物件(中)物件
- Java 類和物件Java物件
- 類和物件案例物件
- Java物件和類Java物件
- Python進階07 函式物件Python函式物件
- Python進階之物件導向Python物件
- 前端進階課程之this指向前端
- 前端進階的破冰之旅前端
- 前端進階 -- TS相關前端
- 高階前端的進階——CSS之flex前端CSSFlex
- 【JVM進階之路】十四:類載入器和類載入機制JVM
- 前端進階(1)Web前端效能優化前端Web優化
- 前端入門16-JavaScript進階之EC和VO前端JavaScript
- Java進階01 String類Java
- c++類和物件C++物件
- C++ 類和物件C++物件
- 6.類和物件物件
- 類圖和物件圖物件
- 認識類和物件物件
- SystemVerilog 類和物件(三)物件
- 第10章 物件和類——物件和類(六) 抽象資料型別物件抽象資料型別
- C++類和物件是什麼?C++類和物件詳解C++物件