之前对于原型、原型链这部分比较模糊,最近刚好有时间,整理一下以加深理解记忆
一、相关知识介绍
1、属性
_proto_属性:对象特有的,由于js中一切皆对象,故函数中也包含_proto_属性。
2、对象
对象是由构造函数创建。js中一切皆对象。因此构造函数也是对象。
3、结论
(1)所有对象原型链的顶端是Object.prototype;
(2)所有对象的_proto_属性均指向Function.prototype。构造函数创建的对象都是Function的实例。
(3)除了Object,所有对象(即构造函数)的prototype均继承自Object.prototype。
二、原型机制设计
在JavaScript中,有两个祖先一样的对象:Function.prototype和 Object.prototype。
(1) Object.prototype是所有对象的根;
(2) Function.prototype是所有构造器的根(除Object外);
事实上所有对象都维护着一个{prototype}属性,这是一个内部的私有属性,无法通过对象来访问。不过在firefox等浏览器中提供了一个共有属性__proto__来访问这个私有属性。在Js中公有属性名和私有属性名可以相同。
1、Object.prototype
(1)通用属性
- Constructor:对创建对象的函数(构造器)的引用(指针),这里为Object,主要是为了Object.prototype也属于Object类型;设计的需要,实际上不是Object构造的。
- Prototype:私有的prototype属性,也就是__proto__的值,这里Object.prototype为null。
(2)通用方法
hasOwnProperty(property):判断对象是否有某个特定的属性。必须用字符串指定该属性 (例如o.hasOwnProperty(”name”))。
isPrototypeOf(object):判断该对象是否为另一个对象的原型。
propertyIsEnumerable(property):判断给定的属性是否可以用for…in语句进行枚举。
toString():返回对象的原始字符串表示。对于Object类,ECMA-262没有定义这个值,所以不同的ECMAScriipt实现具有不同的值。
valueOf():返回最适合该对象的原值。对于许多类,该方法返回的值都与toString()的返回值相同。
2、Function.prototype
(1)通用属性
0…n属性:这是arguments取参数时的索引,0——n
arguments属性:存储存进来所有参数,相当于可变参数参数列表。
callee属性:引用当前函数对象,用于函数递归,特别是在匿名函数中特别有用。
caller属性:返回调用某个函数的函数对象。
constructor属性:Function.prototype的构造器是Function.
length属性:当创建一个函数的实例时,函数的 length 属性由脚本引擎初始化为该函数定义中的参数数目。
prototype属性:内部{Prototype}——也即__proto__,为Object.prototype。
(2)通用方法
apply():用法同call()函数,不同点在于apply()仅接受数组传参,不接受单个参数传参。
call():用来调用所有者对象作为参数的方法,用法a.call(b,c,d);意思是将a方法用于b对象,好像b对象也拥有a方法一样,其中c,d均为传递给a方法的参数。(仅接受单独传参,不能接受数组传参)
toString():将对象转换成字符串。
valueOf():可返回 String 对象的原始值。常由 JavaScript 在后台自动进行调用,而不是显式地处于代码中。
三、原型链
如有三个类,也即A,B,C(其实就是构造器——函数对象),如B继承A,C继承B,那么就等价于:
function A(){}; //A.prototype = Object.prototype A.__proto__ = Function.prototype
function B(){}; //类推
function C(){}; //类推
B.protype = new A();
C.prototype = new B();
var c = new C();
c中维护着一个B的实例,而这个B的实例中又维护着一个A的实例的引用。等价于:
function A(){};
function B(){};
function C(){};
var a = new A();
B.prototype = a;
var b = new B();
C.prototype = b;
c = new C();
也即c中维护着一个B的实例b的引用,而b中维护着一个A的实例a的引用。即这里的继承其实也就是共享对象,而且可以形成共享链。
(a)没有继承之前:
function A(){};
function B(){};
function C(){};
var a = new A();
var b = new B();
var c = new C();
结构如图:
分析:
constructor是对创建对象的函数(构造器)的引用(指针)。
prototype是函数特有的属性,其中存储了要共享的属性和方法,不过prototype是一个私有属性,无法通过对象来访问。
_proto_属性指向创建对象的构造函数的原型prototype,故而可以借用_proto_访问私有属性prototype。
(b)继承之后:
function A(){};
function B(){};
function C(){};
var a = new A();
B.prototype = a;
var b = new B();
C.prototype = b;
c = new C();
结构如图:
由此更加能够表示,其实所谓的继承就是对象共享,是一种链式的间接访问,比如B知道a的地址,那么B的所有子孙也就知道a的地址,自然也就能访问。但是每个B的子孙知道的是同一个a,而不是a的副本。同理C知道b(B的一个实例)的地址,那么C的所有实例也知道b的地址,而b又知道a的地址。所以c(C的一个实例)能够访问b,通过b又能访问a。如果我们把对象看成一个集合的话,那么b={…,&a},c={…,b={…,a}};
也就是a的引用&a属于b,且包含于b;b的引用&b属于c,也包含于c。
原型链的表示为:b.proto = a;c.proto = b;c.proto.proto = a;
参考文献:https://www.2cto.com/kf/201401/269806.html