JS原型
有一段时间,被JS的原型的“原形”搞的晕头转向的,但天下没有攻不下的城池~我认为要理解原型,就要分清楚“原型对象”,“构造函数”,“实例对象”这三者分别是什么,又分别干什么用。
1.原型对象
原型对象就是我们常常看到的prototype,其内部有其自身的属性与方法,其中包含有两个特别的属性:
(1)constructor:该属性指向对象的构造函数(可以理解为C+中的引用,即一个不会自己移动的指针,但可以人为改变指向);
(2)_proto_:该属性指向该对象的原型,可以通过原型访问器访问其原型。
标准对象原型访问器为:Object.getPrototype(obj)【FirFox,Chrome支持】;
非标准对象原型访问器为:_proto_【除了IE外都支持】
若这两个访问器不起作用,可通过对象的构造函数去找原型:obj.constructor.prototype;(这里通过obj.constructor访问对象的构造函数)
JS就是通过原型来实现继承的,我们通过原型对象设置的属性与方法可以被其所有实例共享,通过实例设置原型对象属性与方法可以被其他实例共享,所有我们一般通过原型对象来设置所有构造对象相同的方法与属性。
2.构造函数
原型的constructor属性指向构造函数,利用new运算符可以依照构造函数来创建并初始化一个新的对象,对象构造函数中的prototype属性指向该对象原型。我们可以为构造函数设置属性与方法来扩展对象的初始化属性与方法,只是初始化由this传递进来的对象。我把它理解为为原型对象(prototype)与实例对象建立联系的良民。
3.实例
在使用new创建新对象(对象实例化)时,会为新对象创建独立空间并初始化,其中的_proto_属性指向原型对象(prototype),
实现对原型对象属性与方法的继承。
4.这三者的沟通方式
对象实例的_proto_属性指向原型对象(prototype),而原型对象(prototype)的constructor属性指向对象的构造函数,构造函数通过new运算符创建并初始化实例实例,使得对象原型与对象实例可以交流。注意,一个原型可以有多个构造函数,可以为实例分类,构造函数中的属性与方法是同类实例共享的。
5.原型链
刚刚提到了,原型对象(prototype)中的_proto_属性指向该原型对象的原型,假设将上述 “原型对象”,“构造函数”,“实例对象”这三者看做一个层级,那么原型对象(prototype)中的_proto_属性便承担了沟通上级层级的使者,这样一层一层相连接,就形成了原型链。原型链的顶端为Object,它的_proto_指向null。每一个对象都有自身的原型链,在确定某一对象时,也会类似于确定变量值一样按就近原则一层一层查找,找到则返回属性值或方法,若是直到顶端还未找到则报错。
6.方法与属性的设置
我们可以在“原型对象”,“构造函数”,“实例对象”设置方法和属性。
(1).在原型对象中设置时,设置的属性与方法可以被所有实例共享,且在用new运算符创建实例时不会开辟新的空间存放这些属性和方法,仅仅只是存放了原型的引用(_proto_);
(2).在构造函数中设置属性与对象是通过灵活的this来设置每次传进来的不同对象的相同方法与属性,因此使用new运算符来创建实例时,会为每一个实例开辟相应的空间来存放相同的属性与方法,这种做法十分耗内存,一般我们提倡将实例的共享属性加到实例的原型中去,那构造函数难道就没其他作用了吗,其实它还有一个作用,就是可以用来扩展JS默认对象的方法与属性,这样,与团队的其他成员合作时,不会因为改动了默认对象原有属性而使其他成员在未知情况下使用默认对象出现意想不到的结果,同时也减少了代码量,因为每次在不同实例上添加相同方法时都必须使用"实例名._proto_.***"来设置,这样会加大代码量,影响性能。默认对象有Array,Math,Date,Function,RegExp......
(3).为实例添加属性与方法时,如果与原型对象中的属性方法重名时,实例会屏蔽原型中的同名属性与方法;如果不重名,则会为实例添加相应的属性与方法;一般用来设置独有属性与方法。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。