上面的内存图跟堆栈结构可以参照文章Javascript_01_理解内存分配。
堆区图说明:
Function.prototype函数对象图内部表示prototype属性的红色虚框,只是为了说明这个属性不存在。
通过上图Function、Object、Prototype关系图中,可以得出一下几点:
- 所有对象所有对象,包括函数对象的原型链最终都指向了Object.prototype,而Object.prototype.__proto__===null,原型链至此结束。
- Animal.prototype是一个普通对象。
- Object是一个函数对象,也是Function构造的,Object.prototype是一个普通对象。
- Object.prototype.__type__指向null。
- Function.prototype是一个函数对象,前面说函数对象都有一个显示的prototype属性,但是Function.prototype却没有prototype属性,即Function.prototype.prototype===undefined,所有Function.prototype函数对象是一个特例,没有prototype属性。
- Object虽是Function构造的一个函数对象,但是Object.prototype没有指向Function.prototype,即Object.prototype!==Function.prototype。
二 Prototype跟Constructor关系介绍
在 JavaScript 中,每个函数对象都有名为“prototype”的属性(上面提到过Function.prototype函数对象是个例外,没有prototype属性),用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数本身。这是一种循环引用(i.e. Animal.prototype.constructor===Animal)。 通过以下例子跟内存效果图来分析Prototype、constructor间的关系。 console.log('**************constructor****************');
console.log('anim.constructor===Animal:'+(anim.constructor===Animal)) ; //true
console.log('Animal===Animal.prototype.constructor:'+(Animal===Animal.prototype.constructor)) ; //true
console.log('Animal.constructor===Function.prototype.constructor:'+(Animal.constructor===Function.prototype.constructor)); //true
console.log('Function.prototype.constructor===Function:'+(Function.prototype.constructor===Function)); //true
console.log('Function.constructor===Function.prototype.constructor:'+(Function.constructor===Function.prototype.constructor)); //true
console.log('Object.prototype.constructor===Object:'+(Object.prototype.constructor===Object)); //true
console.log('Object.constructor====Function:'+(Object.constructor===Function)); //true
prototype、constructor内存关系图(在Function、Object、Prototype关系图上加入constructor元素):
上图中,红色箭头表示函数对象的原型的constructor所指向的对象。
- 注意Object.constructor===Function;本身Object就是Function函数构造出来的
- 如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象。
我们接着看代码:
要解释这个结果就要仔细研究一下new这个操作符了.var one=new Person('js');这个语句执行的过程可以分成下面的语句:
按照《悟透javascript》书中说的,new形式创建对象的过程实际上可以分为三步:
第一步是建立一个新对象(叫A吧);
第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;
第三步就是将该对象(A)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。
其中第二步中出现了一个新名词就是内置的原型对象,注意这个新名词跟prototype对象不是一回事,为了区别我叫它inobj,inobj就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。
又头晕了,上图吧!inobj就是__proto__ 即,one.__proto__ == Person.prototype
这样one对象通过内置的原型对象inobj就可以直接访问Person的prototype对象中的任何属性与方法了。这也就解释了上面的代码中为什么one可以访问form函数了。因为prototype对象中有一个constructor属性,那么one也可以直接访问constructor属性。
代码:
接着看继承是如何实现的。
继承的实现很简单,只需要把子类的prototype设置为父类的一个对象即可。注意这里说的可是对象哦!
那么通过prototype属性实现继承的原理是什么呢?还是先看图形说明,然后编写代码进行验证。
用图像直观表示如下:
图 1.3
到此说明了a,b两点是正确的。
同事上面的也可以论证一下观点(该观点摘自JavaScript权威指南):
1) 同一个类的所有实例都从同一个原型对象上继承属性
2) 当且仅当两个对象继承自同一个原型对象时,他们才是属于同一个类的实例
特别说明:只有构造函数才有prototype属性,而构造函数的实例是没有该属性的,也就是说console.log(a1.prototype)输出的是undefined。在javascript中,每个函数都自动有一个prototype属性,而不是每一个对象拥有prototype属性
原型属性与实例对象的创建与否没有关系,它在对象创建之前就已经存在
直观表示如下图:
图 1.5
注意:新定义Prototype对象的话,该Prototype对象原有的constructor属性会丢失,该Prototype此时所指的对象(即{getName:function(){return this.name}}对象)的constructor就是Object,事实上{getName:function()}就是一个Object对象。相信如果A.prototype={getName:function(){returnthis.name}}换成这样写的话,理解起来会方便写:
Var obj = {getName:function(){}}
A.prototype = obj;
//或者这样写:
Var obj = new Object();
Obj.getName = function(){
};
A.prototype = obj;
此时用图形表示a1,a2,prototype对象的关系如下:
通过这个图可以很直观的看出:
a1.constructor === A//=>false
a1.constructor === Object//=>true
A.prototype.constructor === Object
.修改这个问题的办法就行给重定义的Prototype对象显式的添加一个constructor属性,修改其constructor的指向:
/**
* Created by duqiong on 17/3/13.
*/
function A(name){
this.name = name;
this.show=function () {//只是实例方法,不实例化无法调用
console.log('inner-'+this.name+' inner show');
// A.show();
}
}
A.shows=function () {
console.log('static-'+this.name+' static show');
}
var a2 = new A("aa2");
A.prototype.show1=function () {//只是在A的原型对象(实例)增加实例方法,不需要实例化不能调用
console.log('prototype show');
}
A.alarms=function () {
console.log('sdfsdfs');
this.alarm=function () {//只是修改了类方法,类方法不需要实例化
console.log('inner dynamic alarm');
}
//this.alarm();
}
var a1 = new A("aa1");
// console.log(a1);
console.log(a2);
console.log(a1.constructor);
console.log(a2.constructor);
console.log(a2.constructor.constructor);
console.log(A.prototype);
console.log(a1.__proto__);
console.log(A.constructor);
console.log(a1.prototype);
//
A.shows();
a1.__proto__.constructor.shows();//通过实例调用类静态方法
a1.constructor.shows();//通过实例调用类静态方法
a1.show();
A.alarms();
A.alarms();console.log(a1.__proto__===A.prototype);console.log(a1.constructor===A);console.log(A.prototype);a1.show1();
////print//////A { name: 'aa2', show: [Function] }
{ [Function: A] shows: [Function], alarms: [Function] }
{ [Function: A] shows: [Function], alarms: [Function] }
[Function: Function]
A { show1: [Function] }
A { show1: [Function] }
[Function: Function]
undefined
static-A static show
static-A static show
static-A static show
inner-aa1 inner show
sdfsdfs
inner dynamic alarm
true
true
A { show1: [Function] }
prototype show