星空网 > 软件开发 > Java

深入理解Javascript中构造函数和原型对象的区别

 

 

  在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor),instanceof运算符的一些特点。如果对prototype和构造函数不熟悉,可以前往Javascript中prototype属性的详解 和 Javascript 中构造函数与new命令的密切关系 仔细的品味品味。先来做一个简单的回顾。

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

  其次,为了解决构造函数的属性和方法无法被对象实例所共享的问题,我们可以把需要共享的属性和方法放在原型(prototype)对象上。原型对象上的所有属性和方法,都会被对象实例所共享。对于构造函数来说,prototype是作为构造函数的属性;对于对象实例来说,prototype是对象实例的原型对象。所以prototype即是属性,又是对象。

  然后,除了undefined和null之外,每一个数据类型都可以看成一个对象,每一个对象都有它的原型。所有一切对象的原型顶端,都是Object.prototype,即Object构造函数的prototype属性指向的那个对象。当然,Object.prototype对象也有自己的原型对象,那就是没有任何属性和方法的null对象,而null对象没有自己的原型。

  原型链的特点有:

  a:读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined

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

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

  再次,constructor属性是原型对象上的一个属性,可以被所有实例对象所共享。要注意的是,prototype是构造函数的属性,而constructor则是构造函数的prototype属性所指向的那个对象,也就是原型对象的属性。由于constructor属性是一种原型对象和构造函数的关系,所以在修改原型对象的时候,一定要注意constructor的指向问题。

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

 

 

  在接下来的分享中,会谈谈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的属性和方法:

深入理解Javascript中构造函数和原型对象的区别

Object.prototype的属性和方法:

深入理解Javascript中构造函数和原型对象的区别

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: 18010   };11   var rascal ={12     __proto__: keith13   };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);  //true6   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:1803   };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); //true5   console.log(girl.constructor === Keith);  //true6   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)); //true6   console.log(o2.isPrototypeOf(o3)); //true7   console.log(o1.isPrototypeOf(o3)); //true

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

1   function Keith() {};2 3   console.log(Function.prototype.isPrototypeOf(Keith));  //true4   console.log(Object.prototype.isPrototypeOf(Function));  //true5   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()(设置),进行原型对象的读写操作。

 

 

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

  构造函数本身的属性无法被对象实例共享,而原型对象上的属性和方法可以被所用对象实例所共享。

 

 

 

完。

 

 

感谢大家的阅读。




原标题:深入理解Javascript中构造函数和原型对象的区别

关键词:JavaScript

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流