构造函数和原型方式创建对象
为什么要在JavaScript中创建对象?
减少性能消耗: 有一些对象,有着共同的行为,如果使每个对象都有自己的函数,那么就会影响性能
在JavaScript可以用方式创建对象?
1. 对象字面量
对象字面量是一种简单的对象创建方式,适用于简单的对象或单例对象。这种方式通过直接定义对象的属性和方法,比较直观。
//这是一个简单的可以嘎嘎叫的Duck
const duck = {
nickname: '小黄',
singsing: function () {
console.log('嘎嘎');
}
};
duck.singsing();
2. 构造函数和原型
构造函数和原型链是JavaScript中实现面向对象编程的主要方式,适用于需要创建多个相似对象的场景。通过构造函数,可以为每个实例设置初始属性,而通过原型链,可以为所有实例共享方法,节省内存。
// 构造函数
function Duck(nickname) {
this.nickname = nickname;
}
// 在Duck的原型上定义方法singsing
Duck.prototype.singsing = function () {
console.log('嘎嘎嘎');
};
// 实例化对象
const duck = new Duck('鸭王');
// 调用实例方法
duck.singsing();
手写new操作符过程
new 操作符的过程
- 创建一个新的空对象
- 将这个空对象的原型指向构造函数的原型
- 将构造函数中的
this绑定到新创建的对象 - 执行构造函数的代码,并返回对象
1、创建一个新的空对象
这一步创建了一个新的空对象 obj,将来会作为新实例返回
const obj = {};
2、将这个空对象的原型指向构造函数的原型
-
将空对象原型指向构造函数原型的方法有两种
将新对象 obj 的原型指向构造函数的原型,这一步确保了通过 new 创建的对象可以继承构造函数原型上的方法和属性。
-
使用
Object.setPrototypeOf方法Object.setPrototypeOf()静态方法可以将一个指定对象的原型(即内部的[[Prototype]]属性)设置为另一个对象或者null//创建两个类 const obj = {}; const parent = { foo: 'bar' }; //指定obj原型为parent Object.setPrototypeOf(obj, parent); console.log(obj.foo); -
使用 对象的
__proto__属性和prototype属性.prototype是 JavaScript 中每个函数对象默认拥有的一个属性。这个属性指向一个对象,所有由该函数创建的实例都可以共享这个对象的属性和方法。在面向对象编程中,prototype是实现继承和共享方法的重要机制.prototype 的工作原理
构造函数:当定义一个构造函数(如Duck)时,JavaScript 引擎会自动为它创建一个prototype属性,指向一个初始对象(称为原型对象)
实例的原型链:当使用new关键字创建一个实例时,实例会有一个隐式属性__proto__,它指向构造函数的prototype对象
方法查找机制:当访问实例的一个属性或方法时,JavaScript 会先查找实例自身的属性或方法。如果没有找到,它会继续查找实例的__proto__(即构造函数的prototype对象)上的属性或方法。const obj = {}; const parent = { foo: 'bar' }; obj.__proto__ = parent.prototype console.log(obj.foo);
3、将构造函数中的 this 绑定到新创建的对象
apply方法调用一个具有给定this值的函数,并以一个数组(或类数组对象)作为参数。
func.apply(thisArg, [argsArray]
//func:要调用的函数。
//thisArg:调用函数时使用的 this 值。
//argsArray:数组或类数组对象,表示函数调用时的参数列表。
function greet(greeting, punctuation) {
console.log("嘎嘎")
}
const context = { user: '小黄' }
// 使用 apply 调用 greet,this 指向 context,参数为 ['Hello', '!']
greet.apply(context, ['Hello', '!'])
4、执行构造函数的代码,并返回对象
apply第二个参数是数组,在将this绑定到obj对象时,同时也将数组内容作为参数传给构造函数。
return obj
简单单例模式
为什么要使用单例模式
有的类只需要实例化一次,可以减少性能消耗,更方便管理
单例模式的流程
- 创建构造函数
- 创建原型方法
- 挂载静态方法
- 创建实例
// 单例模式,有的类只实例化一次,性能更好,更方便管理
var Singleton = function (name) {
this.name = name
}
Singleton.prototype.getName = function () {
console.log(this.name)
}
// getInstance直接挂载在Singleton上,相当Java中的静态方法
Singleton.getInstance = function (name) {
if (!this.instance) {
// 静态的属性,静态的属性,所有实例共享
this.instance = new Singleton(name)
}
return this.instance
}
let obj1 = Singleton.getInstance('铠甲勇士')
let obj2 = Singleton.getInstance('帝皇铠甲')
console.log(obj1 == obj2, obj1.name, obj2.name)
1. 创建构造函数
该函数接收一个 name 参数并将其赋值给实例的 name 属性。
var Singleton = function (name) {
this.name = name
// this.instance = null 用判断实例有无被创建,且作保存内存地址的作用
}
2. 创建原型方法
getName 方法被添加到 Singleton 的原型中,所有 Singleton 的实例都可以访问这个方法。
Singleton.prototype.getName = function () {
console.log(this.name)
}
3. 挂载静态方法
getInstance 方法被直接挂载到 Singleton 函数上。它在第一次调用时创建一个 Singleton 实例,并将该实例存储在 Singleton 的静态属性 instance 中。之后的调用都返回同一个实例。
// getInstance直接挂载在Singleton上,相当Java中的静态方法
Singleton.getInstance = function (name) {
if (!this.instance) {
// 静态的属性,静态的属性,所有实例共享
this.instance = new Singleton(name)
}
return this.instance
}
4. 创建实例
我们通过Singleton.getInstance 方法创建实例,并验证结果
let obj1 = Singleton.getInstance('铠甲勇士')
let obj2 = Singleton.getInstance('帝皇铠甲')
console.log(obj1 == obj2, obj1.name, obj2.name)
优化单例模式
关键点
Singleton.getInstance是一个静态方法,它确保Singleton只被实例化一次。Singleton.instance是一个静态属性,存储唯一的Singleton实例。- 每次调用
Singleton.getInstance方法时,如果实例不存在,则创建一个新的Singleton实例;如果实例已存在,则返回现有实例。
修改与优化
- 私有化构造函数:通过抛出错误阻止直接调用构造函数,强制使用
getInstance方法创建实例。 - 重置方法:添加一个方法来重置单例实例(例如在测试环境中)。
- 更灵活的参数处理:允许传递更多参数给单例实例。