深入理解js对象4种设计模式-面试常问

发布于:2023-01-12 ⋅ 阅读:(235) ⋅ 点赞:(0)

在这里插入图片描述

1. javascript 工厂模式

什么是工厂模式?为什么需要工厂模式?工厂模式的作用?

  1. 工厂模式是一种创建型模式,简单来说,工厂模式就是创建对象的一种模式
  2. 当我们需要创建大量的对象,并且每个对象都有相同的属性时,如果我们把每个对象都单独创建,就会造成代码的大量冗余。因此我们需要用一些更便捷,节省时间和内存空间的方法,去创造大量的对象。
  3. 工厂模式的作用:
    创建对象;降低代码冗余度。
    工厂模式就相当于一个生产车间,你把零件的信息都设置好丢进工厂里,工厂就会帮你把零件造出来。

1.1 使用工厂模式创建对象

//将创建对象的代码封装在一个函数中
function createPerson(name, age, gender) {
  var person = new Object();
  person.name = name;
  person.age = age;
  person.gender = gender;
  person.sayName = function () {
    console.log(this.name);
  }
  return person;
}
//利用工厂函数来创建对象
var person1 = createPerson("zhangsan", 18, 'male');
var person2 = createPerson("lisi", 20, 'female');

**优点:**只要我们在利用工厂函数创建对象时,给函数传入参数就行
**缺点:**这种方式本质上是将创建对象的过程进行了封装,本质并没有改变,我们创建一个student时无法知道其具体的数据类型,只知道这是一个对象,往往实际开发中我们需要确定这个对象到底是个Person的实例还是Dog的实例。

2. 构造函数模式

ECMAScript 中的构造函数是用于创建特定类型对象的。像 Object 和 Array 这样的原生构造函数,运行时可以直接在执行环境中使用。当然也可以自定义构造函数,以函数的形式为自己的对象类型定义属性和方法。
JavaScript中可以自定义构造函数,从而自定义对象类型的属性和方法,构造函数本身也是函数,只不过可以用来创建对象。
1.1的案例可以用构造函数这样写:

// 自定义构造函数
function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = function () {
    console.log(this.name);
  }
}

var person1 = new Person('zhangsan', 29, 'male');
var person2 = new Person('lisi', 19, 'female');

person1.sayName(); // zhangsan
person2.sayName(); // lisi

在自定义构造函数中,Person()代替了createPerson()工厂函数。Person()内部函数的代码和createPerson()基本是一样的,但有以下区别:

  • 没有显示地创建对象
  • 属性和方法直接赋值给this
  • 没有return 返回值
    使用构造函数一般建议:函数名称首字母大写,非构造函数则以小字母开头。这有助于我们区分构造函数和普通函数。

3. 原型模式

每个函数都会创建一个prototype属性,如下图:
在这里插入图片描述
这个属性是一个对象,包含由特定应用类型的实例共享的属性和方法。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型。

function Person(){}
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
person1.sayName(); // zhangsan 

var person2 = new Person();
person2.sayName();  // zhangsan 
console.log(person2);  // Person {}

在通过实例对象访问属性时,搜索的开始就是先在对象本身上寻找。如果在这个实例上发现了给定的属性或名称,就返回对应的属性值。如果没有找到目标属性,搜索就会沿着指针进入对象的原型,在原型对象上找到属性后,再返回对应的值
因此再上述案例中,调用**person1.sayName()时,会发生两步搜索。首先,JavaScript 引擎会问:“person1 实例有 sayName 属性吗?”答案是有。**然后,继续搜索并问:“person1 的原型有 sayName 属性吗?”答案是有。于是就返回了保存在原型上的这个函数。在调用 person2.sayName()时,会发生同样的搜索过程,而且也会返回相同的结果。这就是原型用于在多个对象实例间共享属性和方法的原理。

3.1 原型层级

虽然通过实例对象,我们可以拿到原型对象的值。但是我们却不可能通过实例重写这些值。如果在实例上添加了一个与原型对象中同名的属性,那就会在实例上创建这个属性,而这个属性会遮住原型对象上的属性。

function Person() { }
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "lisi";
console.log(person1.name); // lisi,来自实例
console.log(person2.name); // zhangsan,来自原型

以上案例中,person1的name属性遮蔽了原型对象上的同名属性。虽然 person1.name 和person2.name 都返回了值,但前者返回的是"lisi"(来自实例),后者返回是"zhangsan"(来自原型)。
返回值时依然遵循着两个步骤:①先在实例上搜索这个属性,如果实例上存在该属性,则返回实例上该属性的值。不再访问原型。②若实例上没有该属性,则顺着指针往原型上寻找,原型上有该属性,则返回原型上的属性值;若原型上没有值,返回undefined

如何解决实例遮盖(shadow)原型对象上的同名属性问题?

通过delete操作符删除实例上的属性,从而让标识符解析过程能够继续搜索原型对象。

function Person() { }
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "lisi";
console.log(person1.name); // lisi,来自实例
//解决遮盖问题
delete person1.name;
console.log(person1.name); // zhangsan,来自原型
console.log(person2.name); // zhangsan,来自原型

4. 组合模式

组合模式是指使用构造函数模式和原型模式
构造函数用于定义实例私有属性,原型模式用于定义实例共有的属性和方法。
这种模式是目前在ECMAScript中使用最广泛,认同度最高的一种创建自定义类型的方法。

function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.firends = ['zhangsan', 'lisi'];
}
Person.prototype = {
//让实例的原型,指向Person构造函数
  constructor: Person, 
  sayName: function () {
    console.log(this.name);
  }
};
var p1 = new Person('larry', 44, 'male');
var p2 = new Person('terry', 39, 'male');

p1.firends.push('robin');
console.log(p1.firends); // [ 'zhangsan', 'lisi', 'robin' ]
console.log(p2.firends); // [ 'zhangsan', 'lisi' ]
console.log(p1.firends === p2.firends); // false
console.log(p1.sayName === p2.sayName); // true

5. 4种设计模式的优缺点

  1. 工厂模式
    优点:封装了创建对象的函数,可以批量创建对象,提高了代码复用率
    缺点:无法区分创建出来的对象种类,函数中的方法,在对象每次创建出来时都会重新创建,造成方法冗余
  2. 构造函数模式
    优点:相比于工厂模式可以区分种类
    缺点:方法依旧冗余
  3. 原型模式
    优点:方法不冗余
    缺点:所有的属性和方法都放在原型对象中,无法通过实例重写值
  4. 组合模式
    优点:构造函数中放实例私有的属性, 构造函数原型对象中放共有属性和方法。方法不冗余。目前使用最广泛,认可度最高。
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到