《javascript高级程序设计》第六章 读书笔记 之 javascript对象的几种创建方式

版权声明:原创版权归本人所有,欢迎转载请注明原文博客链接地址,商用请联系本人 https://blog.csdn.net/weixin_44541490/article/details/87874905

本文首发于https://segmentfault.com/a/1190000017776314
一、工厂模式

工厂模式:使用字面量和object构造函数会有很多重复代码,在此基础上改进
在这里插入图片描述
解决了多个相似对象的问题,但没有解决对象识别的问题(即怎样知道一个对象的类型)

二、构造函数模式

在这里插入图片描述
与工厂模式创建对象的不同之处:没有显示创建对象,直接将属性和方法赋给this对象,没有return语句。默认return的是this对象。
构造函数本身也是函数,只是可以用来创建对象,所以借鉴自其他面向对象语言,构造函数始终应该以一个大写字母开头,非构造函数用一个小写字母开头,用于区别构造函数与其他普通函数。
new操作符创建对象实例,经历4个步骤:
1、创建一个新对象
2、讲构造函数的作用域赋给新对象(所以此时this指向这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性)
4、返回新对象
上面的实例a又一个constructor(构造函数)属性指向Person。
在这里插入图片描述
相比较工厂模式的有点:有constructor属相,可以标志为一种特性的类型。
但实际用来检测对象类型,instanceod更方便些,所以instanceof比较的是constructor属性。
三种使用构造函数创建对象的方法:
在这里插入图片描述

call和apply的作用都是在某个特殊对象的作用域中调用Person()函数。那么上例中,otherPerson的作用域中调用Person函数,this指向otherPerson对象,执行构造函数中的内容,this.name指otherPerson.name=‘shirley’
构造函数模式的缺点:每创建一个实例都要执行一遍构造函数的语句,每个实例有不同的属性值很正常,也有许多内容一模一样的方法,这些方法重复创建(实际是new function实例,所以person1.sayname!=otherPerson.sayname。因为sayName方法是不同实例,引用地址不同)则显得冗余。
很明显,如果实例引用同一个sayName实例(同一个方法)就能够解决上述问题。
在这里插入图片描述
Person的sayName引用的都是同一个sayName函数,即全局变量中的sayName函数。

三、原型模式

