javascript原型详解

作者: 爆炸的榴莲 | 来源:发表于2016-10-31 17:43 被阅读42次

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

    相关文章

      网友评论

      • 大狗奇:感谢!用实验得出结论比直接给结论生动很多!

      本文标题:javascript原型详解

      本文链接:https://www.haomeiwen.com/subject/mxuputtx.html