`

js原型prototype,proto与function之间的关系图

 
阅读更多

原文地址:http://peihexian.iteye.com/blog/2147079
美工人员一般是用js写个function校验表单数据的合法性,例如

function checkAndSubmit(){
   if (document.getElementById('username').value=='') {
      alert('请输入登录用户名!');
      return false;
  }
  document.form[0].submit();
} 
<input type="button" value="登录" onclick="return checkAndSubmit();"/>

 

点击登录按钮后触发function的执行,写到这对美工来讲也就够用了,但是对程序员不行,程序员得封装、继承与多态不是?

 

如何像写java或者c#那样写复杂的js代码呢?

 

Question:如何在js中定义类?

Answer:定义function就是定义类了,例如:

function Person(){
}
var p=new Person();

 

上面定义的Person就是类,下面的p就是Person类的实例了,实例化类实例的时候是通过new 加类名称Person及构造函数()完成的。

如果类有构造函数参数,则类的定义就是下面这样:

function Person(name,age){
   this.name=name;
   this.age=age;
}

var p1=new Person('tom',11); //创建Person的类实例,传递构造函数参数。

 

除了和美工写function一样的写法,能不能显得专业一点呢?可以的,像下面这样:

        var Person=function(name,age){
            this.name=name;
            this.age=age;
        }

        var p1=new Person('tom',11);

 

 

这里面有个问题,见下面的代码:

 

var p2={name:'jake',age:12};

 

上面的p2同样是类实例,同样有name和age属性,这两种定义方法一样吗?看看chrome调试里面的输出:



  

通过chrome的调试工具可以看到,p1就是Person类型的实例,p2是Object类型的实例。

p2的另外一种等价写法类似于

 

        var p3=new Object();
        p3.name='mike';
        p3.age=2;

 

在chrome里面调试的输出结果如下:

 



 看到了吧?p2和p3都是Object类的实例,不是我们自定义类的类实例!

所以,定义自定义JS类的正确方式就是function 类名称(构造函数参数)这样去定义,而不是 var o={key:value} 或者var o=new Object();o.key=value;等方式。

 

Question:如何定义类属性和类方法及静态方法? 

Answer:

           function Person(name,age){

            this.name=name;
            this.age=age;
            this.say=function(){
                console.log(this.name);
            }
        }
        var p1=new Person('tom',11);
        p1.say();

 以上就是定义类属性name和age以及方法的示例,很简单。

在java或者c#里面都可以定义静态方法,js里面可以定义静态方法吗?答案是可以!定义静态方法的语法是类名.静态方法名称=function(){}的方式,例如:

        function Person(){}//定义类
        //定义Person类的静态方法test
        Person.test=function(){
            console.log("static method");
        }
        //调用静态方法
        Person.test();

 

除了在定义function的时候定义属性和方法,还可以在类定义以后追加属性和方法,例如:

 

        function Person(name,age){
            this.name=name;
            this.age=age;
            this.say=function(){
                console.log(this.name);
            }
        }

        var p1=new Person('tom',11);
        //利用prototype追加非静态方法,要是直接直接写Person.hello=function就是定义静态方法了
        Person.prototype.hello=function(){
            console.log(this.age);
        }

        p1.hello();

 这里面出现了一个prototype,这个玩意是个什么东西呢?是用来追加非静态属性或者方法的吗?千万不要这么理解,这个prototype叫做原型,是独立存在的对象,每一个类都有一个对应的prototype,即每一个类都一个对应的原型,有点类似于java里面每一个类被JVM加载以后都有一个对应的Class对象一样,见下面的图:

 

 



 

 

在上面的图中,每一种类类型都有对应的prototype原型对象,是不是很像JVM里面的Class对象一样?Object、Function、Cat都是类类型,都有对应的prototype对象,cat,o都是类实例而不是类,所以没有对应的prototype。

 为了让看上面的乱麻一样的关系图简单一点,先说点简单的,每一个类都有一个prototype属性,该属性指向对应的prototype对象,如Object有一个protytpe属性,指向与object唯一对应的原型对象,Cat类也有一个prototype属性指向了与Cat类唯一对应的prototype原型对象,上面的图中没有写出来,其实就是类名称右侧向右的那根黑色箭头线(说的是类的prototype属性)。

好了,知识点一:类的prototype属性指向与该类唯一对应的prototype对象。

 接着说简单的,死记硬背型的,原型对象(就是图里面的prototype)都有一个constructor属性,该属性反过来指向该类的构造函数(其实就是那个function本身啦),例如

function Person(){
}

Person.prototype.constructor==Person //true

 

 知道了这一点,看上面图中的原型对象的constructor都指向类本身就明白了,问题是知道有这么回事可以干啥?原型对象知道构造函数在哪里以后就可以实例化类实例的时候调用该构造函数了,从这一点来讲,和JVM里面知道类的Class信息以后可以通过反射去实例化类实例是一样的道理。

 

搞清了类(function),类原型对象(prototype对象,同时也是类的prototype属性所指向的对象),原型对象的constructor这几个死记硬背型的关系以后,下面就是_proto_这东东了。

 

javascript中一切都是对象,每个对象都是基于原型对象创建的,每个对象中都有__proto__属性,这个属性指向的就是它基于的原型对象

例如上面的o={},这里面的o的类型是Object类型的,所以o的_proto_属性指向了Object.prototype也就是说o._proto_==Object.prototype,但是这种关系没办法写js去验证,因为除非FF以外其他浏览器都不让程序员访问_proto_这个属性。

继续看简单的,上面的mycat=new Cat(),即mycat是Cat类的实例,所以mycat._proto_==Cat.prototype,即mycat的_proto_属性指向它对应类型的prototype属性或者叫对应的原型对象了。

上面的Cat是个类,也是一个function,JS里面所有的function都是Function的实例,注意后面的是大写F,所以Cat类的_proto_指向了Function类对应的原型对象。

原型对象也是对象吧(都叫原型对象了当然是对象的啦)?是对象就得有类型吧?所以上面图中的Cat类对应的原型对象和Function类对应的原型对象的_proto_指向了什么?指向了Object的prototype,任何的原型对象都是Object的实例。

 

好了,再往下就应该是研究Object和Function之间的纠缠关系了,但是和我要写的类的继承关系不大,先写类的继承问题。

 

Question:如何定义继承关系?

Answer:

 

        function Base(){
            console.log("base constructor");
            this.hello1=function(){
                console.log("hello1");
            }
        }

        function Child(){
            console.log("child constructor");
            this.hello2=function(){
                console.log("hello2");
            }
        }
        Child.prototype=new Base();
        var c=new Child();

        c.hello2();
        c.hello1();

 

  代码好写,理解后面的关系头疼的我出去溜达了好几圈,大爷的。

  最关键就是红色那一行,Child.prototype=new Base();  上面分析过了,类的prototype属性指向与本类唯一对应的原型对象上面,也就是说,没有这行代码的话Child.prototype指向的是与Child唯一对应的原型对象,后面的new Base()创建出来了一个Base类的实例,该实例肯定可以调用Base类的所有方法(是Base的实例嘛),然后让Child.prototype=Base类的实例发生了什么?这里面引出了原型链这个东西。

把那一行代码先拆成两行吧,好理解一点:

//Child.prototype=new Base()拆分成以下两行
var o=new Base();
Child.prototype=o;

 

 上面的o是实例对吧?具体来说是Base类的实例对吧? 实例没有prototype属性,但是有_proto_属性是吧?忘了的话看前面加黑加粗的文字,o._proto_==Base.prototype对吧?

 Child.prototype=o以后,Child.prototype._proto_==Base.prototype对吧?好了,到这就先打住,一般来讲还没乱呢。

 

var c=new Child(); 要是没有前面的红色那行Child.prototype=new Base();的话,c就是Child类的实例吧?c._proto_==Child.prototype吧?很简单,但是前面加红那一行让Child.prototype._proto_=Base.prototype了是吧?

那现在c._proto_._proto_=Base.prototype了吧?

c.hello2()调用的就是Child类里面自己的,这个肯定没问题的。

c.hello1()调用的时候Child类里面没有这个方法吧?没有为啥还能调用Base类的呢?JS调用方法或者读取类属性的时候从_proto_链中去找,一直找到Object.prototype为止!

c._proto_找不到hello1()方法,那就到c._proto_._proto_里面去找,上面已经推导出了,c._proto_._proto_是Base.prototype,所以就找到了hello1()方法,也就可以调用成功了。

 

但是按照正常人类的理解,Child.protoype=new Base();以后不是Child类原来指向的Child.prototype就没了吗?为啥尼玛的new Child()还是能有Child类里面的方法呢?这是个问题

 

这里面有一篇特别好的文章,看这里吧:http://weizhifeng.net/javascript-the-core.html

 

 

 

  • 大小: 60 KB
  • 大小: 18.9 KB
  • 大小: 13.1 KB
分享到:
评论
1 楼 hoofoo 2016-08-30  
关于博主最后一句疑问:“但是按照正常人类的理解,Child.protoype=new Base();以后不是Child类原来指向的Child.prototype就没了吗?为啥尼玛的new Child()还是能有Child类里面的方法呢?”那是因为hello2是在函数Child体内通过this.hello2的方式定义的,这使得hello2成为了函数Child的一个属性,而不是prototype的属性,所以当你把Child的prototype替换掉后,还是能够通过Child的实例对象c访问到hello2。除非你使用Child.prototype.函数名 = function(){}的方式定义。代码如下:
function Base(){  
    console.log("base constructor");  
    this.hello1=function(){  
        console.log("hello1");  
    }  
  }  
  
  function Child(){  
    console.log("child constructor");  
    this.hello2=function(){  
        console.log("hello2");  
    }  
   }  
  Child.prototype.test3 = function(){console.log('test');}
  Child.prototype=new Base();
  var c=new Child();  
  
  c.hello2();//base constructor
  c.hello1();//child constructor
  c.test3();//这句报错,提示信息:Uncaught TypeError: c.test3 is not a function(…)

 

相关推荐

    深入浅析JavaScript中prototype和proto的关系

    prototype,每一个函数对象都有一个显示的prototype属性,它代表了对象的原型(Function.prototype函数对象是个例外,没有prototype属性)。 __proto__:每个对象都有一个名为__proto__的内部隐藏属性,指向于它所对应的...

    JS原型prototype和__proto__用法实例分析

    本文实例讲述了JS原型prototype和__proto__用法。分享给大家供大家参考,具体如下: 先来看一个实例 function Foo() { } var foo = new Foo(); console.log(foo.prototype);// undefined console.log(foo.__proto__...

    js prototype和__proto__的关系是什么

    function是对象,function的原型prototype也是对象,它们都会具有对象共有的特点。即:对象具有属性__proto__,每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部...

    【JavaScript源代码】五句话帮你轻松搞定js原型链.docx

     原型链是一种机制,指的是JavaScript每个对象包括原型对象都有一个内置的[[proto]]属性指向创建它的函数对象的原型对象,即prototype属性。 作用:原型链的存在,主要是为了实现对象的继承。 一、 记住以下5句话...

    JavaScript中__proto__与prototype的关系深入理解

    这里讨论下对象的内部原型(__proto__)和构造器的原型(prototype)的关系。 一、所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function) 代码如下: Number.__proto__ === Function....

    javascript 中__proto__和prototype详解

    __proto__是内部原型,prototype是构造器原型(构造器其实就是函数) 构造器的原型(prototype)是一个对象 那什么是构造器呢? 要想创建一个对象,首先要有一个对象构造器,就像php里面一样,要想创建一个对象,...

    图解prototype、proto和constructor的三角关系

    而关于原型,则是prototype、proto和constructor的三角关系。本文先用一张图开宗明义,然后详细解释原型的三角关系 图示 概念  上图中的复杂关系,实际上来源就两行代码 function Foo(){};var f1 = new Foo; ...

    Javascript中获取对象的原型对象的方法小结

    在Javascript中,如果我们有一个对象但是又不知道它的构造函数时,如何获取它的原型对象呢? 在Chrome中或是FireFox浏览器中,我们可以直接使用对象的__proto__属性获取它的原型对象。 代码如下: &lt;!– lang: js ...

    javascript中的原型对象(prototype)和隐式原型(__proto__)

    每一个函数(除箭头函数外)天生自带一个prototype属性,该属性称之为原型对象(原型),是一个引用类型数据。 作用:保存将来使用该构造函数构造出来的属性和方法,构造出来的属性和方法可以被共享。 注意:在...

    【JavaScript源代码】JavaScript中new操作符的原理示例详解.docx

     new的作用是通过构造函数来创建一个实例对象,该实例与原型和构造函数之间的关系如下图所示: 执行 new 操作时会依次经过以下步骤: 1、创建一个空对象  空对象是 Object 的实例,即 {} 。  let obj = {} 2...

    JS学习笔记之原型链和利用原型实现继承详解

    原型链是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的 实例对象中有__proto__,是对象,叫原型,不是标准的属性,浏览器使用,并且有的游览器不支持 构造函数中有prototype属性,也是对象,...

    javascript原型和原型链

    一、原型规则 1、所有的引用类型(数组、对象、函数)都具有对象特性,即可自由扩展属性(除了“null”) var array=[];array.a=1; var object={};object.a=1; function func(){}; func.a=1; 2、所有的引用类型...

    JS原型链怎么理解

    在谈原型链之前,我们首先要了解自定义函数与 Function 之间是什么关系,而构造函数、原型和实例之间又存在什么千丝万缕的关系呢?其实,所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,...

    Javascript原型链及instanceof原理详解

    javascript中的对象都有一个__proto__属性,这个是对象的隐式原型,指向该对象的父对象的原型(prototype)。显式的原型对象使用prototype,但是Object.prototype.proto=null; 判断某个对象a是否属于某个类A的实例,...

    js原型链与继承解析(初体验)

    首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询obj的原型,我们通过判断obj的原型是否与Object.prototype相等来证明是否存在obj的原型,答案返回true,所以存在。...

    浅谈JavaScript 覆盖原型以及更改原型

    覆盖原型 //囚犯示例 //1.定义原型对象 var proto = { ...Prisoner.prototype = proto; //4.实例化对象——采用工厂函数实例化对象 var makePrisoner = function(name, id) { //采用工厂函数实力化对象prisone

    JavaScript中的原型与原型链

    构造函数2.prototype与proto3.原型链 欢迎大家关注我的公众号,共同交流,共同成长~ 在刚接触JavaScript的对象的时候,我的内心是懵逼的,什么原型、原型链,好像跟我之前学的其他语言的对象不太一样,今天我们就来...

    浅谈javascript原型链与继承

     首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询obj的原型,我们通过判断obj的原型是否与Object.prototype相等来证明是否存在obj的原型,答案返回true,所以存在...

    理解JavaScript的prototype属性

    Function的原型和它的prototype属性无关 对象的原型可以通过非标准的属性 __proto__ 或ECMAScript5的方法 Object.getPrototypeOf() 访问。 1其实是错的,Object这个原型链尽头的对象它没有原型。可是为了更简单表述...

    js的继承方法小结(prototype、call、apply)(推荐)

    js的原型继承 — prototype 先说下什么是prorotype? js中,俗话说“一切皆对象”。用new 出来的都是函数对象;否则就是普通对象 函数对象都有prototype(原型对象);而普通对象则只有__proto__(原型指针) ...

Global site tag (gtag.js) - Google Analytics