javascript--面向对象(二、工厂模式,构造函数,原型模式)

一、前言

上一篇介绍了对象的基本简单的概念和对象的数据属性和访问器属性,以及对这两种属性类型的一些操作,接来说一下与创建对象的模式。

二、创建对象的方式

(1)工厂模式

这种模式就是通过创建一个Object对象,并将属性和方法保存在Object对象中,将Object对象返回。

function createPerson(name, age, job){
            var obj = new Object();//通过创建Object来保存传递进来的属性
            obj.name = name;
            obj.age = age;
            obj.job = job;
            obj.sayName = function(){
                console.log(this.name);
            }
            return obj;//返回创建对象
        }

        var person1 = createPerson('jiang', 12, 'software');
        person1.sayName();

在上面的例子中,声明一个createPerson()的函数,这个函数接受三个参数,而在函数体中实例化一个Object对象,来接收传递进来的参数,最后将创建的对象返回,这样子就能获得一个person对象。这种模式是一种广为人知的设计者模式,抽象了创建具体对象的过程。这种模式虽然解决了创建多个相似对象的问题,但是并没有结局对象识别的问题。而接下来的构造函数就解决了这样的问题

(二)构造函数模式

在大多数的面向对象编程,构造函数主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载(在javascript中并没有重载这个概念)。构造函数模式解决了对象识别的问题,它可以用来创建特定类型的对象。

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

这种方式与工厂模式相比

  1.没有显示的创建对象
  2.直接将属性和方法赋给了this对象
  3.没有return语句

var person = new Person('jiang', 13, 'sfs');
person.sayName(); 

 通过new操作符创建实例,需要经历一下的步骤
   -> 创建一个新对象
   -> 将构造函数的作用域赋给了新对象,(因此this也就指向了新对象)
   -> 执行构造函数中的代码(为该新对象添加属性)
  -> 返回新对象

在两个不同的实例,虽然是两个不同的实例,但这两个对象都有一个constructor属性指向Person,所以也可以看出,构造函数模式对于工厂模式在于对象识别的区别

var person2 = new Person('zhen', 13, 'sfs');
person2.sayName();
console.log(person.constructor == Person);//true
console.log(person2.constructor == Person);//true

所谓构造函数模式,其实也是函数。跟其他函数的区别就在于调用的方式,任何函数通过new操作符来调用的,就是构造函数,如果不是通过new操作符调用的就是普通的函数。

当然构造函数并不是完美的,其问题就在于,每个方法都要在实例上重新创建一次,也就是说两个实例的方法是不相等的。这样使得有些多余。占用内存。

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

将对象内的方法提取到构造函数外面,可以减少同一方法的重复定义。但是这种方式也不好,于是出现了一下的原型模式

(三)原型模式

prototype 每个函数都有的属性,是指向一个对象的指针,包含了可以有特定类型的所有实例共享的属性和方法,换句话说,prototype就是通过调用函数而创建的对象实例的原型对象,其好处就是可以让所有对象实例共享包含的属性和方法。

 function Person(){}
		
		Person.prototype.name = 'jiang';
		Person.prototype.age = 15;
		Person.prototype.job = 'sf';
		Person.prototype.sayName = function(){
			console.log(this.name);
	        };
		var person = new Person();
		person.sayName();         

首先声明一个空的函数。将对象的方法和属性都放到prototype属性上。这样使用构造函数的方式创建新对象,新对象也会拥有相同的属性和方法。那么有人就会问了,Person函数中分明是空的,那么为何创建出来的新对象会有这些属性和方法呢?那么首先就要了解一下什么是原型对象。

(a)原型对象

当我们创建一个新的函数的同时,与之同时prototype会作为新函数的属性被创建出来,而这个属性呢就是一个指针,会指向新函数的原型对象。而在默认情况下原型对象就会获得一个constructor(构造函数)属性,这个属性又指向了我们创建的新函数。上面的例子Person.prototype.constructor,就是我们的Person上面例子用一张图片来说明一下

 Person函数中的prototype属性指向了Person.prototype,同时Person.prototype.constructor又指向了Person函数,当我们实例化person1对象时,这个对象内部也含有一个prototype指针指向了Person.prototype对象。

在上面的例子中。Person对象的所有属性和方法都赋值给了原型对象,也就是在Person对象并没有这些属性,所以实例出来的person1对象中也没有这些属性,只有一个指向原型对象的指针,那么实例化出来的对象不是相当与没有东西。但是如果person1.name又是有数据的。这就涉及到了原型模式的搜索机制

(b)原型模式的搜索机制

原型对象的遍历方式,先从实例本身开始,搜索给定的属性的名称,所有实例中是否有这个属性的值,如果有就返回,如果没有就所有原型中是否存在,存在就返回这个属性的值
原型中会存在屏蔽原理,当我们为实例添加一个属性是,这个属性就会屏蔽原型中同名的属性,也就是当我们访问实例中的某个属性时,如果该属性存在,就会返回该属性不会在搜索原型中的同名属性属性,如果使用delete操作符删除实例中的属性时,就会回复对原型中同名属性的连接。

(c)原型模式的简写方式

Person.prototype = {
			name: 'jiang',
			age: 14,
			job: 'sf',
			sayName: function(){
				console.log(this.name);
			}
		}    

  

以上重写后,起原型的constructor不再指向Person,而是指向了Object,因为这样设置的话就相当与一个以字面量形式创建的新对象,在本质上已经完全重写了默认的prototype对象,因此prototype的constructor不再指向原来的Person。除非自己指定constructor:Person。

//或者重设构造函数
		Object.defineProperty(Person.prototype, 'constructor', {
			enumerable:false,
			value: Person
		});

这样的写法,就可以将原型对象中的构造函数属性重新指向了Person。

原型模式虽然解决了工厂模式和构造函数模式的缺点,当仍然不是完美的模式。这次先吹水吹到这了

 

posted @ 2017-12-30 15:32  _江江江江  阅读(148)  评论(0编辑  收藏  举报