深刻理解Javascript中构造函数和原型对象的区别(转存)

 Object是构造函数,而Object.prototype是构造函数的原型对象。构造函数自身的属性和方法没法被共享,而原型对象的属性和方法能够被全部实例对象所共享。javascript

  首先,咱们知道,构造函数是生成对象的模板,一个构造函数能够生成多个对象,每一个对象都有相同的结构。构造函数的缺点就是,每当你实例化两个对象时,须要调用两次构造函数的某一个方法,这带来的坏处就是占用内存,并且不必。java

  其次,为了解决构造函数的属性和方法没法被对象实例所共享的问题,咱们能够把须要共享的属性和方法放在原型(prototype)对象上。原型对象上的全部属性和方法,都会被对象实例所共享。对于构造函数来讲,prototype是做为构造函数的属性;对于对象实例来讲,prototype是对象实例的原型对象。因此prototype便是属性,又是对象。数组

  而后,除了undefined和null以外,每个数据类型均可以当作一个对象,每个对象都有它的原型。全部一切对象的原型顶端,都是Object.prototype,即Object构造函数的prototype属性指向的那个对象。固然,Object.prototype对象也有本身的原型对象,那就是没有任何属性和方法的null对象,而null对象没有本身的原型。浏览器

  原型链的特色有:框架

  a:读取对象的某个属性时,JavaScript引擎先寻找对象自己的属性,若是找不到,就到它的原型去找,若是仍是找不到,就到原型的原型去找。若是直到最顶层的Object.prototype仍是找不到,则返回undefined函数

  b:若是对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫作“覆盖”(overiding)。性能

  c:一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。若是寻找某个不存在的属性,将会遍历整个原型链。ui

  再次,constructor属性是原型对象上的一个属性,能够被全部实例对象所共享。要注意的是,prototype是构造函数的属性,而constructor则是构造函数的prototype属性所指向的那个对象,也就是原型对象的属性。因为constructor属性是一种原型对象和构造函数的关系,因此在修改原型对象的时候,必定要注意constructor的指向问题。spa

  最后,instanceof运算符返回一个布尔值,用于判断对象是否为某个构造函数的实例。prototype

 

 

  在接下来的分享中,会谈谈Object的部分方法和Object.prototoype的部分方法。虽然都是概念性问题,可是若是理解了这些概念,对于MVVM框架和各类js框架的理解都有至关大的帮助。

  如下的分享会分为以下内容:

  1.Object和Object.prototype的区别

  2.Object.getPrototypeOf()

  3.Object.setPrototypeOf()

  4.Object.create()

  5.Object.prototype.isPrototypeOf()

  6.Object.prototype.__proto__

 

 

1.Object和Object.prototype的区别

  我的认为,要学好javascript的其中一个方法就是,必须理解每个" . "所表明的意思是什么,是调用自身的属性和方法呢,仍是继承原型的对象的属性和方法。来看看Object构造函数和构造函数的原型Object.prototype都有哪些属性和方法。

  Object是构造函数,而Object.prototype是构造函数的原型对象。构造函数自身的属性和方法没法被共享,而原型对象的属性和方法能够被全部实例对象所共享。

Object的属性和方法:

Object.prototype的属性和方法:

  上面例子中,Object拥有本身的方法prototype,getPrototypeOf(),setPrototypeOf()等,这些方法没法被实例所共享。而Object.prototypeOf()的hasOwnProperty,isPrototypeOf(),constructor等属性和方法是能够被实例对象所共享的。举一个最简单的例子。

1     function Keith() {}
2     var a = new Keith();
3     console.log(a.prototype);    //undefined
4     console.log(a.constructor);    //Keith()

  上面代码中,构造函数Keith是没有任何属性和方法的。当访问prototype属性时返回undefined,是由于prototype属性没有办法从构造函数中继承,只能由构造函数自己访问。而constructor返回了Keith(),由于constructor属性自己就是Object.prototype中的属性,能够被全部实例对象所共享。

  那么问题来了,如何知道实例对象的原型呢?能够经过Object.isPrototypeOf方法和继承原型对象的isPrototypeOf方法实现。

1     console.log(Keith.prototype.isPrototypeOf(a));    //true
2     console.log(Object.getPrototypeOf(a) === Keith.prototype) //true

  上面代码中,实例对象a的原型就是Keith.prototype。这两个属性会稍后介绍。

 

2.Object.getPrototypeOf()

  Object.getPrototypeOf方法返回一个对象的原型。这是获取原型对象的标准方法。

复制代码
 1     // 空对象的原型是Object.prototype
 2     console.log(Object.getPrototypeOf({}) === Object.prototype) // true
 3     
 4     // 函数的原型Function.prototype
 5     function keith() {}
 6     console.log(Object.getPrototypeOf(keith) === Function.prototype)     //true
 7 
 8     // 数组的原型Array.prototype
 9     var arr = [1,2,3];
10     console.log(Object.getPrototypeOf(arr) === Array.prototype) ///true
复制代码

  

3.Object.setPrototypeOf()

  Object.setPrototypeOf方法能够为现有对象设置原型,而后返回一个新对象。这个能够接收两个参数,第一个是现有对象,第二个是原型对象。

