JS中prototype属性

prototype简介
在面向对象的语言中,C#,Java中都存在类的概念,类就是对象的模板,对象就是类的实例。但是在JavaScript语言中,是不存在类的概念的,JavaScript是不基于类的,而是通过构造函数(constructor)和原型链(prototype chains)实现的。
构造函数

所谓构造函数,就是提供了一个生成对象的模板并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。总的来说,构造函数就是对象的模板,对象就是构造函数的实例。

JS中构造函数特点:

  • 构造函数的函数名首字母必须大写;
  • 内部使用this对象,来指向将要生成的对象实例。
  • 使用new操作符来调用构造函数,并返回对象实例。

看一个实例:

function Person(){			//构造函数
 this.name = 'keith';
}
 var boy = new Person();	//实例化
console.log(boy.name); //'keith'
构造函数缺点

上面简单了解了构造函数,相信我们非常清楚构造函数的重要性,但是,它是不是也存在缺点呢?

是的,构造函数可以构造出很多对象,每个对象都可以不同,但是对于对象实例之间,相同的属性不可以共享,只能重复声明。

function Person(name,height){
 this.name=name;
 this.height=height;
 this.hobby=function(){
 return 'watching movies';
}
 }
var boy=new Person('keith',180);
 var girl=new Person('rascal',153);
 console.log(boy.name); 	//'keith'
 console.log(girl.name); 	//'rascal'
 console.log(boy.hobby===girl.hobby); 	//false

一个构造函数构造了两个实例,并且两个实例有相同的属性,虽然是相同的属性,但是它们的hobby方法却不一样,也就是,每创建一个实例时,都会创建一个hobby方法,思考一下,同样都是hobby方法,为什么不能让每一个实例都共享一个hobby方法呢?这样,既节省了资源,又方便很多。这也就是构造函数的缺点:同一个构造函数构建的对象实例之间无法共享属性或方法。

那么,我们如何改善呢?

prototype属性
为了解决构造函数实例之间无法共享属性和方法的问题,JS提供了prototype属性。

在javascript中,每个数据类型都是对象(除了null和undefined),而每个对象都继承自另一个对象,而后者成为“原型”(prototype)对象,只有null除外,它没有自己的原型对象。 原型对象的所有属性和方法,都会被实例对象所共享。
function Person(name,height){
this.name=name;
this.height=height;
}
Person.prototype.hobby=function(){
return 'watching movies';
}
var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby); //true

通过构造函数生成对象实例时,都会将对象实例的原型指向构造函数的prototype属性,每一个构造函数都有一个prototype属性,这个属性就是对象实例的原型对象。

在上面代码中,将hobby方法放在原型对象上,那么构造出来的对象实例就可以共享hobby方法。对于构造函数来说,prototype作为构造函数的属性,而对于对象实例来说,对象实例的原型对象是prototype,所以,prototype既是属性,又是对象。

上面代码中,当修改了原型的hobby方法后,两个对象实例都发生了变化,这是因为对象实例其实根本没有hobby方法,就是读取原型对象的hobby方法,也就是说,当某个对象实例没有该属性和方法时,就会去原型对象上查找,如果实例对象自身有某个属性或方法,就不会去原型对象上查找。

boy.hobby=function(){
 return 'play basketball';
 }
 console.log(boy.hobby()); //'play basketball'
 console.log(girl.hobby()); //'swimming'

当修改了boy对象实例的hobby方法时,boy就不会去原型对象上寻找hobby方法了,不过girl是没有hobby方法的,仍然需要去原型对象上去继承方法。

所以,prototype属性可以使对象实例共享同一个属性或方法。

原型链(prototype chains)
这里一个图片能够帮助理解

在这里插入图片描述

图片中,foo/obj为Foo/Object构造函数创建的对象实例,Foo.prototype/Object.prototype分别是各自构造函数的原型属性。

  • 其中每个对象都具有__ptroto__属性,构造函数也是对象,它也有__proto__属性, 每一个构造函数还有prototype属性。
  • 对于构造出来的对象实例来说,对象实例的原型对象是构造函数的原型属性。
  • 每个构造函数的__proto__属性都指向Function.prototype,Function.prototype是所有函数的原型。
  • 每个构造函数的prototype属性就是各自构造函数的prototype属性
  • 而各个函数的prototype的constructor属性默认指向prototype属性所在的构造函数
  • 每个构造函数原型的constructor都是Object.prototype,一切对象的原型顶端都是Object.prototype,也就是Object构造函数的prototype属性指向的对象,Object.prototype的__proto__属性是null

原型链的特点:

  • 读取对象的某个属性使,javascript会先寻找对象本身的属性,如果找不到,就去它的 原型对象中去找,如果还找不到,就去原型的原型中去找,如果直到最顶层的Object.prototype还是找不到,则返回undefined.
  • 如果对象自身和它的原型,都定义了一个同名函数,那么优先读对象自身的属性,也叫"覆盖"
  • 一级级向上在原型链中寻找某个属性,对性能是有影响的,如果寻找某个不存在的属性,就会遍历整个原型链。
constructor属性

从上方原型链的图片中,可以看到,每个构造函数的prototype对象都有一个constructor属性,默认指向prototype对象所在的构造函数。

由于constructor属性是定义在原型对象上,意味着可以被所有实例对象继承。

function A(){};
 var a=new A();
 console.log(a.constructor); //A()
console.log(a.constructor===A.prototype.constructor);//true

上面代码中,A是构造函数,a是构造函数的一个对象实例,a本身是没有constructor属性的,而是去它的原型对象中去读取constructor。

而constructor属性的作用就是去区分原型对象到底属于哪个构造函数。


以上是本人对javascript的prototype属性的一些理解,欢迎大家与我交流。

猜你喜欢

转载自blog.csdn.net/weixin_42653522/article/details/103587622