JS创建对象方法之工厂方法,构造函数方法
我们在ECMAScript中创建对象虽然可以通过Object()构造方法或者是对象字面量的方式,但是这两种方式有缺点的:使用同一接口创建很多对象,会产生大量重复的代码。
JS创建对象之工厂模式
关于工厂模式,首先简单地从字面意思来理解,为什么叫工厂模式,而不叫其他的名字,我觉得可以这样理解。比如说这个工厂是一个加工手刹的工厂,我们是雇主,我们想要一定数目的手刹,我们只需要把订单交给工厂就可以了,不需要知道这个手刹是怎么制作的,同样工厂会给我们的是已经做好的手刹。我们只需要知道如果我们需要手刹,直接交给工厂解决就可以了。
那么,ECMAScript中创建对象的工厂模式就是这个意思,即抽象创建具体对象的过程。 由于在ECMAScript中无法创建类,因此我们可以通过函数来封装以特定接口创建对象的细节。
function createPersonalInfor(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 = createPersonalInfor("Jona",20,"student");
var person2 = createPersonalInfor("Jone",19,"software engineer");
从上面的代码可以看到,我们通过createPersonalInfor()函数,创建了两个对象,这样无疑节省了不少的代码量。但是这种方法也是有着缺点:没有解决对象识别问题(即怎样知道一个对象的类型)。
由于我们在createPersonalInfor()中的对象都是通过Object()构造函数创建的,因此不管你的信息是属于哪个对象的,当判断对象的类型时,结果都是Object。比如说我们想要对象A是Company1类型,对象B是Company2类型的,但是如果使用上面的工厂模式,我们就无法判断A和B分别是哪一个类型的。 对于此问题,出现了一个新的模式:构造函数模式
JS创建对象之构造函数模式
ECMAScript中的构造函数可以用来创建对象。类似于Object, Array这样的原生构造函数,在运行时会自动出现在执行环境中。即他们是已经存在的函数,不需要我们再去显示的写出这个构造函数,直接用就行了。 我们也可以使用自定义的构造函数,从而自定义对象类型的属性和方法。
function Person(name,age,job)
{
this.name = name;
this.age = age;
this.job = job;
this.sayName = function ()
{
alert(this.name);
}
}
var person1 = new Person("Jona",20,"student");
var person2 = new Person("Jone",19,"software engineer");
从这个例子中,我们可以看到与工厂模式时的不同:
- 没有显示创建对象
- 直接将属性和方法赋给this对象
- 没有return 语句
在这里注意一种写法,我们这里自定义的构造函数的函数名首字母大写,非构造函数首字母小写,这样主要是为了区别来构造函数和非构造函数。毕竟,构造函数也还是函数。
我们在创建自定义对象时,使用了new操作符。那么在构造函数调用时,会经历以下几个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this就指向了该新对象)
- 执行构造函数中的代码
- 返回新对象
我们可以通过person1.constructor 来得知该对象的类型,但是如果已知有哪几种类型,也可以通过instanceof来判断对象类型。
1.将构造函数当作函数
构造函数与其他函数的唯一不同就是调用方式不同。但构造函数也还是函数,没有定义构造函数的特殊语法。任何函数,只要通过new操作符来点用,就可以作为构造函数。而任何函数,没有通过new操作符调用,那么就是普通函数。
var person = new Person("jona",20,"student");
person.sayName(); //Jona
Person("Jone",19,"nurse"); //添加到了window中,当成了普通函数调用
window.sayName(); //Jone
//在别的对象中调用
var o = new Object();
Person.call(o,"Ravi",30,"teacher");
o.sayName(); //Ravi
2. 构造函数的问题
那么,构造方法模式有没有缺点呢? 答案是肯定的。
不知道有没有发现,正如上面我们自定的构造函数,如果构造函数里有方法,那么我们每次创建一个新对象的时候,都会创建一个新的Function()实例,即创建了一个新的对象。“函数是对象,函数名是指针”。虽然每次创建的函数会导致不同的作用域链和标识符解析,但是他们的作用机理都是一样的,比如上面定义的Person()中,都是要显示当前对象的name属性。因此,我们可以把函数定义移到构造函数之外来解决这个问题。
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("Jona",20,"student");
var person2 = new Person("Jone",19,"nurse");
在此例中,由于构造函数中的sayName属性的值等于全局作用域中的sayName函数,这样一来,person1 和 person2 就共享了同一个sayName()函数。
缺点是什么呢?
在全局作用域中定义的函数实际上只能被某一个函数调用,这让全局作用域有点名不副实。(这句话可能一时想不明白,请参见博文,我觉得讲的很清楚:
https://blog.csdn.net/chengdabelief/article/details/50990938 )。
而且,如果对象需要定义很多方法,那么我们就要在全局作用域中定义很多全局函数,这破坏了自定义引用类型的封装性。
那么,对于这种问题,可以通过原型模式来解决。
网友评论