首先声明参考博文,感谢
http://blog.sina.com.cn/s/blog_6c62f4330102wq0u.html
http://blog.csdn.net/leadn/article/details/51781539
等作者,还有一些其他链接这里就不粘贴了
js中关于prototype的文章不少,今天自己写一点自己的理解
不喜欢上来就粘概念,因为如果概念能够理解,也不需要写这篇文章了。
关于原型链里面的东西,主要就是prototype,__proto__这两个属性难以理解,今天我们就来看一下怎么玩耍这两个属性
先上代码
:先用js自定义一个对象,同时创建这个对象
function Man(){ // 定义一个对象
}
var m = new Man() ; //创建一个对象
console.log(m.__proto__) //对象的__proto__属性
console.log(m.prototype) //对象的prototype属性
console.log("========================")
console.log(Man.__proto__) //函数的__proto__属性
console.log(Man.prototype) //函数的prototype属性
结果
我们就直接直观的看结果就好了
1.明显的m.prototype打印为undefined
2.m.__proto__和Man.prototype打印的结果是一样的,可以自己手动证明(m.__proto__ == Man.prototype 返回 true)
3.还有一个Man.__proto__打印的东西好奇怪 function() ,无所谓,暂时不管他
这个时候该上概念了
prototype是构造函数的属性,指的就是构造函数的原型,在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类,只有构造函数这个属性才有这种效果哦~如果一个构造函数没有指定该属性,那么该属性下的__proto__会默认的指向原生Object的原型对象,该属性会变成一个对象,其中constructor属性指向本身。(这句直接粘来的,暂时看不懂就看我标红就行)
__proto__是对象具有属性,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。
开始解释
1.为什么m.prototype打印为undefined?
废话,prototype是构造函数的属性,m是个对象,自然没有这个属性了,所以打印undefined
2.m.__proto__和Man.prototype打印的结果是一样的
他俩其实说的就是一个对象,看上面的蓝字,都是指向原型,我这里用的同一个对象,自然相等啊
3.不解释,等着
那么这两个东西怎么用呢?
你再看概念,prototype是构造函数的属性,__proto__是对象具有属性
其实,只要区分清楚函数和对象,就能调用对应的属性,达到你想要的结果
别跟我说函数是对象,容易混淆,函数就是函数
到此,你只要能够知道的这么两点就行了
1.只有函数可以调用prototype属性
2.对象可以调用__proto__,但是不能调用prototype属性
接下来说原型链
其实,你只要理解了上面的两点,原型链就简单多了
开始
先回顾上面概念:看上面绿字(在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类),简单讲,就是函数的prototype属性指向了他爸爸。
再来看prototype的字面意思:原型,啥意思呢,就是说对象在创建之初,是根据原型的样子来创造的(就像女娲造人,照着自己的样子造,也可以理解为照着图纸造大楼)
,也就是说原型是谁,他就象谁。
那么,上面的Man.prototype怎么指向了自己的函数对象呢?
没有指定过prototype的函数就像孤儿一样,没有爸爸,怎么办呢,自己就是自己的爸爸(自己照顾自己呗),既然不能长得像别人,那就像自己好了
原型链,要想链起来肯定得有多个对象啊,(其实所有的对象都来自Object,这里怕引起误会,就不用Object了)
我们再声明一个对象People
function People(){ this.name = "Bullet"; }
然后让people成为Man的爸爸(这里就是实现js的继承,这种继承代码不够健壮,可以自己百度其他的继承方式,但这也算一种继承方式)
很简单,不是说函数的prototype属性指向了他爸爸么,那么我就
Man.prototype = new People(); //这里也只能用Man.prototype,因为对象是没有prototype属性的,这里后面要用new 是因为prototype指向的是一个对象哦
这样,构造Man的时候就不照着Man构造了,而是照着People构造,既然照着People构造,People有的Man也得有啊。这样就实现了继承
(测试继承的代码)
function People(){ this.name = "Bullet"; } function Man(){ } Man.prototype = new People(); //让Man认People做爸爸
var m = new Man(); console.log(m.name); //这里就能继承父类的name属性了,会打印Bullet
那么,有了继承,如果子类的属性没有自定义的话,就会使用父类的属性,(就像这里可以读到父类的name属性一样)合情合理啊,但是Man到底怎么找到People的name属性的呢
这就要循着原型链寻找了
我们这里要找m的name属性,m本身是没有name的,怎么办?找他爸爸要啊,可他爸爸怎么找呢?我们只设定了Man.prototype = new People();,但是我m是个对象,有没有prototype属性,怎么办呢?
还能怎么办?,我是个对象,我虽然没有prototype但是我有__proto__属性啊
m.__proto__ == Man.prototype
反之亦然啊
Man.prototype = 爸爸对象
m.__proto__ == Man.prototype
m.__proto__ = 爸爸对象
恭喜m对象顺利找到爸爸!!!这样就可以用爸爸的属性了呦
结论
所以子类找父类就像这样
首先我们根据prototype 来实现继承,但是子类找父类时却要通过__proto__属性。
没办法子类没有prototype啊,你能拿我怎么样?
子对象.__proto__,这样就找到了父类,
原来__proto__属性就是对象用来找爸爸的属性啊!!!
接着,找到的父类是个对象啊,就能继续__proto__找到爷爷,一致这么找下去
所以 对象.__proto__.__proto__.__proto__.__proto__....是能一直点到尽头的(null)
这个.__proto__.__proto__.__proto__.__proto__....组成的就是一条原型链
这时,有人会有疑问
函数.prototype.prototype...可以一直点么
肯定不行啊,都说了 函数.prototype 获取(或设置)的是个对象,对象没有prototype属性,所以点到第二层就已经挂掉了(undefined)
console.log(Man.prototype.prototype); // 这里一定为undefined Man.prototype指向的是一个对象,对象没有prototype属性
这也又一次解释了问题一
1.为什么m.prototype打印为undefined?
废话,prototype是构造函数的属性,m是个对象,自然没有这个属性了,所以打印undefined
两者可以结合,我们可以
函数.prototype.__proto__.__proto__.__proto__....是能一直点到尽头的,最后为null
最后,我们解释最后一个问题
3.为什么Man.__proto__打印的东西好奇怪 ,是一个function()
首先他不是undifined,说明函数是有__proto__这个属性 ,怎么强行解释一波呢?
很简单,我先道个歉,对不起,因为函数他确实是个对象,刚才让你区分是为了好解释,现在你再想起来吧(因为万物皆对象,函数当然也是对象)
所以Man这个函数是可以使用__proto__属性的
上面又说了__proto__相当于找爸爸的属性,
我们使用Man.__proto__返回function() 说明function()是这个函数的爸爸,我们还能再继续.__proto__,能够找到Object,说明fanction也是Object的一个子类
console.log(Man.__proto__); //找到爸爸function() console.log(Man.__proto__.__proto__); //找到爷爷Object{}
解释一:
这里为什么不是找到People,原因是因为这里是一个Man函数对象,函数是通过function()来构造的,上面的继承关系是对对象(就是m)来讲的,
所以Man函数的爸爸是function,而new Man()对象的爸爸是People,不要混淆对象和函数的区别就好(虽然我知道函数也是个对象)
解释二:
网上经常可以看到这张好乱的对象关系
你慢慢看原图,我再给你画一个(虽然很丑)
使用Object这个超类来找父类的时候会返回null(传说中的无中生有),可以自行百度,上面参考链接中有提到返回null的原因
最后总结
对象找爸爸用__proto__属性来找,这个大家都有,万物皆对象
函数因为可以构造,所以可以使用prototype来找他是根据谁构造出来的,但是只能找一层,因为函数是根据对象构造出来的,继续找就只能用__proto__了
所以, 一个对象.__proto__ == 这个对象的函数.prototype
翻译成人话就是 对象他爸 == 构造这个对象的对象
记住只要分清对象和函数两个东西,使用__proto__和prototype就不会再有难度了
头一次写博客,大家可以随意喷啊,促进进步,哈哈哈