javascript -- 创建对象 工厂模式和构造函数模式

创建对象

  一般情况下,我们可以使用Object构造函数或者对象字面量来创建对象, let obj1 = new Object () let obj2 = {}, 但是这些方式有着明显的不足之处, 当我们要创建有同样接口的多个对象的时候我们就要重复编写很多代码。

  在ES6的类出现之前, ES5.1的构造函数加原型继承同样支持了面向对象的结构。

工厂模式

function createPerson(name, age, job) {
	let o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function() {
		console.log(this.name);
	};
	return o;
}
let person1 = createPerson("Nicholas", 29, "Software Engineer");
let person2 = createPerson("Greg", 27, "Doctor");

  工厂模式可以解决创建多个类似对象的问题, 但没有解决对象标志的问题(即新创建的对象是什么类型的问题)。

构造函数模式

function Person(name, age, job){
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = function() {
		console.log(this.name);
	};
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
和工厂模式的区别
  • 没有显式地创建对象
  • 属性和方式都直接赋值给了 this
  • 没有 return
  • 构造函数名称的首字母大写, 非构造函数名称首字母小写
  • 确保了实例被标志为特定类型 (例如上述代码中 person1的类型被标志为 Person )
创建一个构函数的实例, 使用 new 操作符

  当我们 new 一个构造函数的时候, 会发生下列操作

  1. 在内存中创建一个新的对象
  2. 这个新对象内部的 [[Prototype]] 特性被赋值为构造函数的 prototype 属性
  3. 构造函数内部的 this 被赋值给这个新对象 (this 指向新对象)
  4. 执行构造函数内部的代码 (给新对象添加属性)
  5. 如果构造函数返回一个非空的对象,则返回该对象; 否则返回刚创建的新对象
构造函数模式标志了对象类型

   上述代码中, person1 和 person2 分别保存着 Person 的不同实例, 这两个对象都有一个 constructor 属性指向了 Person
  constructor 本来是用于标识对象类型的。不过,一般认为 instanceof 操作符是确定对象类型更可靠的方式。前面例子中的每个对象都是 Object 的实例,同时也是 Person 的实例,如下面调用instanceof 操作符的结果所示

consoel.log(person1.constructor == Person) // true
consoel.log(person2.constructor == Person) // true
console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true
构造函数也是函数

  构造函数和普通函数唯一的区别就是调用方式不同, 任何函数只要使用 new 操作符调用就是构造函数, 而不使用 new 操作符调用的就是普通函数。

// 作为构造函数
let person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); // "Nicholas"
// 作为函数调用
Person("Greg", 27, "Doctor"); // 添加到 window 对象
window.sayName(); // "Greg"
// 在另一个对象的作用域中调用
let o = new Object();
Person.call(o, "Kristen", 2

  这个例子一开始展示了典型的构造函数调用方式,即使用 new 操作符创建一个新对象。然后是普通函数的调用方式,这时候没有使用 new 操作符调用 Person() ,结果会将属性和方法添加到 window 对象。
  这里要记住,在调用一个函数而没有明确设置 this 值的情况下(即没有作为对象的方法调用,或者没有使用 call() / apply() 调用), this 始终指向 Global 对象(在浏览器中就是 window 对象)。;因此在上面的调用之后, window 对象上就有了一个 sayName() 方法,调用它会返回 “Greg” 。
  最后展示的调用方式是通过 call() (或 apply() )调用函数,同时将特定对象指定为作用域。这里的调用将对象 o 指定为 Person() 内部的 this 值,因此执行完函数代码后,所有属性和 sayName() 方法都会添加到对象 o 上面。

构造函数的问题

  构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍, 因此不同实例上的函数虽然同名却不相等,例如:

console.log(person1.sayName == person2.sayName)  //false

   因为都是干同一件事情, 没有必要定义两个不同的 Function 实例, 况且 this 对象可以把函数与对象的绑定推迟到运行时。
  要解决这个事情, 我们可以把函数定义转到构造函数的外部

function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.jon = job;
	this.sayName = sayName;
}

function sayName() {
	console.log(this.name)
}

   在这里, sayName() 被定义在了构造函数的外部, 在构造函数内部, sayName属性就等于 全局的sayName() 函数。因为这次的 sayName 属性只是包含一个指向外部函数的指针, 所以 person1 和 person2 共享了定义在全局作用域上的 sayName() 函数。
  诚然这样子解决了相同逻辑的函数重复定义的问题, 但是全局作用域也因此被捣乱了, 因为那个函数实际上只能在一个对象调用。 如果这个对象需要多个方法,那么就要在全局定义多个函数, 就会导致自定义类型引用的代码不能很好的聚集在一起。 这个新的问题可以通过原型模式来解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值