JS创建对象的六种方式

JavaScript是一种面向对象的程序设计语言,既然是面向对象,那么就一定存在去创建对象,本文就主要深入学习下创建对象的六种方式。

一、Object构造函数
这种方式我也称之为:基于已有对象扩充其属性和方法创建对象。代码如下:

图1:Object构造函数创建对象

这种创建对象的方式比较简单,是基于已经存在的Object对象来去扩展属性和方法,但是缺点也很明显,就是如果需要大量的同类对象就意味着大量的重复性的代码,效率不高。

二、工厂方式
工厂设计模式是软件领域应用广泛的设计模式,同时这种模式也可以应用到JavaScript创建对象中。

图2:工厂模式创建对象

工厂模式解决了如果要创建多个对象代码重复性的问题,但是也有缺点,如果多次调用createPerson()函数,那么每个实例对象内部都会有一个info函数对象,说白了,这个info函数在每个实例对象中都会有一份,而不是所有的实例对象所共享info函数对象,这个就意味着每个实例对象都会占用一份函数的内存,造成内存的浪费。(tips:面向对象中,属性是实例每个对象所有拥有的,而函数是每个实例对象所共享的)。

持续改进代码,如下:

图3:工厂方式创建对象改进版

解决问题的思路是:把info函数设计成了一个全局的函数,这样的话,在createPerson()函数内部的info属性就指向了这个info函数的一个指针,因此就可以实现obj1实例对象和obj2实例对象共享同一个info方法。

不过这样做,也同样会导致另外一个问题,就是:如果对象里面需要定义很多方法,那么就意味着要定义很多的全局函数,为了解决这个问题,就使用到了原型方式。

三、构造函数方式
在前面第一种方式中,已经讲过通过Object构造函数创建对象的方式,其实除了此种方式外,还可以使用自定义的构造函数方式去创建对象,代码如下:

图4:构造函数方式创建对象

创建Person实例对象的时候在外部使用到了“new”操作符。这种调用方式实际上经历了四个步骤,分别是:

1、创建一个新的实例对象

2、将构造函数的作用域赋给新对象(因此this就指向了这个新的对象)

3、指向构造函数中的代码(为这个新的实例对象添加属性)

4、返回新对象的地址。

特别要注意的是:

1、通过构造函数的方式去创建对象,在构造函数的内部,我们是不用写return语句的,但是实际上是存在的一个隐含的return语句,作用就是将之前的生成的对象返回。

2、这种构造函数的方式依然存在对象方法重复占用的问题,说白了就是每个实例对象都会有info方法,类似于上面提到的工厂方法创建对象时一样的问题,解决办法是一样的,就是把info函数设计成一个全局的函数,代码和上面的改进代码一样,在此就不再列举了。

四、prototype原型方式
问题引入:使用工厂方式和构造函数方式创建对象都存在一个问题,就是如果存在很多的实例对象,那么每个实例对象内部都会有一份函数对象,也就是说每个函数均会占用一份内存空间,造成内存泄露,比如说上面的代码,obj1和obj2…obj100实例对象中都会有info函数对象,这样显然是没有必要的,因为这个info函数所完成的功能是一样的。那有的小朋友会说,不是都解决了嘛,我们可以把函数都设计成全局的函数,那么这样的话,不就实现了多个实例对象共享同一个方法了吗?这样做其实也是可以的,但是带来的问题主要有两点:

1、破坏了函数对象的封装性,属性和方法分离了。

2、如果一个对象里面有大量的方法,难道这些方法都要设计成全局函数不成。

问题解决:使用原型方式。代码如下:

图5:原型方式创建对象

上面的程序运行结果解释下,实例对象在访问方法或属性时,优先是搜索自己的属性和方法,如果自己的属性和方法没有找到,则再去搜索原型的属性和方法,如果原型中也没有找到,就去原型对象的原型中搜索,一直沿着原型这条链搜索,直到找到为止,如果没有找到,则就会出现undefined,上面的p2.name = “lisi”,实际上是给p2对象添加了一个自己的属性为name,所以才会输出“lisi”。(tips:如果这个代码没有理解,请参考我的一篇文章《JavaScript高级之prototey原型》)。

这种原型方式创建对象还是有些不足,在于:单纯的使用原型方式去构造一个类,虽然解决了多个实例对象共享方法的问题,但是无法像类似于构造函数的方式为属性赋初值,只能在对象生成之后再去改变属性的值。同时,使用原型方式还会存在一个问题,看代码:

图6:原型方式创建对象问题

通过上面的代码实例,可以总结出一点:使用原型方式创建对象,那么生成的所有对象会共享原型中的属性,这样一个对象对某个属性的改变也会反应到其他对象当中。

五、原型+构造函数方式
问题引入:单纯的使用原型方式虽然也可以创建对象,但是问题是属性都是对象所共享的,没有私有属性了,而且一个对象对该属性的改变有也会导致其他对象对属性的改变。

解决:使用原型+构造函数方式,这种方式也是创建对象最常用的方式。我们可以把对象的私有属性用构造函数实现,方法使用原型来实现,这样就实现了对象之间的属性互不干扰,同时各个对象共享同一个方法。

简单来说,就是:属性定义在构造方法中,方法定义在prototype-原型中。代码如下:

图7:原型+构造函数方式创建对象

使用原型+构造函数方式创建对象,结合了构造函数方式和原型方式的优点,又摒弃了这两种方式的缺点,实现了对象之间属性的互不干扰,各个对象共享同一个方法。

六、动态原型方式
问题引入:在使用上面第五种方式,也就是使用原型+构造函数方式创建对象时,对象的属性和方法并不在一起,也就是说并不在Person构造函数中,说白了,就是属性和方法是分离的,这个对于其他面向对象语言的开发者来说是有点不好的(毕竟程序员或多或少都有强迫症,哈哈),而动态原型方式就可以解决这个问题。

解决思路:在构造函数中通过标志变量让所有对象共享一个方法,而每个对象拥有自己的属性。代码如下:

图8:动态原型方式创建对象

在构造函数中写if语句是为了判断是否是第一次执行Person函数,如果是第一次执行Person函数,那么if语句中的typeof Person.flag的值肯定为undefined,那么就给Person构造函数的原型对象添加info函数,同时把flag标记变量置为true,如果不是第一次执行,那么typeof Person.flag的值肯定就不再是undefined,而是true,那么就表明Person原型对象中已经有了info函数,所以就不会再去向Person原型对象中添加info函数了。使用动态原型方式,就可以实现将属性和方法封装在同一个代码块里,更好的实现了面向对象的封装性。

这个就是JavaScript中创建对象的六种方式,当然使用最多是还是原型+构造函数模式和动态原型模式

发布了12 篇原创文章 · 获赞 0 · 访问量 1115

猜你喜欢

转载自blog.csdn.net/weixin_45931321/article/details/104278567