目錄
創建對象
一、創建對象的 5 種核心方式
1. 對象字面量(直接量)
2. 使用?Object.create()
3. 工廠模式
4. 構造函數模式
5. ES6?class?語法(語法糖)
二、構造函數與?new?關鍵字
1. 構造函數的作用
2. 構造函數的特征
3.?new?關鍵字的執行機制
三、構造函數 vs 工廠函數對比
四、原型鏈與構造函數的關系
五、常見問題與解決方案
1. 忘記使用?new?導致問題
2. 方法重復創建(內存浪費)
六、總結
實例成員與靜態成員
一、實例成員(Instance Members)
1. 定義與特點
2. 內存模型
二、靜態成員(Static Members)
1. 定義與特點
2. ES6?class?中的靜態成員
三、實例成員 vs 靜態成員
四、關鍵使用場景
1. 實例成員的典型應用
2. 靜態成員的典型應用
五、繼承與靜態成員
1. ES6?class?的靜態繼承
2. ES5 手動實現靜態繼承
六、常見問題與解決方案
1. 靜態方法中訪問實例成員
2. 實例方法誤用?this?訪問靜態成員
創建對象
一、創建對象的 5 種核心方式
1. 對象字面量(直接量)
特點:最簡潔的方式,適合創建簡單對象。
const person = {name: "Alice",age: 30,greet() {console.log(`Hello, I'm ${this.name}`);}
};
2. 使用?Object.create()
特點:基于現有對象原型創建新對象,適合原型繼承。
const prototypeObj = { type: "Human" };
const person = Object.create(prototypeObj);
person.name = "Bob";
3. 工廠模式
特點:通過函數封裝對象創建邏輯,避免重復代碼。
function createPerson(name, age) {return {name,age,greet() {console.log(`Hi, I'm ${this.name}`);}};
}
const person = createPerson("Charlie", 25);
4. 構造函數模式
特點:結合?new
?關鍵字創建對象,支持實例化。
function Person(name, age) {this.name = name;this.age = age;this.greet = function() {console.log(`Hello, I'm ${this.name}`);};
}
const person = new Person("David", 28);
5. ES6?class
?語法(語法糖)
特點:基于原型的面向對象語法糖,更接近傳統面向對象語言。
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hi, I'm ${this.name}`);}
}
const person = new Person("Eve", 32);
二、構造函數與?new
?關鍵字
1. 構造函數的作用
定義:用于創建對象的模板函數,通常與?new
?關鍵字配合使用。
-
初始化對象:定義對象屬性和方法。
-
標識對象類型:通過?
instanceof
?可檢測對象類型。 -
綁定原型鏈:通過?
prototype
?屬性實現繼承。
示例:
function Person(name, age) {this.name = name; // 實例屬性this.age = age;
}
使用?new
?的實例化過程:
-
創建空對象,其?
__proto__
?指向?Person.prototype
。 -
執行構造函數,將?
this
?綁定到新對象。 -
返回新對象(若構造函數無顯式返回對象)。
2. 構造函數的特征
-
命名約定:通常首字母大寫(如?
Person
),便于區分普通函數。 -
強制使用?
new
:若不使用?new
,this
?會指向全局對象(嚴格模式下為?undefined
),導致意外行為。// 錯誤示例(未使用 new) const p = Person("Frank"); console.log(window.name); // "Frank"(污染全局)
3.?new
?關鍵字的執行機制
當使用?new
?調用構造函數時,發生以下步驟:
function Person(name) {this.name = name;
}// new 的底層模擬(簡化版)
function myNew(constructor, ...args) {// 1. 創建一個新對象,并鏈接到構造函數的原型const obj = Object.create(constructor.prototype);// 2. 執行構造函數,將 this 綁定到新對象const result = constructor.apply(obj, args);// 3. 返回對象(如果構造函數返回對象,則優先返回它)return typeof result === 'object' ? result : obj;
}const person = myNew(Person, "Alice");
詳細步驟:
-
創建空對象:創建一個新對象?
obj
,其隱式原型(__proto__
)指向構造函數的?prototype
。 -
綁定?
this
:將構造函數內的?this
?指向新對象?obj
,并執行構造函數代碼。 -
處理返回值:若構造函數返回一個對象,則返回該對象;否則返回?
obj
。
三、構造函數 vs 工廠函數對比
特性 | 構造函數 | 工廠函數 |
---|---|---|
創建方式 | new Person() | createPerson() |
原型鏈 | 實例的?__proto__ ?指向構造函數原型 | 默認無原型鏈綁定 |
類型檢測 | instanceof ?有效 | instanceof ?無法識別 |
性能 | 稍優(引擎優化) | 略低 |
代碼復用 | 通過原型方法共享 | 每個對象獨立方法(內存冗余) |
四、原型鏈與構造函數的關系
-
構造函數:通過?
prototype
?屬性定義共享方法。 -
實例對象:通過?
__proto__
?訪問原型鏈。 -
繼承機制:實例可訪問構造函數原型上的屬性和方法。
示例:
function Person(name) {this.name = name;
}// 在原型上添加方法(共享)
Person.prototype.greet = function() {console.log(`Hi, I'm ${this.name}`);
};const person = new Person("Grace");
person.greet(); // 調用原型方法
五、常見問題與解決方案
1. 忘記使用?new
?導致問題
解決方案:在構造函數內部強制?new
?調用。
function Person(name) {if (!(this instanceof Person)) {return new Person(name);}this.name = name;
}
const p = Person("Henry"); // 自動補 new
2. 方法重復創建(內存浪費)
問題:構造函數內定義方法會導致每個實例獨立一份方法。
function Person(name) {this.name = name;this.greet = function() {}; // 每個實例都有 greet 方法
}
解決:將方法定義在原型上。
Person.prototype.greet = function() {}; // 共享方法
六、總結
-
對象創建方式:根據場景選擇字面量、構造函數、
class
?等。 -
構造函數核心:結合?
new
?實現實例化,通過原型鏈共享方法。 -
new
?的機制:創建對象、綁定原型、執行構造函數、返回對象。 -
最佳實踐:方法定義在原型上,避免內存浪費;使用?
class
?提升代碼可讀性。
??
實例成員與靜態成員
一、實例成員(Instance Members)
1. 定義與特點
-
定義:屬于實例對象的屬性和方法,每個實例獨立擁有。
-
特點:
-
實例屬性:通過構造函數內的?
this
?定義,每個實例獨占內存。 -
實例方法:定義在構造函數的?
prototype
?上,所有實例共享。
-
-
示例:
function Person(name) {this.name = name; // 實例屬性(每個實例獨立) }// 實例方法(共享) Person.prototype.sayHello = function() {console.log(`Hello, I'm ${this.name}`); };const alice = new Person("Alice"); alice.sayHello(); // "Hello, I'm Alice"
2. 內存模型
-
實例屬性:每個對象在堆中獨立存儲。
-
實例方法:存儲在原型對象(
Person.prototype
)中,所有實例通過原型鏈共享。
二、靜態成員(Static Members)
1. 定義與特點
-
定義:屬于構造函數本身的屬性和方法,無需實例化即可訪問。
-
特點:
-
通過構造函數直接調用(如?
Array.isArray()
)。 -
無法訪問實例成員(無?
this
?指向實例)。
-
-
示例:
function Person() {}// 靜態屬性 Person.species = "Homo sapiens";// 靜態方法 Person.createAnonymous = function() {return new Person("Anonymous"); };console.log(Person.species); // "Homo sapiens" const anon = Person.createAnonymous();
2. ES6?class
?中的靜態成員
-
使用?
static
?關鍵字定義:class Person {static species = "Homo sapiens"; // 靜態屬性static createAnonymous() { // 靜態方法return new Person("Anonymous");} }
三、實例成員 vs 靜態成員
特性 | 實例成員 | 靜態成員 |
---|---|---|
歸屬 | 實例對象 | 構造函數 |
內存占用 | 屬性獨立,方法共享 | 全局唯一 |
訪問方式 | instance.property | Constructor.property |
this ?指向 | 實例對象 | 構造函數 |
適用場景 | 對象狀態和行為 | 工具方法、工廠函數、類配置 |
四、關鍵使用場景
1. 實例成員的典型應用
-
描述對象狀態(如?
person.name
)。 -
定義對象行為(如?
person.sayHello()
)。
2. 靜態成員的典型應用
-
工具函數:無需實例即可調用的功能(如?
Math.abs()
)。 -
工廠方法:創建特定類型的實例(如?
Person.createAnonymous()
)。 -
全局配置:類級別的常量或配置(如?
Person.DEFAULT_AGE = 30
)。
五、繼承與靜態成員
1. ES6?class
?的靜態繼承
-
子類自動繼承父類的靜態成員:
class Employee extends Person {static role = "Developer"; } console.log(Employee.species); // "Homo sapiens"(繼承自 Person)
2. ES5 手動實現靜態繼承
-
需顯式復制靜態成員或設置原型鏈:
function Person() {} Person.species = "Homo sapiens";function Employee() {} // 繼承實例方法 Employee.prototype = Object.create(Person.prototype); // 繼承靜態成員 Object.setPrototypeOf(Employee, Person);console.log(Employee.species); // "Homo sapiens"
六、常見問題與解決方案
1. 靜態方法中訪問實例成員
-
問題:靜態方法無實例上下文,無法直接訪問實例屬性。
-
解決:通過參數傳遞實例:
Person.compareAge = function(person1, person2) {return person1.age - person2.age; };
2. 實例方法誤用?this
?訪問靜態成員
-
問題:實例方法中?
this
?指向實例,無法直接訪問靜態成員。 -
解決:通過構造函數名訪問:
Person.prototype.logSpecies = function() {console.log(Person.species); // 正確 };