利用每个函数都有的一个prototype(原型)属性。这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。(解决了构造函数模式的缺点)
prototype就是通过调用构造函数而创建的那个对象实例的原型对象。(就是所创建的对象的原型,例如:var person1=new Person(’linda‘,15)person1的原型为Person。otherPerson=new Object() otherPerson的原型为Object)
例子:
在这里插入图片描述
判断一个对象的prototype是否指向某对象
在这里插入图片描述
访问一个对象的prototype属性
在这里插入图片描述
Firef、Safari、Chrome中支持一个属性__proto__可以访问到prototype
注意: JavaScript 中任意对象都有一个内置属性 [[Prototype]] ,在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。以下统一使用__proto__来访问 [[Prototype]],在实际开发中是不能这样访问的。(摘自:https://segmentfault.com/a/1190000011801127)
ES5中增加了一个新方法,Object.getPrototyOf()
在这里插入图片描述
“原型最初只包含constructor属性”即,对象的constructor保存在原型属性中。
对象实例不能改变原型中的值,所以当我们试图改变原型中的值时,会在实例中新添加一个相同名称的属性。当我们访问这个属性时,就会优先返回实例中的属性,这就是覆盖了原型的属性。
那么我们怎么确定一个属性是来自对象实例还是来自对象的原型,使用Object对象的hasOwnProperty()方法可以检测属性是否存在于对象实例中,若存在,则访问的属性必定是来自对象实例,因为当访问一个属性时,先搜索对象属性若不存在才会去搜索对象的原型属性的。
在这里插入图片描述
delete可以删除实例的属性,但不能删除对象原型中的属性。
对象、构造函数(constructor)和原型(Prototype)的联系:
如下图
在这里插入图片描述

person1和person2实例中的prototype属性中保存的是Person的原型,原型里有constructor属性又指向Person。所以对象和constructor之间是通过原型联系起来的。

确定属性是原型中的属性:
in操作符只要通过对象能够访问到属性就返回true,不管是实例的属性还是原型中的属性,所以通过in操作符返回true儿hasOwnProperty()返回false就可以确定属性是原型中的属性。

用字面量法定义Person的prototype属性:
在这里插入图片描述
会造成constructor属性不再指向Person
我们知道,没创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。使用字面量定义prototype会重写这个对象,因此constructor会变成新的对象的constructor属性(指向object构造函数)。
打印person2显示:
![图片描述][13]
红框内没有constructor属性。我们知道person2.constructor会寻找person2对象中的constructor对象中的constructor对象,person2实例对象中没有,寻找原型中(红框)也没有,继续寻找prototype的原型对象即object对象,所以,person2.constructor===Object true;
在这里插入图片描述
原型模式创建对象是在函数构造模式的基础上解决共有方法和属性的,那么函数构造模式是为了解决字面量法的无法判断是哪个对象的问题而出现,怎么判断?通过对象的constructor属性判断,现在原型的constructor都指向对象,就把这个优势抹去了,所以出现下面这样的方法来保存这个特性。
在这里插入图片描述
这样做与原来的差别是:constructor属性的[[Enumberable]]特性被设置为true,原来是false(不可枚举)。所以如果使用兼容EXMAScript5的JavaScript引擎,可以使用Object.defineProperty()
在这里插入图片描述
原型具有动态性。
构造函数的原型改变会立即反映到所有实例中,但如果是重新写一个原型对象则不会了

在这里插入图片描述
![图片描述][18]
原因:person1的构造函数会自动指向一个原型对象,而保存的是这个原型对象的指针,新建一个prototype对象后,在堆内存中开辟了一个新的对象空间,在person2中保存的是这个新对象的引用指针,所以两者不同。
原型模式很少会单独使用,因为所有属性都是共享的,但实力一般都是要有属于自己的全部属性的。原型模式无法做到,所以这是原型模式的缺点。

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

创建自定义类型的最常见方式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
在这里插入图片描述
结果每个人实例都有自己的一份实例属性的副本,同时又共享着对方法的引用,最大限度地节省了内存。这种方式还支持向构造函数传递参数。(既有构造函数模式的优点又有原型模式的优点)

五、动态原型模式

在构造函数中判断是否存在sayName函数,如果不存在添加到原型中,下图创建了两次对象,调用两次构造函数,但if的判断只执行一次,在第一次执行。

在这里插入图片描述

扫描二维码关注公众号,回复: 5266324 查看本文章

六、寄生构造函数模式

与工厂模式在写法上红框圈出不同之外一模一样。叫法上把Person函数叫做构造函数,其他无区别
在这里插入图片描述
适用情境:
可以在特殊的情况下用来为对象创建构造函数。假如我们想创建一个具有额外方法的特殊数组。
在这里插入图片描述

七、稳妥构造函数模式

稳妥对象:没有公共属性,而且其方法也不引用this的对象。
稳妥对象最适合在一些安全的环境中(这些环境中会禁止是使用this和new),或者在防止数据被其他应用程序(如Mashup程序)改动时使用。
稳妥构造函数与寄生构造函数类似的模式。不同点:1、新创建对象的实例方法不引用this,2、不适用new操作符调用构造函数。
在这里插入图片描述
所以,使用稳妥构造函数模式创建的对象与构造函数之间没有关系,无法用instanceod判断所属类型。
之所以稳妥,是因为除了sayName方法外部无法对传入构造函数中的原始数据进行访问。即使可以给这个对象添加方法或数据成员。
使用环境:某些安全执行环境下使用(ADsafe和Caja)

猜你喜欢

转载自blog.csdn.net/weixin_44541490/article/details/87874905