javascript中__proto__和prototype的区别

我就按照我学习的过程来写吧,不一定对,仅供参考,不对的请斧正:
————-开篇甜点————-
首先呢,先列个代码:
function a(){
return “hello1″;
};
a.say=”hello2”;
alert(a.say);//弹出hello2
以上代码理解不?说明定义的这个function它不仅包含它本身的定义内容:return “hello1”;还可以包含其他东西。
—————是什么—————-
既然可以定义say,就可以定义prototype,只是prototype这个东西是官方定义好了我们可以直接用的。
你知道的,现在编程都喜欢用面向对象的,所以js也想面向对象,于是就引进了prototype与__proto__ 。
然后就给出一个官方的用法定义:prototype用在构造函数上,__proto__用在实例对象上。
什么是构造函数呢?随便定义一个function就是构造函数,比如上文的a,此处的构造函数实际上就是其他面向对象语言中的类class,不同的地方就是其他语言的类必须实例化之后才能用,而js不实例化也能直接调用;
什么是实例对象呢?就是new a();
new之后的东西比如new a()有__proto__而没有prototype;
构造函数a,有prototype而没有__proto__。
并且console.log(new a().__proto__==a.prototype) //true。
————–一段结论模糊的小猜测—————

写到这里我突然冒出个想法,既然一样,为啥不都写成prototype呢?以前我还没想过这个问题,刚才也没搜到相关资料,所以我自己猜测一下吧:
假如__proto__改成prototype,那么new a().prototype==a.prototype肯定没问题。但问题出在这个new上。
js中,new的过程拆分成以下三步:
(1) var b={}; 
(2) b.__proto__ = a.prototype;
(3) a.call(b); 
看到没,之所以new a().__proto__==a.prototype,是因为第二部的赋值操作。
但是__proto__改成prototype,第二步就变成b.prototype = a.prototype;单独看肯定没问题,但是此处存在一个隐患:
(js的)面向对象中,只能从构造函数中new出实例对象,不能从实例对象中new出实例对象。即b=new a();但不能 c=new b;
如果__proto__不改成prototype,那么c=new b的时候,在new过程的第二步,就是c.__proto__ = b.prototype;因为b中有__proto__而没有prototype,所以此步报错,但是__proto__改成prototype,那就不会报错导致new成功,那么整个面向对象的流程就错了。所以__proto__不能改成prototype。

以上是我的推测,应该有疏漏,因为:
var a=new  Function();
a.prototype.say="hello2";
var b=new a();
console.log(b.say);
也new了两次,但能成功运行。

请高人指点,多谢

—————-干嘛的————–
接下来继续,既然知道了prototype跟__proto__是什么了,那他俩是干嘛的呢?结论是用来实现原型链继承的。那接下来再细说说。

先说说引入prototype的目的吧,因为没有prototype就没有__proto__什么事了。
话还要从面相对象说起,先上代码:(以下代码以及解释是从阮一峰博客窃来的,嗯,程序员之间抄代码能叫偷么~Javascript 面向对象编程(一):封装

function Cat(name,color){
this.name = name;
this.color = color;
this.type = “猫科动物”;
this.eat = function(){alert(“吃老鼠”);};
}
var cat1 = new Cat(“大毛”,”黄色”);
var cat2 = new Cat (“二毛”,”黑色”);
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。
alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = “猫科动物”;
Cat.prototype.eat = function(){alert(“吃老鼠”)};
然后,生成实例。
var cat1 = new Cat(“大毛”,”黄色”);
var cat2 = new Cat(“二毛”,”黑色”);
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠
这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
alert(cat1.eat == cat2.eat); //true

以上就是阮一峰为我们解释的引入prototype的作用。
引入__proto__的目的就是让实例出来的对象共享构造函数的prototype里的内容。

———–那再说说怎么用————–
其实怎么用很简单,就是上面的例子,只要定义好prototype,__proto__基本不用管,反正new的时候系统会自己配置好。就这么简单。

但是往深了说,prototype与__proto__组成的原型链,来判断各对象之间的关系却意义非凡。典型的就是instanceof的应用。instanceof展开说也有一大堆,自己去搜搜吧。

————-一个直观但又不怎么恰当的类比————–
用个不恰当的例子吧,就好比拍照片,我本人就是构造函数,每张照片就是实例化后的对象,我穿的衣服就是定义在prototype上的内容,照片中的衣服就是__proto__,照片中的衣服实际上就是我本人穿的衣服的副本,完全相同,但又不一样。你可以根据照片上的衣服(__proto__)找到我穿的实际的衣服(prototype),进而找到卖衣服的厂商(Function)以及加工车间(Object)。嗯,这还是个直营的服装生产商。。。
console.log(Object instanceof Function);//true
console.log(Function instanceof Object);//true

未经允许不得转载:前端撸码笔记 » javascript中__proto__和prototype的区别

上一篇:

下一篇: