javascript原型详解

相信很多人都很困惑于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。

    原文作者:爆炸的榴莲
    原文地址: https://www.jianshu.com/p/3967720a3b7c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