复制代码
 1     var keith = {
 2         height: 180
 3     };
 4     var rascal = Object.setPrototypeOf({}, keith);
 5     console.log(rascal.height);    //180
 6 
 7     //上下两个代码片断相同。
 8     var keith = {
 9         height: 180
10     };
11     var rascal ={
12         __proto__: keith
13     };
14     console.log(rascal.height);    //180
复制代码

  上面代码中,rascal对象是Object.setPrototypeOf方法返回的一个新对象。该对象自己为空、原型为keith对象,因此rascal对象能够拿到keith对象的全部属性和方法。rascal对象自己并无height属性,可是JavaScript引擎找到它的原型对象keith,而后读取keith的height属性。

 

4.Object.create()

  Object.create方法用于从原型对象生成新的对象实例,能够代替new命令。它接受一个参数,这个参数为所要继承的原型对象,而后返回一个实例对象。

复制代码
1     var Keith = {
2         hobby : function() {
3             return 'Watching Movies';
4         }
5     };
6 
7     var rascal = Object.create(Keith);
8     console.log(rascal.hobby())    //'Watching Movies'
复制代码

  上面代码中,Object.create方法将Keith对象做为rascal的原型对象,此时rascal就继承了Keith对象中的全部属性和方法。rascal就成为了Keith对象的实例对象。用下面这段代码比较好理解。

复制代码
1     function Keith() {};
2     Keith.prototype.hobby = function() {
3         return 'Watching Movies';
4     }
5 
6     var rascal = Object.create(Keith);
7     console.log(rascal.hobby())    //'Watching Movies';
复制代码

  new操做符和Object.create方法都是返回一个对象实例,可是二者有一些区别。

复制代码
1     function Keith() {}
2     var a = new Keith();
3     var b = Object.create(Keith.prototype);
4 
5     console.log(a instanceof Keith);    //true
6     console.log(b instanceof Keith);    //true
复制代码

  上面代码中,可使用new操做符来调用构造函数,返回对象实例;而Object.create传入的参数必须是构造函数Keith的原型。

  实际上,若是有老式浏览器不支持Object.create方法,能够用下面这段代码来构造一个Object.create方法。

复制代码
1     if (typeof Object.create !=='function') {
2         Object.create = function(x) {
3             function F() {};
4             F.prototype = x;
5             return new F();
6         };
7     }
复制代码

  下面这三种方式生成的实例对象都是等价的。

1     var o1 = Object.create({});
2     var o2 = Object.create(Object.prototype);
3     var o2 = new Object();

  在使用Object.create方法时,要注意的是必须传入原型对象,不然会报错。

1     var o1 = Object.create();
2     console.log(o1);//TypeError: Object.create requires more than 0 arguments

  Object.create方法生成的对象实例,动态继承了原型对象。也就是说,修改原型对象的属性和方法会反应在对象实例上。

复制代码
1     var keith = {
2         height:180
3     };
4 
5     var rascal = Object.create(keith);
6     keith.height=153;
7     console.log(rascal.height)    //153
复制代码

  上面代码中,修改原型对象,会影响生成的对象实例。

  Object.create方法生成的对象,继承了它的原型对象的构造函数。

复制代码
1     function Keith() {};
2     var boy = new Keith();
3     var girl = Object.create(boy);
4     console.log(Object.getPrototypeOf(girl) === boy); //true
5     console.log(girl.constructor === Keith);    //true
6     console.log(girl instanceof Keith);    //true
复制代码

  上面代码中,girl对象的原型是boy对象,girl对象的constructor属性指向了原型对象boy的构造函数Keith。

 

5.Object.prototype.isPrototypeOf()

  对象实例的isPrototypeOf方法,用于判断一个对象对象是不是另一个对象的原型。

复制代码
1     var o1 = {};
2     var o2 = Object.create(o1);
3     var o3 = Object.create(o2);
4 
5     console.log(o1.isPrototypeOf(o2)); //true
6     console.log(o2.isPrototypeOf(o3)); //true
7     console.log(o1.isPrototypeOf(o3)); //true
复制代码

  上面代码中,能够看出,只要某个对象处于原型链上,isPrototypeOf都返回true。

1     function Keith() {};
2 
3     console.log(Function.prototype.isPrototypeOf(Keith));    //true
4     console.log(Object.prototype.isPrototypeOf(Function));    //true
5     console.log(Object.getPrototypeOf(Object.prototype) === null); //true

  上面代码中,构造函数Keith的原型指向了Function.prototype,而构造函数Function的原型指向了Object.prototype。Object的原型指向了没有任何属性和方法的null对象。

 

6.Object.prototype.__proto__

  __proto__属性(先后两条下划线)能够改写某个对象的原型对象。这个属于实例方法。

1     var keith = {};
2     var rascal = {};
3     rascal.__proto__ = keith;
4     console.log(keith.isPrototypeOf(rascal));    //true

  上面代码中,经过rascal对象的__proto__属性,将rascal的原型指向了keith对象。

  __proto__属性只有浏览器才须要部署,其余环境能够没有这个属性,并且先后的两根下划线,表示它本质是一个内部属性,不该该对使用者暴露。所以,应该尽可能少用这个属性,而是用Object.getPrototypeof()(读取)和Object.setPrototypeOf()(设置),进行原型对象的读写操做。

 

 

  来作一个小小的总结,上面对一些属性和方法的介绍均可以归结为一句话:

  构造函数自己的属性没法被对象实例共享,而原型对象上的属性和方法能够被所用对象实例所共享。

相关文章
相关标签/搜索