3

原文链接

面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装继承多态的特性!

但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值、对象或者函数。

可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法)。

一、工厂模式

创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。

function createPerson(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.getName = function () {
    return this.name;
  }
  return o; // 使用return返回生成的对象实例
}
var person = createPerson('Jack', 19, 'SoftWare Engineer');

二、构造函数模式

function Person(name,age,job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.getName = function () {
    return this.name;
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engineer');
var person2 = new Person('Liye', 23, 'Mechanical Engineer');

构造函数模式与工厂方法区别在于:

  • 没有显式地创建对象

  • 直接将属性和方法赋值给this对象

  • 没有return语句

上述由Person构造函数生成的两个对象person1与person2都是Person的实例,因此可以使用instanceof判断,并且因为所有对象都继承Object,因此person1 instanceof Object也返回真:

console.log(person1 instanceof Person); // true;
console.log(person2 instanceof Person); // true;
console.log(person1 instanceof Object); // true;
console.log(person2 instanceof Object); // true;
console.log(person1.constructor === person2.constructor); // ture;

虽然构造函数方式比较不错,但也存在缺点,那就是在创建对象时,特别针对对象的属性指向函数时,会重复的创建函数实例,以上述代码为基础,可以改写为:

function Person(name,age,job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.getName = getName;
}
function getName() {
  return this.name;
}

三、原型模式

JS中每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。

原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。

function Person() {}

Person.prototype.name = "Jack";
Person.prototype.age = 29;
Person.prototype.getName = function () {
  return this.name;
}

var person1 = new Person();
var person2 = new Person();

console.log(person1.getName === person2.getName); // ture;

四、组合构造函数及原型模式

目前最为常用的定义类型方式,是组合构造函数模式与原型模式。

构造函数模式用于定义实例的属性,而原型模式用于定义方法共享的属性

这样,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方方法的引用,最大限度的节约内存。此外,组合模式还支持向构造函数传递参数,可谓是集两家之所长。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.lessons = ['Math', 'Physics'];
}
Person.prototype = {
  constructor: Person, // 原型字面量方式会将对象的constructor变为Object,需要强制指回Person
  getName: function () {
    return this.name;
  }
}

var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');

var person2 = new Person('Lily', 39, 'Mechanical Engneer');

console.log(person1.lessons); // ["Math", "Physics", "Biology"]
console.log(person2.lessons); // ["Math", "Physics"]
console.log(person1.getName === person2.getName); // true

五、动态原型模式

组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致。

动态原型模式将所有构造信息都封装在构造函数中,同时又保持了组合的优点。

其原理就是通过判断构造函数的原型中是否已经定义了共享的方法或属性,如果没有则定义,否则不再执行定义过程。

该方式只定义一次原型上方法或属性,且将所有构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例中:

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.lessons = ['Math', 'Physics'];
}
if (typeof this.getName) {
  Person.prototype = {
    constructor: Person, // 原型字面量方式会将对象的constructor变为Object,需要强制指回Person
    getName: function () {
      return this.name;
    }
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');

var person2 = new Person('Lily', 39, 'Mechanical Engneer');

console.log(person1.lessons); // ["Math", "Physics", "Biology"]
console.log(person2.lessons); // ["Math", "Physics"]
console.log(person1.getName === person2.getName); // true

小小赵老汉
1k 声望105 粉丝