JS创建对象的9种方式,你还在愁不知道如何创建对象吗?

什么是对象呢?ECMA-262中定义:对象是无序属性的集合,其属性值可以包含基本值、对象或者函数。

理解一下:对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。

知道什么是对象,下面我们一起来看一下创建对象的多种方式。


创建Object的实例

简介:创建对象的最简单方式:创建一个Object实例,然后添加属性和方法。早期JS开发人员使用这个模式创建对象。

var perosn = new Object();
perosn.name = 'Helen';
person.age = 23;
person.job = 'Software Engineer';
person.sayName = function(){
	alert(this.name);
}

对象字面量

简介:替代Object实例的一种创建对象的方式。

var person = {
	name: 'Helen',
	age: 23,
	job: 'Software Engineer',
	sayName: function(){
		alert(this.name);
	}
}

Object实例与对象字面量这两种创建对象的方式有一个明显的缺点:使用同一个接口创建很多对象,对产生大量的重复代码。

工厂模式

简介:抽象了创建具体对象的过程,用函数来封装特定接口创建对象的细节。

function createPerson(name, age, job){
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function(){
		alert(this.name);
	}return o;
}
var person1 = createPerson('Sam', 24, 'Software Engineer')
var person2 = createPerson('Helen', 23, 'Software Engineer')

函数createPerson()能够根据接收的参数来构建一个包含所有必要信息的Person对象,可以无数次的调用这个函数,每次都会返回一个对象(包含三个属性,一个方法)。工厂模式解决了创建多个相似对象的问题,但却没有解决对象识别的问题。

构造函数模式

简介:自定义构造函数,可以自定义对象类型的属性和方法。

function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = function() {
		alert(this.name)
	}
}
var person1 = new Person('Sam', 24, 'Software Engineer')
var person2 = new Person('Helen', 23, 'Software Engineer')

与工厂模式不同之处:①没有显式的创建对象;②直接将属性和方法赋给了this对象;③没有return语句。

构造函数名首字母大写,用来标识是构造函数。

要创建Person的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历4个步骤:
①创建一个新对象;
②将构造函数的作用域赋给新对象(this会指向新对象);
③执行构造函数中的代码(为这个新对象添加属性);
④返回新对象

perosn1和person2分别保存Person的一个不同的实例,通过对象的constructor属性,可以得出他们都是Person类型的(创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型)。

alert(person1.constructor == Person);   // true
alert(person2.constructor == Person);   // true

使用构造函数模式创建对象的问题:每个方法都要在每个实例上重新创建一遍,因为函数也是对象,因此,每定义一个函数,也就是实例化了一个对象。不同实例上有两个相同名字且相同任务的函数,是没有必要的。

alert(person1.sayName == person2.sayName)   // false

所以我们可以做如下变形:

function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}
function sayName() {
	alert(this.name)
}
var person1 = new Person('Sam', 24, 'Software Engineer')
var person2 = new Person('Helen', 23, 'Software Engineer')

我们把sayName()函数的定义转移到了构造函数外部,在构造函数内部,我们将sayName属性设置成等于全局的sayName函数,这样可以使person1和person2共享全局作用域下的sayName()函数。但是这样又有一个问题,就是全局作用域下定义的函数实际上只能被某个对象调用,如果对象需要定义很多方法,那就要定义很多个全局函数,就谈不上封装性了。

原型模式

简介:我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。

function Person(){}
Person.prototype.name = 'Helen';
Person.prototype.age = 23;
Person.prototype.sayName = function(){
	alert(this.name);
}
var person1 = new Person();
person1.sayName();   // Helen

var person2 = new Person();
person2.sayName();  // Helen

alert(person1.sayName == person2.sayName)   // true

我们将方法和属性直接添加到了person的prototype属性中,通过调用构造函数来创建的新对象具有相同的属性和方法,即新对象的属性和方法是由所有实例共享的。但是原型模式也是有缺点的:首先:它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值;其次:原型中的所有属性是被很多实例共享的,这种共享对函数非常合适,对于包含基本值的属性也可以,但是对于包含引用类型值的属性来说,问题就比较突出了。

function Person(){}
Person.prototype.name = 'Helen';
Person.prototype.age = 23;
Person.prototype.friends = ['sam', 'sunny'];
Person.prototype.sayName = function(){
	alert(this.name);
}
var person1 = new Person();
var person2 = new Person();

person1.friends.push('Eric');

alert(person1.friends);  // sam,sunny,Eric
alert(person2.friends);  // sam,sunny,Eric
alert(person1.friends === person2.friends);   // true

因为friends数组存在于Person.prototype而非person1中,所以修改person1.friends引用的数组也会修改person2.friends引用的数组。

组合使用构造函数模式和原型模式

简介:最常见的方式。构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性,另外,这种混成模式还支持向构造函数中传递参数。

function Person(name, age, job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.friends = ['sam', 'sunny'];
}
Person.prototype.sayName = function(){
	alert(this.name);
}
var person1 = new Person('Sam', 24, 'Software Engineer')
var person2 = new Person('Helen', 23, 'Software Engineer')

person1.friends.push('Eric');
alert(person1.friends);  // sam,sunny,Eric
alert(person2.friends);  // sam,sunny
alert(person1.friends === person2.friends);   // false
alert(person1.sayName == person2.sayName)   // true

实例属性在构造函数中定义,由所有实例共享的属性和方法在原型中定义,所以,修改了person1.friends并不会影响person2.friends。

动态原型模式

简介:动态原型模式把所有信息都封装在构造函数中,在构造函数中初始化原型(仅在必要的情况下)。动态原型模式可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

function Person(name, age, job) {
	// 属性
	this.name = name;
	this.age = age;
	this.job = job;
	// 方法
	if (typeof this.sayName != "function") {
		Person.prototype.sayName = function(){
			alert(this.name);
		}
	}
}
var person1 = new Person('Sam', 24, 'Software Engineer');
person1.sayName();  // Sam

只在sayName()方法不存在的情况下,才会将它添加到原型中。这段代码只会在初始调用构造函数时才会执行。

寄生构造函数模式

简介:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

function Person(name, age, job) {
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function(){
		alert(this.name);
	};
	return o;
}
var person1 = new Person('Helen', 23, 'Software Engineer')
person1.sayName()  // Helen

Person函数中创建了一个新对象,并以相应的属性和方法初始化该对象,然后又返回了这个对象。但是,返回的对象与构造函数或者与构造函数的原型属性之间没有关系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同,所以,不能依赖instanceof操作符来确定对象类型。

稳妥构造函数模式

简介:稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数。

function Person(name, age, job) {
	var o = new Object();
	o.name = name;
	o.age = age;
	o.job = job;
	o.sayName = function(){
		alert(name);
	};
	return o;
}
var person1 = Person('Helen', 23, 'Software Engineer')
person1.sayName()  // Helen

用这种模式创建的对象person1中保存的是一个稳妥对象,除了使用sayName()方法之外,没有其他办法可以访问其数据成员。同样:返回的对象与构造函数或者与构造函数的原型属性之间没有关系。


至此,创建对象的方式已经介绍完了,不知道大家了解了多少呢?

你可能感兴趣的:(#,js,●,基础篇,js,javascript,对象,原型模式)