相信很多人都很困惑于javascript的原型系统,,在我刚开始接触javascript的原型系统的时候,我也非常困惑于它的结构,因为我以前都没有接触过这种基于原型的语言。javascript是一个基于原型的语言,虽然是以函数为第一等公民的语言,但也可以实现面向对象,那就是基于它的原型系统。
javascript原型之函数
现在我们来写一个函数
function A(){
}
这是一个内容为空的函数,但它真的内容为空吗?让我们来看下面一段代码
function A(){
}
console.log(A.toString());;//输出为function A() {}
我们知道,javascript里面的一切都是对象,函数也是,既然函数是对象那么就可以调用函数对象的方法,所以我试着调用了toString 方法,它输出了一个字符串,证明toString方法是存在的。那么toString 方法到底是存在在那里呢,不存在于函数体里,那么存在一个地方必然有toString的函数体且对象function A以某种方式获得了toString方法的调用权。。。
我百度了一下基于原型的语言的特征,基于原型的语言,必然有一个或着多个最初的对象,然后以后的对象都是由这些最初对象克隆过来的,也就是说,基于原型的语言中对象的生成是根据存在的对象来复制的。
好,那我们开始下一步的实验
function A(){
}
console.log(A.__proto__);//Function
console.log(A.prototype);//{}
我输出了Javascript对象所拥有的两个属性,这是javascript语言规定的两个属性_proto_属性指向对象构造函数的原型(不是很理解),prototype属性指向对象的原型。从结果看A函数构造函数的原型是Function,A函数自己的原型是{ }(同样不是很理解)
于是我又做了下面这个实验
Function.prototype.getName = function(){
return "FunctionTest";
}
function A(){
}
console.log(A.__proto__ === Function.prototype);//true
console.log(A.getName());//FunctionTest
function B(){
}
console.log(B.__proto__ === Function.prototype);//true
console.log(B.getName());//FunctionTest
console.log(A.__proto__ === A.constructor.prototype);//true
//即函数作为对象它的构造函数为Function
我从另外的地方得知javascript里面有内建的Function和Object对象,于是我想着Function对象是否和function A 有些关联呢,当我看到第一条console.log语句返回true的时候,我知道我是正确的,于是我扩展了Function.prototype 给其中添加了getName方法,然后我在用函数A调用了这个方法返回FunctionTest,我又新建了函数B,也调用了这个方法,返回FunctionTest。
至此我知道了函数_proto属性的指向,指向其构造函数的原型,当对象A调用getName函数的时候,由于A对象没有getName函数,javascript会寻找对象A的_proto属性所对应对象,有则调用,没有则继续向上找。
函数的prototype属性我一开始始终没有找到与之对应的对象
console.log(A.prototype === Function.__proto__);//false
console.log(A.prototype === Function.prototype);//false
console.log(A.prototype === Object.__proto__);//false
console.log(A.prototype === Object.prototype);//false
后来我换了一种思考方式终于找到了
console.log(A.prototype.__proto__ === Object.prototype);//true
console.log(A.prototype.prototype);//undefined
而之后我又实验了
console.log(Function.prototype.__proto__ === Object.prototype);
所以最终所有的一切对象的内置函数比如toString都是在Object.prototype里的
然后我又有一个猜测
所有一切函数对象的内置函数比如call,apply都是Function.prototype里的,可以很容易的就验证,普通对象是不能调用call,apply的。
javascript原型之对象
我实验了如下的代码
function A(){
this.getText = function(){
return "Text";
}
}
A.prototype.getName = function(){
return "my god";
}
var i = new A();
console.log(i.getText());//Text
console.log(i.getName());//my god
然后我改了一下程序
function A(){
this.getName = function(){
return "Text";
}
}
A.prototype.getName = function(){
return "my god";
}
var i = new A();
console.log(i.getName());//Text
console.log(i.__proto__ === A.prototype);//true
console.log(i.prototype);//undefined
由上面的实验可知,由函数A作为构造函数,所克隆出来的普通对象i的_proto_属性指向函数A的原型(也就是prototype属性),且普通对象i的prototype属性是没有定义的。
当i调用getName函数时,由于i是由函数A克隆出来(大家还没忘记原型语言的特征吧,就是新的对象是由另一个已存在的对象克隆的)的,里面只用getText函数没有getName函数,于是javascript就会寻找i的_proto_属性,而i的proto属性所指向的其实就是A.prototype,所以javascript就在A.prototype里面找getName函数,找到了就调用。所以第二段代码中优先调用返回Text的那个getName函数。
javascript里面还有一种对象就是对象字面量,对象字面量的是否拥有Function.prototype或者Object.prototype的函数呢
请看实验
var a = {
getName:function(){
return "a";
}
}
console.log(a.__proto__);//{}
console.log(a.prototype);//undefined
console.log(a.__proto__ === Object.prototype);//true
由于对象字面量不是由函数对象克隆的,所以没有Function.prototype里面的方法,因为a._proto_指向的是Object.prototype,这就说明javascript只能在a本身和Object.prototype里面找调用的函数。
总结
由上面以一些内容我们可以得出:
1.javascript函数调用顺序是查找对象的原型(即,对象的_proto_属性),一层一层的往上找,直到遇到该函数或者undefined才停止。
2.函数作为特殊的对象,它的原型是Function.prototype,能够调用Function.prototype里面的方法,而Function.prototype的原型又是Object.prototype,故而函数对象既能调用Function.prototype里面的方法,还能调用Object.prototype里面的方法,这就说函数对象即是“函数”又是对象。
3.普通对象的原型是其构造函数的原型(即,A.prototype),而A.prototype的原型是Object.prototype,所以,普通对象只能调用它本身和Object.prototype内的函数
4.对象字面量的原型是Object.prototype。