以前一直对javascript中的prototype不是很理解,今天在阅读了《javascript高级程序设计之后》终于理解了其中的prototype。来简单的总结一下从书中学习到的内容。
我们都知道在创建了一个function之后,这个function就具有了prototype这样的一个属性,利用这个prototype我们可以做很多的事情,其中我们经常用到的一点就是利用它来当做构造函数,因此,本文重要从function作为构造函数的角度来说明一下prototype。
其实javascript中的构造函数与其他的函数没有什么区别,只是调用的方式的略微点不一样。下面来看代码。
function Dog(){ this.name = "Ben"; }
我们声明了一个简单的函数,叫做Dog,如果我们只是简单的调用这个方法 ,那么她只会为全局对象添加一个name属性。并不会生成对象。
但是当我们把这个方法当做构造函数来调用的时候,情况就会不一样了,这个函数会返回一个对象。
var d = Dog(); console.log(d) //undefined //作为构造函数调用 var dog = new Dog(); console.log(dog) // dog对象。
这属于javascript中生成对象的方式了,相信大家应该都明白,下面我们就来详细说明这个prototype在这个构造函数中到底起了什么作用。
在文章开头的时候,就已经说了,在声明构造函数的时候,这个函数已经prototype这样一个属性。在chrome的控制台中,输入代码可以查看到这样的一个结果。
<span style="font-size:18px;"> console.log(Dog.prototype); //Dog {} </span>
实际上函数的prototype指向了一个对象,称作为原型对象,这个对象其中包含了一个属性叫做constructor,但是为什么我们在输出的对象之中看不到它呢,这是在javascript中,把这个属性的Erumerable属性设置为false了,所以在这里我们看不到,但是看不到我们还是可以访问到这个属性。看下面的结果:
console.log(Dog.prototype.constructor); // function Dog(){ // this.name = "Ben"; // }
看到结果,是不是发现了什么,输出来的结构就是我们自己定义的构造函数,那么他们到底是什么关系呢?我们可以使用相等操作符来比较一下。
console.log(Dog.prototype.constructor === Dog);//true
我们可以看到结果,这两个实际上是完成相等的。
这么说来,我们是不是又可以用constructor来访问到prototype呢?没错,这样是完全可以的,这样一样,这里面就相当于形成了一个循环指向关系,这个实际上也可以采用递归的方式来验证一下,调用递归的时候很快就会调用栈溢出,因为在这个递归调用中是没有出口的。
上面的关系我们可以简单的用下面的图来表示一下
好,上面我们已经简单的介绍了一下构造函数中的prototype,那么我们再来看一下,对象中的prototype,在javascript中,声明对象中并没有prototype这个属性,而是使用__proto__(注意:__是两个_) 来访问prototype ,那么对象中的__proto__跟构造函数中的prototype又是什么关系呢?
dog.__proto__ === Dog.prototype;//true
结果一看便知,通过这个结果我们就可以知道同一个类的所有对象的__proto__都是相等的,因为他们实际上都指向了构造函数的prototype。
现在看完了,构造函数和对象的的prototype,我们再来看一下prototype这个对象本身,刚才我们已经提过了,prototype其实本身就是一个对象,既然他是一个对象,我们就可以扩展这个对象,下面我们为这个对象添加一个方法和属性。
Dog.prototype.say = function(){ console.log('wangwang'); } Dog.prototype.color = "black";
我们再来看一下刚才自己定义的那个dog对象,是不是也有了这个方法和属性。
dog.day() ;// 'wangwang' dog.color ;// 'black';
我们通过调用发现,这个这个对象上也有了个方法和属性,但是我们并没有直接在对象添加方法和属性,这就说明了,prototype中的属性和方法也会为对象共享。
在这里共享是通过查找得到的,当我们访问一个对象的属性时,这个对象首先会在自己的属性集里面去找这个属性,如果找到了,那么就直接返回,如果没有找到就会去prototype的属性集里面找。在这个例子中我们当我们访问属性和方法的时候,首先在dog对象里面找,找不到之后再去prototype的属性和方法找。
既然这些方法和属性都是共享的,那么我们修改这些方法和属性会有什么结果呢?
var dog2 = new Dog(); dog.color = "red"; console.log(dog2.color); //black
从结果可以看到,dog2的color属性并没有受到影响,这是因为在使用对象去操作prototype的属性时,并没有真正的操作prototype,而是自己新建了一个color属性,并且将red赋值给了color,所以这里并没有影响到prototype。也就没有影响到dog2了。(读者可以自己验证一下,color是作为dog对象的属性而存在的)。
同样的我们可以看到当我们修改了方法之后,prototype中方法并没有收到影响。
关于prototype的简单说明就到这里。文中如有错位还望各位大神指正。
文章系原创,转载请注明