prototype作为JS相对比较难理解的一个知识点,在这里发表下自己的理解。
本文将包含以下几部分内容:
1、js prototype的简单介绍,
2、js构造函数的介绍,
3、prototype的深入理解,
4、constructor。
一、在其他的面向对象语音中,比如Java,存在类(class)的概念,对象就是类的实例。但是再js当中呢,是没有类的概念的,平常时说的加一个class类是指在样式css中加一个类class。js中一切皆对象,所有的东西都是对象(除了null和undefined),js中所有的东西都是由object衍生而来的,原型链终点指向Object.prototype。
二、js构造函数
在介绍prototype之前先来对JS的构造函数做个简单的介绍。如果熟悉Java的同学应该了解Java的构造函数默认存在类中,也可以自定义类的构造函数,通过构造函数去new 一个对象。那么在JavaScript中什么是构造函数呢,所谓的构造函数,简单来说就是提供了一个生成对象的模板并描述对象的基本结构的函数,对象就是构造函数的实例。
构造函数有一下特点:
1.首字母大写,
2.使用new 来调用构造函数返回实例对象,
3.构造函数中使用this指向生成的对象实例。
下边提供一个简单的例子:
function Person(name){ this.name=name; } var lucy=new Person('lucy'); console.log(lucy.name);
这里将输出lucy。
这例子就是使用Person构造函数使用new关键字生产person的实例对象lucy。
但是构造函数也存在缺点,就是当构造函数存在一个方法A的时候,当创建对象的时候会在每个实例都生成一个A方法,对象与对象之间的A方法是不一样的,无法实现共享,所以就导致资源的浪费。一下例子说明:
function Person(name){ this.name=name; this.say=function(){ alert("say something !") } } var lucy=new Person('lucy'); var tom =new Person('tom'); console.log(lucy.name); //'lucy' console.log(tom.name); //'tom' console.log(lucy.say===tom.say); //false
以上例子就说明同一个构造函数的对象实例之间无法共享属性或方法。
三、关于prototype
上边说的构造函数无法共享的缺点用prototype将得到很好的解决。
js prototype的定义:prototype 属性使您有能力向对象添加属性和方法。
用法:object.prototype.name=value。用法实例:
function Person(name){ this.name=name; this.say=function(){ alert("say something !") } } Person.prototype.eat=function(){ return "eatting"; } var lucy=new Person('lucy'); var tom =new Person('tom'); console.log(tom.eat()); //eatting console.log(lucy.eat()===tom.eat()); //true
上边的代码中,证明两个实例都沿用了同一个prototypesh原型链 中的eat方法,当修改原型对象的eat方法时,两个实例都发生改变,这是因为对象实例本身没有eat方法,都是读取的原型对象中的方法。所以,当实例对象本身没有这个方法时,会往他的原型对象中找,如果原型对象中没有再往上一级找,直到最顶层。如果实例中有跟原型对象中一样的方法就不会再往上找了。
所以:
原型对象的作用,就是定义所有对象实例所共享的属性和方法。
每一个构造函数都存在一个prototype属性,对于对象实例来说,prototype是对象实例的原型对象。所以prototype即是属性,又是对象。
原型链:对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型对象本身对于对象实例来说也是对象,它也有自己的原型,所以形成了一条原型链(prototype chain)。比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。所有一切的对象的原型顶端,都是Object.prototype,即Object构造函数的prototype属性指向的那个对象。
当实例对象读取某个属性时,先看自身有没有这个属性,如果有就不再往上找,如果没有就往他的原型对象找,如果没有就再往上一层原型对象找,知道找到,否则undefined,当实例中存在同名属性时就会优先读取自身的属性,相当于“覆盖”。
平常时我们用的数组的属性或者方法就是用原型对象上的属性或者方法,比如
var arr = [1,2,3]; console.log(arr.length);//用的是Array.prototype里边的length属性
四、constructor
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
function Abc(){ console.log(111) } console.log(abc.prototype.constructor===Abc) //true
下边用一张图解释下这几着之间的关系:
每一个构造函数都存在一个prototype属性,而构造函数生产的实例对象中通过原型链指向了原型对象,原型对象中有一个construtor属性指向了构造函数。