zoukankan      html  css  js  c++  java
  • javascript-理解原型、原型链

      首先,先认识下什么是原型?

          原型是一个对象,其他对象可以通过它实现属性继承,而且任何一个对象都可以成为原型。这是为什么呢?请继续看。

          首先,要弄明白什么是对象,在JavaScript中可以说一切都是对象,除了(undefined, number, string, boolean)这四种值类型,我们可以通过 typeof() 这个函数和instanceof进行检测,如何判断一个变量是不是对象,值类型的类型判断用typeof,引用类型的类型判断用instanceof。如下:

     1 function show(x) {
     2 
     3             console.log(typeof(x));    // undefined
     4             console.log(typeof(10));   // number
     5             console.log(typeof('abc')); // string
     6             console.log(typeof(true));  // boolean
     7 
     8             console.log(typeof(function () { }));  //function
     9             console.log((function () { }) instanceof Object);  // true
    10 
    11             console.log(typeof([1, 'a', true]));  //object
    12             console.log(typeof ({ a: 10, b: 20 }));  //object
    13             console.log(typeof (null));  //object
    14             console.log(typeof (new Number(10)));  //object
    15         }
    16         show();

      其中上面的四种(undefined, number, string, boolean)属于简单的值类型,不是对象。剩下的几种情况——函数、数组、对象、null、new Number(10)都是对象。对象是属性的集合。

          弄明白什么是对象后,我们再来理解下函数与对象的关系。对象都是通过函数创建的,为什么这么说呢,请看代码:  

     1         var obj = { a: 10, b: 20 };
     2         var arr = [5, 'x', true];
     3         //上面看起来不像是通过函数创建出一个对象,其实其本质如下
     4 
     5         var obj = new Object();
     6         obj.a = 10;
     7         obj.b = 20;
     8 
     9         var arr = new Array();
    10         arr[0] = 5;
    11         arr[1] = 'x';
    12         arr[2] = true;
    13 
    14         console.log(typeof (Object));  // function
    15         console.log(typeof (Array));  // function

      通过上述代码理解还是非常的迷惑吧。对象是函数创建的,而函数却又是一种对象,函数和对象到底是什么关系啊?我们就通过prototyoe原型来加以理解。

          每个函数都有一个属性叫做prototype(原型)。这个prototype的属性值是一个对象(属性的集合),默认的只有一个叫做constructor的属性,指向这个函数本身。如图(SuperType是一个函数,右侧的方框就是它的原型)

     

      当然,原型作为一个对象,不可能只含有consrtuctor这一个属性而已,我们可以在其中添加一些我们自定义的属性,例如:

    1         function Fn() { };
    2         Fn.prototype.name = 'hello';
    3         Fn.prototype.getYear = function () {
    4             return 2015;
    5         };

     这样原型属性就增加了:
      问题是,这样有什么用呢?我们用代码来演示下:
    1         function Fn() { };
    2         Fn.prototype.name = 'hello';
    3         Fn.prototype.getYear = function () {
    4             return 2015;
    5         };
    6 
    7         var fn = new Fn();
    8         console.log(fn.name);         //hello
    9         console.log(fn.getYear());    //2015

      Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。因为每个对象都有一个隐藏的属性 ----“__proto__”,这个属性引用了创建这个对象的函数的prototype。即:fn.__proto__ === Fn.prototype.这里的"__proto__"成为“隐式原型”

     prototype与__proto__的区别

       两者都是对象类型的属性,并非所有的对象类型都有prototype属性,一般只有function对象才有prototype属性(除非主动赋值),它指向的是一个对象,将来会被多个该function的实例所继承(或者说该对象处于多个实例的原型链上);__proto__才是真正的原型链的实际指针,然而许多浏览器并不对外公开这个属性,Firefox暴露出这一属性,仅供开发人员理解,但不推荐开发中使用,目前chrome也可以支持了。

      小插曲:instanceof表示的就是一种继承关系,或者原型链的结构

      typeof在判断到引用类型的时候,返回值只有object/function,你不知道它到底是一个object对象,还是数组,还是new Number等等。这个时候就需要用到instanceof.

    instanceof的判断队则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。如下图:

        

         所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function),JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,MathJSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。如下:

     1     Number.__proto__ === Function.prototype  // true
     2     Boolean.__proto__ === Function.prototype // true
     3     String.__proto__ === Function.prototype  // true
     4     Object.__proto__ === Function.prototype  // true
     5     Function.__proto__ === Function.prototype // true
     6     Array.__proto__ === Function.prototype   // true
     7     RegExp.__proto__ === Function.prototype  // true
     8     Error.__proto__ === Function.prototype   // true
     9     Date.__proto__ === Function.prototype    // true
    10 
    11     Math.__proto__ === Object.prototype  // true
    12     JSON.__proto__ === Object.prototype  // true

      注:上述代码中__proto__目前在IE6/7/8/9中都不支持,IE9中可以使用Object.getPrototypeOf(ES5)获取对象的内部原型。除了IE(IE11开始支持),其他的浏览器支持非标准的访问器__proto__。那么那些不支持__proto__属性的可以通过constructor间接得到,constructor属性不是对象自己的属性,而是顺着原型链向上从原型对象中获取的。这个属性指向的是这个原型对象所对应的构造函数。而构造函数的prototype属性指向了原型对象, 所以这样我们就可以间接得到了,例:

         function Foo(){};
         var foo = new Foo();
         alert(foo.constructor.prototype == Foo.prototype); // true

      每个对象都有一个__proto__属性,原型链上的对象正是依靠这个__proto__属性连结在一起的,__proto__是否指向实例对象的原型prototype对象的引用。什么是原型链呢?简单来说,访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。所有对象都继承于Object,原型链的顶端就是Object.prototype,Object.prototype.__proto__ = null;看图理解下:

      

      对象的原型链是沿着__proto__这条线走的,因此在查找f1.hasOwnProperty属性时,就会顺着原型链一直查找到Object.prototype。由于所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这也是所谓的“继承”。 说一个函数的例子吧,我们都知道每个函数都有call,apply方法,都有length,arguments,caller等属性。为什么每个函数都有?这肯定是“继承”的。函数由Function函数创建,因此继承的Function.prototype中的方法。

      理解了原型与原型链后,那么用原型有什么好处呢?

      1.对象属性可以随时改动,对象或者函数,刚开始new出来之后,可能啥属性都没有。但是你可以根据你的需要继续添加,非常灵活。

      2.如果继承的方法不合适,可以做出修改。例如在json2.js源码中,为Date、String、Number、Boolean方法添加一个toJSON的属性。

      

      3.可以继续创建新的方法,不过如果你要添加内置方法的原型属性,最好做一步判断,如果该属性不存在,则添加。如果本来就存在,就没必要再添加了。

      记:博文参考了网上很多关于原型的介绍,归纳得不足之处,请指正。

  • 相关阅读:
    Asp.net web api部署在某些服务器上老是404
    log4net记录日志到数据库自定义字段
    PIE-Basic 投影变换
    PIE-Basic 波段合成
    PIE-Basic 空间量测
    PIE-Basic 存储格式转换
    PIE-Basic 位深转换
    PIE-Basic 数据拉伸与显示
    PIE-Basic数据信息查看
    PIE-Basic 影像格式转换
  • 原文地址:https://www.cnblogs.com/wy2999/p/4941361.html
Copyright © 2011-2022 走看看