一、单例设计模式
把描述当前事物特征的信息进行分组归类,减少全局变量的污染。就是一个对象:不仅仅被叫做变量,也被称为命名空间
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,指向当前构造函数的实例对象;
