`

JavaScript学习之二 — JavaScript创建对象的8种方式

阅读更多
本文列举了《JavaScript高级程序设计:第二版》书中讲到的8种创建JavaScript对象的模式,这里有英文版下载

代码里边用到的一些公用方法本文后边有附
1、最简单的方式
/******************************************************************************************
 * 1、最简单的方式
 * 缺点:如果创建类似的对象,会出现大量的重复代码
 */
var person1 = new Object();
person1.nickname = 'maitian';
person1.age = 27;
person1.address = 'bj';
person1.getNickName = function() {
	return this.nickname
};
printPerson('1、最简单的方式', person1);

输出:
1、最简单的方式	 nickname: maitian, age: 27, address: bj

2、工厂模式
/*****************************************************************************************
 * 2、工厂模式
 * 优点:针对1存在的问题,解决代码重复
 * 缺点:
 * 	a、未解决对象识别问题,即该对象是什么类型
 *  b、每次创建对象,getNickName函数都会运行一次
 * @param {String} nickname
 * @param {number} age
 * @param {String} address
 * @return {Object}
 */
function createObject(nickname, age, address) {
	var person = new Object();
	person.nickname = nickname;
	person.age = age;
	person.address = address;
	person.getNickName = function() {
		return this.nickname
	};
	return person;
}
var person2 = createObject('maitian', 27, 'sz');
printPerson('2、工厂模式', person2);

输出:
2、工厂模式	 nickname: maitian, age: 27, address: sz

3、构造函数模式
/******************************************************************************************
 * 3、构造函数模式
 * 优点:解决第2中的缺点b提到的对象识别问题
 * 缺点:
 * 	 a、如同2中的缺点b,每次创建对象,getNickName函数都会运行一次
 * 	 b、如果创建对象的时候忘记了new,直接写成了var person = Person3('nickname',24,'address'),
 * 		person则为undefined
 * @param {String} nickname
 * @param {number} age
 * @param {String} address
 */
function Person3(nickname, age, address) {
	this.nickname = nickname;
	this.age = age;
	this.address = address;
	this.getNickName = function() {
		return this.nickname;
	}
};
var person3 = new Person3('maitian', 27, 'zz');
printPerson('3、构造函数模式', person3);
printMessage("  person3 instanceof Person3: " + (person3 instanceof Person3));
// 这里person3为undefined
person3 = Person3('maitian',24,'zz');

/**
 * 3.1、对构造函数模式的改进之一
 * 优点:解决了3中缺点a存在的每次创建对象都创建一个getNickName函数问题
 * 缺点:
 *  a、创建一个全局函数,别的地方几乎不会用到的
 *  b、依然存在3中缺点b
 * @param {String} nickname
 * @param {number} age
 * @param {String} address
 */
function Person31(nickname, age, address) {
	this.nickname = nickname;
	this.age = age;
	this.address = address;
	this.getNickName = getNickName;
};
var getNickName = function(){
	return this.nickname;
};
var person31 = new Person31('maitian', 27, 'zz');
printPerson('\t3.1、对构造函数模式的改进之一', person31);
printMessage("\t  person31 instanceof Person31: " + (person31 instanceof Person31));

//《JavaScript高级程序设计》第二版第18.1.1节中给出的解决方法
/**
 * 3.2、对构造函数模式的改进之二
 * 优点:解决了3中缺点b
 * 缺点:如3中缺点a
 * @param {} nickname
 * @param {} age
 * @param {} address
 * @return {}
 */
function Person32(nickname, age, address) {
	if (this instanceof Person32) {
		this.nickname = nickname;
		this.age = age;
		this.address = address;
		this.getNickName = function() {
			return this.nickname;
		}
	} else {
		return new Person32(nickname, age, address);
	}

};
var person32 = new Person32('maitian', 27, 'zz');
printPerson('\t3.2、对构造函数模式的改进之二', person32);
printMessage("\t  person32 instanceof Person32: " + (person32 instanceof Person32));

输出:
3、构造函数模式	 nickname: maitian, age: 27, address: zz
  person3 instanceof Person3: true
	3.1、对构造函数模式的改进之一	 nickname: maitian, age: 27, address: zz
	  person31 instanceof Person31: true
	3.2、对构造函数模式的改进之二	 nickname: maitian, age: 27, address: zz
	  person32 instanceof Person32: true

4、原型模式
/*****************************************************************************************
 * 4、原型模式
 * 优点:解决3.1中缺点里提到的全局函数问题
 * 缺点:
 *   a、如果用字面量重设原型,则在重设原型之前创建的对象所指向的原型不会被改为这个字面量对象,如4.1所示
 *   b、通过这种方式创建的对象的默认字段值都相同
 *   c、如果原型的字段是引用类型,那么所有实例都指向同一引用,一个实例的修改别的实例也能看到
 */
var Person4 = function() {
};
Person4.prototype = {
	constructor : Person4,
	nickname : 'maitian',
	age : 27,
	address : 'gw',
	jobs : ['designer','farmer'],
	getNickName : function() {
		return this.nickname;
	}
};
var person41 = new Person4();
var person42 = new Person4();
printPerson('4、原型模式 - before person41.jobs.push', person41);
printPerson('4、原型模式 - before person41.jobs.push', person42);
printMessage("  person41 instanceof Person4: " + (person41 instanceof Person4));
person41.jobs.push('programmer');
printPerson('4、原型模式 - after person41.jobs.push', person41);
printPerson('4、原型模式 - after person41.jobs.push', person42);
/**
 * 4.1、原型模式存在的问题之一
 * 	如果用字面量重设原型,则在重设原型之前创建的对象所指向的原型不会被改为这个字面量对象,如4.1所示
 */
var Person41 = function() {
};
var person41 = new Person41();
Person41.prototype = {
	constructor : Person41,
	nickname : 'maitian',
	age : 27,
	address : 'gw',
	getNickName : function() {
		return this.nickname;
	}
};
printPerson('\t4.1、原型模式存在的问题之一', person41);
//注意连这里的instanceof都返回为false
printMessage("\t  person41 instanceof Person41: " + (person41 instanceof Person41));

/**
 * 4.2、原型模式存在的问题之一的解决办法
 * 	不用字面量设置原型
 */
var Person42 = function() {
};
var person42 = new Person42();
Person42.prototype.nickname = 'maitian';
Person42.prototype.age = 27;
Person42.prototype.address = 'gw';
Person42.prototype.getNickName = function() {
	return this.nickname;
};
printPerson('\t4.2、型模式存在的问题之一的解决办法', person42);
printMessage("\t  person42 instanceof Person42: " + (person42 instanceof Person42));

/**
 * 4.3、原型模式存在的问题之二
 *  通过这种方式创建的对象的默认字段值都相同,并且这些属性都是原型的属性而非实例的属性
 */
var Person43 = function() {
};
Person43.prototype.nickname = 'maitian';
Person43.prototype.age = 27;
Person43.prototype.address = 'gw';
Person43.prototype.getNickName = function() {
	return this.nickname;
};
var person431 = new Person43();
var person432 = new Person43();
printPerson('\t4.3、型模式存在的问题之二', person431);
printPerson('\t4.3、型模式存在的问题之二', person432);
printMessage("\t  person431 instanceof Person42: " + (person431 instanceof Person43));
printMessage("\t  'nickname' in Person43: - before (person431.nickname = 'yueye') " + ('nickname' in Person43));
printMessage("\t  person431.hasOwnProperty('nickname'): - before (person431.nickname = 'yueye') " + person431.hasOwnProperty('nickname'));
person431.nickname = 'yueye';
printMessage("\t  'nickname' in Person43: - after (person431.nickname = 'yueye') " + ('nickname' in Person43));
printMessage("\t  person431.hasOwnProperty('nickname'): - after (person431.nickname = 'yueye') " + person431.hasOwnProperty('nickname'));

输出:
4、原型模式 - before person41.jobs.push	 nickname: maitian, age: 27, address: gw,jobs: designer,farmer
4、原型模式 - before person41.jobs.push	 nickname: maitian, age: 27, address: gw,jobs: designer,farmer
  person41 instanceof Person4: true
4、原型模式 - after person41.jobs.push	 nickname: maitian, age: 27, address: gw,jobs: designer,farmer,programmer
4、原型模式 - after person41.jobs.push	 nickname: maitian, age: 27, address: gw,jobs: designer,farmer,programmer
	4.1、原型模式存在的问题之一	 nickname: undefined, age: undefined, address: undefined
	  person41 instanceof Person41: false
	4.2、型模式存在的问题之一的解决办法	 nickname: maitian, age: 27, address: gw
	  person42 instanceof Person42: true
	4.3、型模式存在的问题之二	 nickname: maitian, age: 27, address: gw
	4.3、型模式存在的问题之二	 nickname: maitian, age: 27, address: gw
	  person431 instanceof Person42: true
	  'nickname' in Person43: - before (person431.nickname = 'yueye') false
	  person431.hasOwnProperty('nickname'): - before (person431.nickname = 'yueye') false
	  'nickname' in Person43: - after (person431.nickname = 'yueye') false
	  person431.hasOwnProperty('nickname'): - after (person431.nickname = 'yueye') true

5、构造函数模式和原型模式的组合
/*****************************************************************************************
 * 5、构造函数模式和原型模式的组合
 * 优点:
 * 	a、解决4中缺点b
 * 	b、解决4中缺点c
 * 缺点:
 * 	a、如果原型对象通过字面量创建依然存在4中缺点a的问题
 *  b、构造函数和原型需要分开写,比起Java语言,不够简洁
 */
function Person5(nickname, age, address) {
	this.nickname = nickname;
	this.age = age;
	this.address = address;
	this.jobs = ['designer','farmer'];
};
Person5.prototype.getNickName = function() {
	return this.nickname;
};
var person5 = new Person5('maitian', 27, 'zz');
var person52 = new Person5('maitian2', 27, 'zz2');
printPerson('5、构造函数模式和原型模式的组合 - before person5.jobs.push', person5);
printPerson('5、构造函数模式和原型模式的组合 - before person5.jobs.push', person52);
printMessage("\t  person5 instanceof Person5: " + (person5 instanceof Person5));
printMessage("\t  'nickname' in Person5: - before (person5.nickname = 'yueye') " + ('nickname' in Person5));
printMessage("\t  person5.hasOwnProperty('nickname'): - before (person5.nickname = 'yueye') " + person5.hasOwnProperty('nickname'));
person5.jobs.push("programmer");
printPerson('5、构造函数模式和原型模式的组合 - after person5.jobs.push', person5);
printPerson('5、构造函数模式和原型模式的组合 - after person5.jobs.push', person52);

输出:
5、构造函数模式和原型模式的组合 - before person5.jobs.push	 nickname: maitian, age: 27, address: zz,jobs: designer,farmer
5、构造函数模式和原型模式的组合 - before person5.jobs.push	 nickname: maitian2, age: 27, address: zz2,jobs: designer,farmer
	  person5 instanceof Person5: true
	  'nickname' in Person5: - before (person5.nickname = 'yueye') false
	  person5.hasOwnProperty('nickname'): - before (person5.nickname = 'yueye') true
5、构造函数模式和原型模式的组合 - after person5.jobs.push	 nickname: maitian, age: 27, address: zz,jobs: designer,farmer,programmer
5、构造函数模式和原型模式的组合 - after person5.jobs.push	 nickname: maitian2, age: 27, address: zz2,jobs: designer,farmer

6、动态原型模式
/*****************************************************************************************
 * 6、动态原型模式
 * 优点:适当缓解了5中的缺点b
 * 缺点:依然存在5中的缺点a,不能用字面量重置原型对象
 */
function Person6(nickname, age, address) {
	this.nickname = nickname;
	this.age = age;
	this.address = address;
	if (!this.getNickName) {
		Person6.prototype.getNickName = function() {
			return this.nickname;
		}
	}
};
var person6 = new Person6('maitian', 27, 'gz');
printPerson('6、动态原型模式', person6);
printMessage("\t  person6 instanceof Person6: " + (person6 instanceof Person6));
printMessage("\t  'nickname' in Person6:" + ('nickname' in Person6));
printMessage("\t  person6.hasOwnProperty('nickname'): " + person6.hasOwnProperty('nickname'));

输出:
6、动态原型模式	 nickname: maitian, age: 27, address: gz
	  person6 instanceof Person6: true
	  'nickname' in Person6:false
	  person6.hasOwnProperty('nickname'): true

7、寄生构造函数模式
/*****************************************************************************************
 * 7、寄生构造函数模式
 *	优点:可以快速创建对象
 *  缺点:
 *   a、如同2的缺点a,未能解决对象的识别问题
 *   b、如同2的缺点b,每创建一个对象都会运行一次function
 *   c、如果适用new去创建对象,实际上是创建了两个对象
 */
function Person7(nickname,age,address){
	var person = new Object();
	person.nickname = nickname;
	person.age = age;
	person.address = address;
	person.getNickName = function() {
		return this.nickname
	};
	return person;
};
var person7 = new Person7('maitian', 27, 'tj');
printPerson('7、寄生构造函数模式', person7);
printMessage("\t  person7 instanceof Person7: " + (person7 instanceof Person7));
printMessage("\t  'nickname' in Person7: " + ('nickname' in Person7));
printMessage("\t  person7.hasOwnProperty('nickname'): " + person7.hasOwnProperty('nickname'));
//这里不用new跟用new是一样的
person7 = Person7('maitian', 27, 'tj');
printPerson('7、寄生构造函数模式存在问题之一的验证', person7);


输出:
7、寄生构造函数模式	 nickname: maitian, age: 27, address: tj
	  person7 instanceof Person7: false
	  'nickname' in Person7: false
	  person7.hasOwnProperty('nickname'): true
7、寄生构造函数模式存在问题之一的验证	 nickname: maitian, age: 27, address: tj

8、稳妥构造函数模式
/*****************************************************************************************
 * 8、稳妥构造函数模式
 * 优点:适合用在安全环境,即不能适用this和new的环境
 * 缺点:
 *  a、如同2的缺点a,未能解决对象的识别问题
 *  b、如同2的缺点b,每创建一个对象都会运行一次function
 *  c、可以不使用new创建,如果用new则存在7中缺点c
 */
function Person8(nickname,age,address){
	var person = new Object();
	person.getNickName = function() {
		return nickname
	};
	person.getAge = function(){
		return age;
	};
	person.getAddress = function(){
		return address;
	};
	return person;
};
var person8 = new Person8('maitian', 27, 'km');
printPerson('8、稳妥构造函数模式', person8);
printMessage("\t  person8 instanceof Person8: " + (person8 instanceof Person8));
printMessage("\t  'nickname' in Person8: " + ('nickname' in Person8));
printMessage("\t  person8.hasOwnProperty('nickname'): " + person8.hasOwnProperty('nickname'));

输出:
8、稳妥构造函数模式	 nickname: maitian, age: 27, address: km
	  person8 instanceof Person8: false
	  'nickname' in Person8: false
	  person8.hasOwnProperty('nickname'): false



附:用到的公用方法
/*--------------------------一些工具方法-----------------------*/
/**
 * Chrome浏览器不支持doPrintPerson函数,会抛出Uncaught TypeError: Illegal invocation异常,不知道为何
 */
var isChrome = function() {
	return /\bchrome\b/.test(navigator.userAgent.toLowerCase());
}();
function printPerson(type, person) {
	var nickname = person.getNickName ? person.getNickName() : person.nickname,
		age = person.getAge ? person.getAge() : person.age,
		address = person.getAddress ? person.getAddress() : person.address,
		jobs = person.getJobs ? person.getJobs() : person.jobs ;
	var message = type + "\t nickname: " + nickname + ", age: " + age + ", address: " + address;
	if(jobs){
		message += ",jobs: " + jobs.join(',');
	}
	printMessage(message);

};
/**
 * 延迟载入函数
 * 
 * @param {}  message
 */
var printMessage = function(message) {
	if (isChrome) {
		//chrome不支持延迟载入函数
		console.info(message);
	} else {
		if (typeof console == 'object') {
			// ie8, chrome, firefox, safari
			printMessage = console.info;
		} else if (typeof opera == 'object') {
			// opera
			printMessage = opera.postError;
		} else if (typeof java == 'object' && typeof java.lang == 'object') {
			// LiveConect java控制台,firefox,safari,opera支持此方式
			printMessage = java.lang.System.println;
		} else {
			printMessage = function() {
				alert('printPerson无法将信息输出到控制台');
			}
		}
		printMessage(message);
	}
};
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics