一、单例设计模式
把描述当前事物特征的信息进行分组归类,减少全局变量的污染。就是一个对象:不仅仅被叫做变量,也被称为命名空间
1. 单例模式
单例模式:把描述事物的信息放到一个命名空间中进行分组,防止全局变量的污染
// 单例模式创建老师的信息记录对象
var t1 = {
name: '马宾',
age: 18,
subject: 'JS',
from: '珠峰'
};
var t2 = {
name: '姜文',
age: 19,
subject: '架构师课程',
from: '珠峰'
};
// 单例模式虽然解决了全局变量命名空间互相覆盖的问题,但是效率太低,当大规模创建对象时,就需要写许多重复的代码。怎么解决这个问题?
2. 高级单例模式
为了让单例模式变得更高大上一些,真实项目中的单例模式都采用闭包的形式
function teacher(name, age, subject, from = '珠峰') {
var obj = {}; // 原材料
obj.name = name; // 加工
obj.age = age; // 加工
obj.subject = subject;
obj.from = from;
obj.teach = function () {
console.log(`${this.name} 老师教 ${this.subject}`);
};
return obj; // 出厂
}
var t3 = teacher('任金辉', 19, 'JS');
console.log(t3);
var t4 = teacher('薛振翔', 19, 'JS');
console.log(t4);
console.log(t3 === t4);
// 工厂模式:像上面这样,把创建对象的细节封装成一个函数,在函数中为这个对象添加属性,这种创建对象的模式叫做工厂模式;
// 工厂虽然可以批量生产,但是生产出来的对象都一样,没有分类。
二、工厂模式
批量化生产:把实现某个功能的代码进行封装,后期在想实现这个功能,我们直接执行函数即可
- 低耦合:减少页面中冗余的代码
- 高内聚:提高代码的重复使用
var ary = new Array(1, 2, 3, 4); // 我们这样通过 new 操作符以实例创建方式创建了一个数组的实例?}
console.log(ary);
var t5 = new teacher('马宾', 18, 'JS', '珠峰');
console.log(t5);
// 我们通过 new 操作符执行工厂模式的函数,和直接执行没有啥区别;我们 new Array 得到数组实例,但是 new teacher 只能得到一个普通对象,和不 new 没区别;这个时候,咱们的 teacher 函数和 Array 存在
不同。
// 因为 Array 虽然是函数数据类型的,但是不是普通函数,它叫做构造函数。
三、重构类的原型
/*
* 重构类的原型:让某个类的原型指向新的堆内存地址(重定向指向)
* 问题:重定向后的空间中不一定有 CONSTRUCTOR 属性(只有浏览器默认给 PROTOTYPE 开辟的堆内存中
才存在 CONSTRUCTOR),
这样导致类和原型机制不完整;所以需要我们手动再给新的原型空间设置 CONSTRUCTOR 属性;
* 问题:在重新指向之前,我们需要确保原有原型的堆内存中没有设置属性和方法,因为重定向后,
原有的属性和方法就没啥用了(如果需要克隆到新的原型堆内存中,我们还需要额外的处理) => 但是内置类的
原型,由于担心这样的改变会让内置的方法都消失,所以禁止了我们给内置类原型的空间重定向,例如:
Array.prototype = {...} 这样没有用,如果想加方法 Array.prototype.xxx = function(){...} 可以这
样处理
*/
function Fn() {
// ...
}
Fn.prototype.xxx = function () {}
// 批量给原型设置属性方法的时候:重构类的原型
Fn.prototype = {
constructor: Fn,
getA: function () {},
getB: function () {}
};
// 批量给原型设置属性方法的时候:设置别名
let proto = Fn.prototype;
proto.getA = function () {}
proto.getB = function () {}
proto.getC = function () {}
proto.getD = function () {}
四、构造函数显示设置返回值
如果我们手动修改构造函数的返回值时;
- 如果 return 一个基本数据类型的值,没有任何影响,不会覆盖原有实例;
- 如果 return 引用数据类型,原有的实例就会背这个引用类型值覆盖
注意:慎重修改改造函数的返回值
五、调用和普通调用的区别
var address = ‘ ‘ 像这种,在实例中。这就是一个私有变量,不会添加到实例中。只有通过 this.xxx = xxx 这种方式才能将属性添加到实例中
new 操作符可以让函数执行,通过 new 调用,这个函数就会被当作构造函数对待,返回一个实例对象; new 执行和函数普通执行有很明显的区别。之所以会有这种区别,是因为函数的普通执行和 new 执行有很大区别
- 普通函数执行机制
- 开辟私有作用域
- 形参赋值
- 变量提升
- 代码执行
- 销毁栈内存(特殊情况外)
- new 构造函数执行:
- 开辟作用域
- 形参赋值
- 变量提升
- 隐式创建一个属于当前这个类的实例对象,然后把 this 指向这个实例对象
- 执行构造函数中的代码;如果 this.xxx = xxx; 就是给实例添加私有属性
- 隐式返回这个实例对象,相当于 return this;
- 销毁栈内存(构造函数的作用域销毁是否和普通函数一样)
- 构造函数中的 this,指向当前构造函数的实例对象;