上一章,我们说到了对象的一些基本的属性,这一章,我们来看下如何去创建一个对象。
在js中创建对象有两种方式,一种是单纯的通过对象字面量的方式,一种是复杂的方式。
1.对象字面量
通过定义变量饿方式来创建一个对象。以上一章的房子图纸为例
var house = new Object();
house.name = '二锅头的房子';
house.money = 2000;
这样就创建了一个house对象,当时这种方法存在一个问题,那就是如果我们要创建多个house,我们就需要重复大量的代码,这样会造成代码的冗余。因此我们有了一系列的复杂的方式
2. 复杂方式
2.1 工厂模式
工厂模式是在所有的程序设计中一种广为人知的设计模式,通过一个类来抽象创建具体对象的过程。由于js中无法创建类,因此我们需要通过一个函数,在函数中封装特定的对象。就跟工厂一样,外人不需要知道如何生产,只需要给出需要的东西,工厂就会生产出所需要的的东西。同样以上面的房子为例
function createHouse(name,money) {
var obj = new Object();
obj.name = name;
obj.money = money;
obj.showInfo = function() {
console.log(obj.name + '价格是:' + obj.money);
}
return obj;
}
var house = createHouse('二锅头的房子',20000000);
这样我们就创建了一个house对象,但是用这种方法会有一个问题,那就是我们无法获取这个对象的类型,因为无论怎么判断,返回的永远是object.因此有了通过构造函数模式的方法
2.2 构造函数方法
在js中,针对Object底层有默认的构造函数,当我们通过new来创建对象的时候,底层会自动执行构造函数。从而创建对象。我们来看下面的例子
var house = new Object();
house.name = '来瓶二锅头';
上面的方法,在底层的时候,会经历以下4个步骤
- 创建一个虚拟的新对象
- 将Object的内置构造函数赋值给新对象
- 执行内置构造函数的代码
- 返回新对象给house
同时js也支持我们自己自定义构造函数,从而通过在构造函数中自定义对象的属性以及方法。因此我们可以借助此方法来通过构造函数方法来创建对象。同样以房子为例
function createHouse(name,money) {
this.name = name;
this.money = money;
this.showInfo = function() {
console.log(this.name + '价格是:' + this.money);
}
}
var house = new createHouse('二锅头的房子',20000000);
var house2 = new createHouse('二锅头的房子2',2000000000);
同样的我们可以创建对象,同样的我们来看下执行步骤
- 通过new操作符创建一个新对象。
- 将构造函数的作用域赋值给新对象,因此this指向的为新的对象
- 执行构造函数,也就是createHouse方法
- 返回新对象给house
我们知道在工厂模式的时候是因为无法判断对象类型,所以我们就有了构造函数方法。那么我们如何判断呢?
在上面我们说过了每个对象都有构造函数,因此我们可以通过对象属性constructor来判断。所以我们看下以下代码
house.constructor == createHouse; // true
house2.constructor == createHouse;// true
结果都为true,因此我们可以通过constructor属性来获取对象类型(因为constructor指向的是createHouse)。
但是构造函数方式会存在一个弊端,那就是我们每创建一个实例的时候,内部的方法都需要实例化一下,同样以上面的例子,每创建一个house,我们就需要实例化一个showInfo方法,这样同样容易造成代码的冗余,因此我们就有了另一种方法,通过原型的模式
2.3 原型模式
要明白如何通过原型的方法创建对象,首先我们需要知道什么是原型,以及原型的原理。下面我们来一一剖析。
在构造函数方法里我们说过了我们会将构造函数赋值给新对象,那么如何赋值的呢?其实就是通过原型的方式。因为在我们所有的对象中都有一个property的属性。这个属性是一个指向一个对象的属性(有些书中会说是一个指针,但是实际上js中没有指针的概念,只是其他语言中用习惯了。)因此我们可以将所有共享的属性和方法都放在这个属性下,当通过构造函数指向这个属性的时候,我们创建对象的时候就可以使用所有的属性和方法,而不必都放在构造函数中。还是有点绕。我们依旧以上面的例子来看看
function House() {}
House.property.name = '二锅头的房子';
House.property.money = '2000000';
House.property.showInfo = function(){
console.log(this.name + '价格是:' + this.money);
}
var house = new House();
house.showInfo();
var house2 = new House();
house2.showInfo();
我们可以看到其实打印出来的结果是一样的,说明我们都是调用的同一个函数,因此构造函数方式存在的问题解决了。下面我们来一点点的来看下上面的方法如何实现的
- 通过new House()方法,创建一个对象,我们假定对象名为obj.
- 将构造函数constructor赋值给新对象,也就constructor(House方法)指向obj.
- 执行构造函数
- 将原型上的属性和方法以及执行过的构造函数一并返回给house
上面4步,可以用下图进行表示
image.png
我们来一一说明
- new House创建一个House对象,此对象拥有一个构造函数,House方法,同时其拥有一个原型(property)属性,此原型拥有2个属性,name,money以及一个方法showInfo
- 将构造函数赋值给House对象,也就是通过constructor指向House
- 执行构造函数方法,
- 将对象返回给house实例,也就是通过house实例的原型指向House对象即可
因此其实上面的方法我们还可以通过另一种事项方法
function House() {}
House.property = {
constructor:House,
name: '二锅头的房子',
money:2000000,
showInfo: function(){
console.log(this.name + '价格是:' + this.money);
}
}
var house = new House();
house.showInfo();
var house2 = new House();
house2.showInfo();
通过这种方式应该会可以更加清楚明白上面的解释了。通过原型的方式解决了上面构造函数的问题,但是依旧会存在一个问题,那就是由于属性是完全共享的,如果其中一个属性是对象或者数组,就会导致两个实例的值一样,其中一个修改,另一个也同样会修改。例如
function House() {}
House.property = {
constructor:House,
name: '二锅头的房子',
money:2000000,
member:['二锅头']
showInfo: function(){
console.log(this.name + '价格是:' + this.money);
}
}
var house = new House();
house.showInfo();
var house2 = new House();
house2.showInfo();
house2.member.push('五粮液');
console.log(house.member); // 二锅头,五粮液
因此产生了另一种方式,那就是组合构造函数和原型的方式
2.4 构造函数+原型组合方式
前面我们说到了构造函数可以设置自定义属性,原型方式可以设置共享的属性以及方法,那么我们可以将其结合,这样就能满足需求了,同样以上面的为例
function House(name,money,member) {
this.name = name;
this.money = money;
this.member = member;
}
House.property = {
constructor:House,
showInfo: function(){
console.log(this.name + '价格是:' + this.money);
}
}
var house = new House();
house.showInfo();
var house2 = new House();
house2.showInfo();
house2.member.push('五粮液');
console.log(house.member); // 二锅头
此方法是当前最流行的方案。
至此常见的对象的创建就介绍完成了。还有两种不常见的方法,个人觉得没有必要去学习。所以感兴趣的可以去自己学习下哦
网友评论