原型陷阱

来源:互联网 发布:蒙泰软件下载官方网站 编辑:程序博客网 时间:2024/05/20 03:41
       为什么要写这篇呢?前段时间在看别人用js写一个flappy bird游戏的源码的时候,发现里面用到了借用构造器这个方法来实现对象之间的继承,自己看得有一脸蒙B,毕竟当初继承这方面没怎么看,很多东西也没用过,都忘记了,所以自己把书找来又重新看了一遍,从最开始的对象,到原型,再到继承,发现又收获了很多知识,赶紧来记录一下;这篇就先写一下"原型陷阱”吧。


先用书上的例子说明一下什么是原型陷阱吧。


新建一个构造器函数,然后实例出1个对象

function Dog(){  this.tail=true;}var benji=new Dog();


因为是带着学习的目的,所以我想把尽可能多的知识点就提一下;


1.new到底做了什么?
var obj={ };
obj.__proto__=Dog.prototype;
var result=Dog.call(obj);
return typeof  result==='object'? result :obj;
其实这里我们可以发现,如果构造器函数返回的是一个非对象类型(或者不返回)时,会正常返回this;如果返回的是一个对象,那么我们将得到的是这个对象,说起来很模糊,我们用例子来说明一下;
根据上面的代码,我们benji是Dog实例出来的对象,那么我们benji.tail会等于true,这一点毋庸置疑;但是我们改一下代码呢?



function Dog(){  this.tail=true;  return {   tail:false;  }}var benji=new Dog();

如果是这样的代码,我们benji.tail则是false; 这里是我们需要注意的一个点;



2.老生常谈的.__proto__ 和prototype

prototype是每一个函数都有的属性,叫做原型,只有这个函数被当做构造器函数的时候才起作用;而.__proto__是每个对象的属性,指向的是这个对象的构造器函数的原型;




在构造器函数的原型中再添加属性

Dog.prototype.say=function( ){  return 'woof';}


这里我们发现无论是在添加原型方法之前实例出来的对象,还是之后实例出来的对象,都能使用这个新方法;

var rusty=new Dog();rusty.say();//woofbenji.say();//woof

这个很简单嘛,rusty.__proto__=Dog.prototype


用一个自定义的新对象完全覆盖掉原有的原型对象

Dog.prototype={    hair:true}


然后就出现了一个很奇怪的现象:
var lucy=new Dog();lucy.say();//报错lucy.hair//truebenji.say();//woofbenji.hair//报错

我们新实例出来的对象可以访问新添加的原型属性,但是不能使用以前的原型属性,但是以前实例出来的对象和它正好相反;


问题出在哪里呢?
我们可以利用constructor这个属性来帮我们查找一下;
这里我们需要注意一下:constructor是原型对象上的一个属性,默认指向这个原型的构造函数。
举个栗子:

function fn(){}fn.prototype.constructor===fn //true



然后我们再这样操作一下:

lucy.constructor //function Object(){[native code]}benji.constructor // function Dog(){this.tail=true}

我来解释一下,刚前面说了constructor是原型上的方法,我们实例出来的对象其实是没有的,他们是通过__proto__去寻找其构造器函数的原型上使用的这个方法;


lucy.constructor其实是lucy.__proto__.constructor 
我们发现他们的构造器函数错了,所以其函数上的原型也是错了,自如也找不到原型上定义的方法了;
这里我们终于知道排查到错误的原因了,是因为实例的构造器函数错了,为什么会错呢?是因为问我们完全覆盖prototype其实是对象赋值,对象赋值是传的引用,导致我们原型对象的地址发送了改变;

Dog.prototype={    hair:true}


上面这一句其实可以改成
var obj={  hair:true}Dog.prototype=obj

而我们这个obj对象的构造器函数是Object这个函数;


解决办法就是重新把其原型上的constructor重置一下;

Dog.prototype.constructor=Dog;



到这里我觉得自己明白了原型陷阱的原因了,不过这里我当初有一个小纠结的地方,后来写完这篇后解惑了;
function fn(){}fn.constructor//function Function() { [native code] }

这里我刚才是的疑惑是函数不是有prototype属性么?而这个属性的prototype的cnstructor属性指向的就是这个函数本身啊,为毛这里会是Function,后来一看是我傻逼了,fn去调用constructor属性的时候,发现自己没有的时候,会去.__proto__去寻找,而我以为是去其prototype中寻找~~╮(╯▽╰)╭ 好蠢,糊涂了,还好不是面试;(其实所有函数都是被Function 这个构造器函数实例出来的;)






原创粉丝点击