本文首發(fā)于 JS:構(gòu)造函數(shù)總結(jié)
什么是構(gòu)造函數(shù)旦签?
構(gòu)造函數(shù)(Constructor)的創(chuàng)建方式和普通函數(shù)一樣。但通常首字母進(jìn)行大寫(xiě),用于和普通函數(shù)區(qū)分污桦。
但是當(dāng)一個(gè)函數(shù)創(chuàng)建好以后琴庵,我們并不知道它是不是構(gòu)造函數(shù)(即使函數(shù)名的首字母為大寫(xiě))骗随。只有當(dāng)它以 new
操作符來(lái)調(diào)用的時(shí)候聘裁,我們才能說(shuō)它是一個(gè)構(gòu)造函數(shù)轰豆。
// 這是一個(gè)通用的默認(rèn)構(gòu)造函數(shù)類(lèi) Car
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
}
// 分配給 beetle 的一個(gè)新的 Car 對(duì)象引用
let beetle = new Car("Volkswagen", "Beetle", 1938)
console.log(beetle.model) // expected output: "Beetle"
構(gòu)造函數(shù)屬于被實(shí)例化的特定類(lèi)對(duì)象
構(gòu)造函數(shù)屬于被實(shí)例化的特定類(lèi)對(duì)象胰伍,所以它的函數(shù)名同時(shí)也是它的類(lèi)名。
上面代碼中的 Car()
是一個(gè)構(gòu)造函數(shù)酸休,Car
既是它的函數(shù)名骂租,也是它的類(lèi)名。
將構(gòu)造函數(shù) Car
轉(zhuǎn)換為類(lèi)聲明如下:
class Car {
constructor(make, model, year) {
this.make = make
this.model = model
this.year = year
}
}
構(gòu)造函數(shù)的作用
構(gòu)造函數(shù)的作用是新建實(shí)例對(duì)象斑司,并且給實(shí)例對(duì)象內(nèi)的成員(屬性或方法)賦值渗饮。
在我們需要?jiǎng)?chuàng)建大量同一類(lèi)型的對(duì)象時(shí),這些對(duì)象都具有某些屬性或方法宿刮,如果我們直接通過(guò)變量加字面量的形式進(jìn)行賦值互站,會(huì)產(chǎn)生很多重復(fù)的代碼。而當(dāng)我們將這些對(duì)象抽象為一個(gè)類(lèi)時(shí)僵缺,創(chuàng)建一個(gè)構(gòu)造函數(shù)胡桃,就可以實(shí)現(xiàn)代碼復(fù)用。
// 通過(guò)變量加字面量的形式依次進(jìn)行賦值
let beetle = { make: "Volkswagen", model: "Beetle", year: "1938" }
let porsche911 = { make: "Porsche", model: "Porsche 911", year: "1963" }
let accord = { make: "Accord", model: "Honda", year: "1976" }
// 通過(guò)構(gòu)造函數(shù)的形式進(jìn)行賦值
let beetle = new Car("Volkswagen", "Beetle", 1938)
let porsche911 = new Car("Porsche", "Porsche 911", 1963)
let accord = new Car("Accord", "Honda", 1976)
用 this
來(lái)設(shè)置對(duì)象內(nèi)的屬性或方法
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
this.introduce = function () {
alert(`I'm the ${model}. I'm from ${make}. I was made in ${year}.`)
}
}
通過(guò) new
操作符進(jìn)行調(diào)用
要調(diào)用構(gòu)造函數(shù)磕潮,請(qǐng)使用 new
操作符將新的對(duì)象引用分配給一個(gè)變量/常量翠胰。
let beetle = new Car("Volkswagen", "Beetle", 1938)
構(gòu)造函數(shù)的執(zhí)行過(guò)程
當(dāng)以
new
操作符調(diào)用某一個(gè)構(gòu)造函數(shù)時(shí),構(gòu)造函數(shù)會(huì)立刻在堆內(nèi)存中創(chuàng)建一個(gè)新的內(nèi)存空間自脯,并將這個(gè)內(nèi)存空間的地址賦值給this
亡容。執(zhí)行函數(shù)體內(nèi)的代碼,當(dāng)構(gòu)造函數(shù)執(zhí)行完畢冤今,會(huì)將
this
賦值給一個(gè)新的實(shí)例instance
,并返回instance
茂缚。當(dāng)調(diào)用結(jié)束時(shí)戏罢,
instance
就是我們所需要的新的對(duì)象。
構(gòu)造函數(shù)的返回值
默認(rèn)情況下脚囊,構(gòu)造函數(shù)的返回值是 this
龟糕,即新創(chuàng)建的對(duì)象。
// 這是一個(gè)通用的默認(rèn)構(gòu)造函數(shù)類(lèi) Car
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
}
// 分配給 beetle 的一個(gè)新的 Car 對(duì)象引用
let beetle = new Car("Volkswagen", "Beetle", 1938)
console.log(beetle) // expected output: { make: 'Volkswagen', model: 'Beetle', year: '1938' }
特殊情況下悔耘,構(gòu)造函數(shù)可以返回一個(gè)指定的值讲岁。
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
return {
name: `${make} ${model} ${year}`,
}
}
let beetle = new Car("Volkswagen", "Beetle", 1938)
console.log(beetle) // expected output: { name: 'Volkswagen Beetle 1938' }
構(gòu)造函數(shù)的繼承
通過(guò) call
或 apply
方法
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
}
function Wheel(make, model, year) {
Car.call(this, make, model, year)
this.wheels = 4
}
let beetle = new Wheel("Volkswagen", "Beetle", 1938)
console.log(beetle.wheels) // expected output: 4
通過(guò) prototype
屬性
function Car(make, model, year) {
this.make = make
this.model = model
this.year = year
}
function Wheel() {
this.wheels = 4
}
// 將 Wheel 的原型對(duì)象賦值給 Car 的原型對(duì)象
Car.prototype = new Wheel()
// 糾正繼承鏈
Car.prototype.constructor = Car
let beetle = new Car("Volkswagen", "Beetle", 1938)
console.log(beetle.wheels) // expected output: 4
構(gòu)造函數(shù)(Constructor)和類(lèi)(Class)的區(qū)別
構(gòu)造函數(shù)是一個(gè)普通的函數(shù)。在 ES6 之前,我們習(xí)慣用構(gòu)造函數(shù)去創(chuàng)建一個(gè)類(lèi)缓艳。不過(guò)后來(lái) ES6 定義了 Class 關(guān)鍵字校摩,它可以理解為是構(gòu)造函數(shù)的一個(gè)語(yǔ)法糖,可以讓我們更加方便地創(chuàng)建一個(gè)類(lèi)阶淘。
class Car {
constructor(make, model, year) {
this.make = make
this.model = model
this.year = year
}
}
類(lèi)只能使用 new
操作符調(diào)用衙吩,否則會(huì)報(bào)錯(cuò)。
class Car {
constructor(make, model, year) {
this.make = make
this.model = model
this.year = year
}
}
// 報(bào)錯(cuò)
Car(make, model, year) // typeError: Class constructor Car cannot be invoked without 'new'
class
可以通過(guò) extends
關(guān)鍵字很方便的實(shí)現(xiàn)繼承
class Car {
constructor(make, model, year) {
this.make = make
this.model = model
this.year = year
}
}
class Beetle extends Car {
constructor(make, model, year) {
// super 用于訪問(wèn)和調(diào)用一個(gè)對(duì)象的父對(duì)象上的函數(shù)
// 傳遞給 super 的參數(shù)會(huì)被傳遞給父類(lèi)的構(gòu)造函數(shù)
super(make, model, year)
// super() 只能在使用 this 之前調(diào)用
this.wheels = 4
}
}
let myBeetle = new Beetle("volkswagen", "beetle", 1967)
console.log(myBeetle) // expected output: Beetle {make: "volkswagen", model: "beetle", year: 1967, wheels: 4}