javascript-理解原型、原型链

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

      原型是一个对象,其余对象能够经过它实现属性继承,并且任何一个对象均可以成为原型。这是为何呢?请继续看。html

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

 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)都是对象。对象是属性的集合。git

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

 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原型来加以理解。chrome

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

 

  固然,原型做为一个对象,不可能只含有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__的区别app

   二者都是对象类型的属性,并不是全部的对象类型都有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.能够继续建立新的方法,不过若是你要添加内置方法的原型属性,最好作一步判断,若是该属性不存在,则添加。若是原本就存在,就不必再添加了。

 

  记:博文参考了网上不少关于原型的介绍,概括得不足之处,请指正。

相关文章
相关标签/搜索