预备知识:
- 每个函数对象在定义的时候都会自动生成一个prototype属性(显式原型),保存的是一个空Object对象的地址值。即函数对象的prototype属性指向一个空Object对象。
- 每个实例对象在创建的时候都会自动生成一个__proto__属性(隐式原型),保存的值和prototype值相同,即实例对象的__proto__和函数对象的prototype都指向同一个空Object对象
var Fun = function(){ this.test1 = function(){ console.log("test1"); }; };//内含语句:this.prototype = {}; var fun = new Fun();//内含语句:this.__proto__ = Fun.prototype;
原型
由上面的描述,我们来分析上面的代码:
1.函数定义时,在栈内存中开辟一块内存,变量名为Fun,Fun保存着函数对象在堆内存的地址。
2.堆内存的函数对象自动生成一个prototype属性,指向一个Object空对象。
3.当实例化Fun函数时,在栈内存中开辟一块内存,变量名fn,存放实例对象在堆内存中的地址。
4.实例对象fun自动生成__proto__属性,同样指向空Object对象。
5.通过显示原型给空Object对象添加方法:Fun.prototype.test = function(){alert("test")};
即:实例对象的隐式原型指向构造函数对象的显式原型
如下图所示:
原型链
我们知道,JS内置Object构造函数,那么Object构造函数是否也有prototype属性呢?答案是肯定的。我们可以输出它的原型对象看一看:
由图可知Object函数的原型对象中有许多内置函数。
当我们定义一个函数时,同时也实例化了一个Object空对象,那么这个Object空对象也是一个实例对象,自然也有__proto__属性,它的__proto__和它的构造函数(也就是Object函数)的prototype指向同一个对象,也就是Object原型对象。
所以说,通过空Object对象的__proto__属性,我们就将自定义的函数和内置的Object函数联系起来了,通过隐式原型(__proto__)我们构造出了一条原型链。
function Fun(){
this.test1 = function(){
console.log('test1');
}
}
Fun.prototype.test2 = function(){
console.log("test2");
}
var fun = new Fun();
fun.test1();
fun.test2();
console.log(fun.toString());
fun.test3()
console.log(Object.prototype);
首先,定义Fun函数,函数内定义了一个test1属性,属性值是一个函数。再通过显式原型为Fun的Object空对象中添加test2函数。toString()函数是Object函数的原型函数的内置函数。
fun.test1():
实例化Fun后,调用实例对象的test1属性,加括号test1()则调用test1函数,输出test1
fun.test2():
fun调用test2函数,但是fun实例对象中并没有test2函数,于是沿着fun的__proto__属性向上寻找,在Object空对象中找到test2函数,调用执行
fun.toString()
fun调用toString函数,fun实例中没有这个函数,沿着fun的__proto__来到Object空对象,还是没有,沿着Object空对象的__proto__属性来到Object原型对象,找到toString函数,调用执行
fun.test3():
首先我们来思考一个问题,Object原型对象,也是一个实例对象,那么它也应该具有__proto__属性,那么它的__proto__属性指向哪里呢?不妨输出试试看:
可以看到指向null,那么至此原型链走到了末尾,如果到这里还找不到要的属性,那么就是真的找不到了
fun调用test3函数,fun实例中没有这个函数,沿着fun的__proto__来到Object空对象,还是没有,沿着Object空对象的__proto__属性来到Object原型对象,还是找不到,沿着Object原型对象的__proto__来到null,说明没有test3这个函数,报错:Uncaught TypeError: fun.test3 is not a function
上面的过程可以用下图辅助理解:
附:
1.自定义函数,也是Function()的一个实例 function fun = function(){};等价于 var fun = new Function(); fun = function(){};
2.所以自定义的函数也有__proto__属性,指向Function的显式原型,即:Fun.__proto__=Function.prototype.所有自定义函数的__proto__都相等
3.再往前追溯,内置函数Object,一开始也是function Object()这样定义的,所以说Object函数也是Function的实例,Object.__proto__ = Function.prototype;
综上所述,原型链从实例函数对象触发,到Object原型对象停止,形成了自定义函数和内置函数的联系,又因为是通过__proto__这一隐式原型实现的,所以原型链又称为隐式原型链。
以上就是我个人对原型和原型链的理解,如果有不对的地方还请大家指出~