请教下关于 JavaScript 原型对象理解

2015-07-14 22:12:03 +08:00
 83f420984
当我创建一个 Foo( ) 函数时,它就一个 prototype 属性,并指向一个对象,则 Foo prototype 原型对象,这个原型对象默认有一个 constructor 属性,并指向 Foo( ) 函数本身。

问题一:这个 Foo( ) 函数怎么会存在 prototype 属性的同时,还有一个 Foo prototype 原型对象?
问题二:这个 constructor 属性在这里面到底起的是什么作用?

以上有表达有不对的或者其它地方问题的,请谅解,被 prototype 绕晕了,谢谢。
2307 次点击
所在节点    程序员
6 条回复
FrankFang128
2015-07-14 22:21:54 +08:00
每个对象的 __proto__ 属性指向其构造函数的 prototype 属性
方法 function 是一种特殊的对象 Object

琢磨这两句话

constructor 的作用就是指向构造函数。
买本美国人写的JS书看看吧。
YuJianrong
2015-07-14 22:53:11 +08:00
1. 所有函数都是对象,和其他对象区别在于有一个内部成员 [[call]],你调用函数的时候其实就是调用这个成员
2. 所以函数上可以挂任何成员,prototype 是其中之一,一般会挂上一个对象,这时候就叫prototype 原型对象(所以问题一是这是一样的)
3. 当你调用 new FuncA() 的时候,其实做了这些事情
a. 生成一个空对象 {}
b. 设置这个空对象的内部成员 [[prototype]] 为 FuncA.prototype ,这个内部成员在v8中是可以访问的__proto__
c. 以这个空对象为 this 调用 FuncA, 即 FuncA.call(this, xxx)
4. 至于 FuncA.prototype.constructor, 对于一个函数的缺省 prototype来说就是函数本身,即
FuncA.prototype.constructor === FuncA
不过这个其实几乎没有任何用处,你大可忽视这个东西。

盗张图:
http://www.codeproject.com/KB/scripting/687093/PrototypeGraph.png
Biwood
2015-07-15 00:03:32 +08:00
一、为什么函数都有一个原型对象?
因为 JavaScript 里面的任何一个函数都可以作为构造函数使用,所谓构造函数,就是用来构造对象用的函数,使用 new Foo() 操作就能生成一个新的对象,这个新的对象是基于 Foo() 函数的原型对象来生成的,这就是原型对象存在的意义。构造函数在构造一个新对象的过程中需要有一个原型来做参考,这个原型就是构造函数的原型对象。

二、这个 constructor 属性起什么作用?
constructor 翻译过来就是构造器,在这里就是指 new 出来的新对象的构造函数,一般我们通过新对象的 constructor 属性可以找到是哪个函数构造了它,这就是 constructor 属性的作用。原型对象上之所以有 constructor 属性,是因为这个属性要继承给新生成的对象,以方便能够找到当前这个构造函数。
an168bang521
2015-07-15 00:54:30 +08:00
[问题一] :这个 Foo( ) 函数怎么会存在 prototype 属性的同时,还有一个 Foo prototype 原型对象?
答:这个要从继承方面来理解的,Jquery就是通过这个继承来搞的,而且这么模式有良好的可扩展性;我的理解是prototype存在的意义就是为了原型链继承;传统的单例模式,工厂模式,就不说了,这个你应该理解的;因为工厂模式,得到的数据,都是一样的,不利于保护私有变量;所以引发出了原型链继承这种模式,这种模式,不仅可以有公有的,也有私有的;Foo这个是属于构造函数,Foo相当于自然界中的类;假设f1,和f2是Foo这个构造函数的两个实例;f1和f2会继承Foo本身包含的字符串;这些继承过来的东西都是私有的,虽然f1和f2里面的内容一样,但是他们并不想等;f1.a!==f2.a;举个例子,就好比我们俩都属于人类(人类相当于构造Foo函数),我们俩都继承了人类这个类的特征,我有一个鼻子两只手,你也有一个鼻子两只手;但是我的手不等于你的手;当然f1和f2也要有相同的属性;这个就是prototype出现的原因,这里还有了解一下,每一个对象都有__proto__这个属性,f1和f2是Foo的是例,函数派生自Object,所以函数上也有__proto__的;换句话说,Foo除了有prototype外,还有__proto__这个属性;在f1和f2的实例中查找不到的时候,会通过f1.__proto__来查询。f1.__proto__指向Foo.prototype开辟的那个内存地址;f1.__proto__===Foo.prototype;同样f2__proto__===Foo.prototype;如果在Foo.prototype定义了一个b方法;f1.__proto__.b===f2.__proto__.b;然后再说下查找顺序。这个如果foo.prototype也没有,会一直找的,直到找到Object这个基类上;如果Object的原型也没有。那就报错了。你也可以在控制台输出console.dir(f1),你把f1详细输出看下,就知道他们是怎么回事了;如果想在Foo扩展公有的东西,可以Foo.prototype={ XX:xx,AA:aa,BB:bb}这样写;
但是上面扩展的虽然可以用,但是有一个例外,就是contructor属性不再指向Foo了;这是因为写函数.prototype的时候,本质上重写了默认的prototype对象;因此 constructor属性也就变成了新对象的 constructor属性,指向Object构造函数了,不再指向Foo;原理就不再继续说了,因为我好不容易才扯到第二个constructor上来的,扯多了就跑远了;

[问题] 二:这个 constructor 属性在这里面到底起的是什么作用?
好吧终于从问题一引申到问题二了,这个比较好理解,就是起到当默认指向的改变时候,向上面讲的那个情况下,可以强制写回来;上面的写法可以这样改进:
Foo.prototype={ constructor:Foo,XX:xx,AA:aa,BB:bb}这样就妥妥的了;
ariestiger
2015-07-15 01:23:13 +08:00
有没有兴起了解一下这门语言?http://iolanguage.org/
83f420984
2015-07-15 10:55:46 +08:00
@FrankFang128
@YuJianrong
@Biwood
@an168bang521

谢谢老兄耐心的解答,解决了我心中疑问 :)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/205741

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX