JS原型链最上层的js面向对象原型是什么的原型js面向对象原型

Javascript之旅 对象的原型链之由来
字体:[ ] 类型:转载 时间:
本人是Javascript菜鸟,下面是前几天学习Javascript的旅程心得,希望对和我一样的入门者有点用,也希望高手批评指正。
以问题开始: function Base(){}var base = new Base() 上面两行代码会创建几个对象(object)? 要回答这个问题,先明确一下Javascript里object的概念。 Objects 在Javascript里,几乎一切都是object(Arrays、Functions、Numbers、Objects……),而没有C#里的class的概念。object的本质是一个name-value pairs的集合,其中name是string类型的,可以把它叫做“property”,value包括各种objects(string,number,boolean,array,function…),指的是property的值。 typeof 既然object包含Arrays、Functions、Numbers、Objects……,那怎么区分这些呢?答案是typeof。 typeof返回一个字符串,如typeof(Null) = “object”,typeof(false) = “Boolean”, typeof(1) = “number”。既然总是返回字符串,那么对于typeof (typeof x),不管x是什么,总是返回”string”。
Constructor
JS里没有class,也就没有class里的构造函数,那么object是怎么被创建的呢?用构造器:constructor。constructor其实就是Function,因此本身也是object。开头的function Base(){}就是一个构造器,var b = new Base()就是用这个构造器(通过关键字new)创建了一个叫b的object。至此我们可以得出结论,开头的两行代码至少创建了2个object:一个是Base,类型为function的object,一个是base,类型为object的object。
Function()和Object()
这是两个重要的预定义好的构造器。一切function(比如开头的Base())都是由Function()构造出来的;而Object的prototype将会被所有object继承,下面会讲到。
Function的创建过程
当执行function Base(){this.a = 1}时,相当于var Base = new Function(“this.a = 1”),也就是说,这行代码本身,将使用预定义好的Function() constructor,来构造一个function型object(即Base)出来。在这个创建过程中,js将做哪些事呢?
  1,&首先当然会创建一个object起来,Base指向这个object。typeof 这个object = “function”       2,&给Base附上__proto__属性,让它等于Function这个构造器的prototype(也是预定义好的)。这是很重要的一步,也是规律性的一步。(规律:)在执行任意类似varx = new X()时,都会把X的prototype赋给x的__proto__,也就是说,x.__proto__和X.prototype此时会指向同一个对象。
       3,&为Base创建call属性,该属性是个function。因此我们可以这样写:Base.Call()
       4,&为Base创建Construct属性,该属性也是个function。在执行var base = new Base()时,即会调用这个Construct属性。  5,&为Base创建Scope,Length等属性,略。  6, 为Base创建prototype属性:先用new Object()创建一个对象,为这个对象创建一个属性叫constructor,该属性值设置为Base。再把Base的prototype设置为这个新创建的对象。伪代码如下:
var x = new Object();x.constructor = BBase.prototype =
先把关注点放到2和6。
__proto__和prototype
从2可以看出来,任意一个用构造器构造出来的object(包括Objects和Functions),都会有__proto__属性,指向该构造器的prototype属性。注意__proto__是个私有属性,在IE上是看不到的,我用的是chrome,可以看到。
从6可以看出,任意一个用new Function()构造出来的functions,都会有prototype属性,该属性是用new Object()构建出来的,初始公开属性只有一个constructor。
再来分析下第6步的伪代码,也就是为function创建prototype的这一步:
var x = new Object(); // 参见2中的规律,会有x.__proto__= Object.prototype。x.constructor = BBase.prototype =
此时我们用Base()构造一个对象出来:
var base= new Base(); // 参见2中的规律,会有base.__proto__ = Base.prototype,也就是 = x。& // 因此有base.__proto__.__proto__ = x.__proto__ // 而x.__proto__ = Object.prototype(见上一个代码片段)   // 所以,base.__proto__.__proto__ = Object.prototype.
__proto__.__proto__,这就是传说中JS对象的原型链!由于用Function()创建构造器时的关键的第6步,保证了所有object的原型链的顶端,最终都指向了Object.prototype。
Property Lookup
而我们如果要读某个object的某个属性,JS会怎么做呢?
比如有个object叫xxx,我们执行alert(xxx.a),也就是读取xxx的a属性,那么JS首先会到xxx本身去找a属性,如果没找到,则到xxx.__proto__里去找a属性,由此沿着原型链往上,找到即返回(没找到,则返回undefined)。可以来看个例子:
上图得知:base本身是没有constructor属性的,但是base.constructor确实能返回Base这个函数,原因就在于base.__proto__有这个属性。(base.__proto__是啥?就是Base.prototype,上面构建Function的第6步的伪代码里,为Base.prototype.constructor赋值为Base本身。)
Object作为“基类”
另外,由于任意object的原型链的顶端都是Object.prototype。所以,Object.prototype里定义的属性,就会通过原型链,被所有的object继承下来。这样,预定义好的Object,就成了所有对象的“基类”。这就是原型链的继承。
看上图,Object.prototype已经预定义好了一些属性,我们再追加一条属性叫propertyA,那么这个属性和预定义属性一样,都可以从base上读到。
已经得知,
对于 var xxx =new Object(); 有xxx.__proto__= Object.
对于 var xxx =new Base(); 有xxx.__proto__.__proto__= Object.
看上去很像什么呢?从c#角度看,很像Base是Object的子类,也就是说,由Base()构造出来的object,比由Object()构造出来的object,在原型链上更低一个层级。这是通过把Base.prototype指向由Object()创建的对象来做到的。那么自然而然,如果我想定义一个继承自Base的构造器,只需把改构造器的prototype指向一个Base()构造出来的对象。
function Derived(){}var base = new Base();Derived.prototype =var d = newDerived(); //很容易推算出:d.__proto__.__proto__.__proto__ = Object.prototype.
推算过程:d.__proto__指向Derived.prototype,也就是base;则__proto__.__proto__指向base.__proto__,也就是Base.prototype,也就是某个new object()创建出来的东东,假设是o;则__proto__.__proto__.__proto__指向o.__proto__,也就是Object.prototype。
回答开头的问题,以及几个新的问题
那两行代码至少创建了三个对象:Base、base、Base.prototype。顺便说说,base是没有prototype属性的,只有function类型的object,在被构建时才会被创建prototype属性。
d.constructor会返回什么呢?
构造器Base()和Derived()里都是空的,如果有代码,将会怎么被执行呢?
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具1.你了解对象吗?
本文结合基本javascript的权威书籍中的内容,根据自己的理解,通过相关示例向大家展示了对象,原型及原型链的基本内容,希望大家能够喜欢,如有不足,也希望提出建议,大家共同进步。
虽然说在javascript中所有一切都是对象,这不假,但是对象和对象还是不一样的,产生对象的方法有这几种。
1)直接通过new Object生成&名正言顺&的对象实例。这个对象实例将继承所有来自Object
对象的属性和方法,其中Object.prototype是最顶端的原型对象。
2)通过编写构造函数,然后再new一个自定义函数。这个也不难理解,系统给的毕竟有些属性和方法不是符合程序员的心意,所以会自定义一个函数,其声明方法就和声明一个函数一样。
function Foo(){
this.i =0;
}//这个函数现在可以称作构造函数了吗?其实不然,判断是构造函数还是普通函数,不能只看函数名的首字母是否大写,而是要看调用的时候,是那种方式调用,如果是以new的方式,那才是构造函数,这样才可以拿着这个构造函数愉快的创建以它为模板的对象实例。方法和用new
Object一样,var foo = new Foo();
这样一个构造函数就变成了对象模板。
这就是那句老话,js里所有都是对象,函数是对象,对象是对象,而js又是函数编程,基本脱离不了函数(对象)。
有了这个基础,我们再来看看js这个活雷锋帮我们内置了什么功能强大的东东吧!
js有很多内置构造函数,他将常用的方法属性封装好放入构造函数里,这个构造函数都是可以直接调用,例如Math,他是一个包含很多数学方法的构造函数,使用的时候可以Math.ceil(6.3)//向上取整,
var tt = new Date();//取当前系统时间。原生函数的行为是ECMAScript标准规定的(函数对象的[[Construct]]),由JS引擎负责实现标准。同一原生的构造函数,是否通过
new关键字调用可能具有不同的行为(如Date、String、Boolean、Number函数),也可能具有相同行为(如Object、
RegExp、Array)。有的原生构造函数必须使用new关键字(如Promise、Proxy、Set、Map、ArrayBuffer),也有的
一定不能使用new关键字(如Symbol)
2.原型的理解
首先,最重要的就是要知道,原型也是一个对象,因为在JS中万物皆对象。很多人对这句话不是很能理解,我是这样的理解的,在JS中任何方法的原型都最终都可以指向对象的原型,这也是很重要的原型链的问题。说到原型,我们要扯一下为什么要有原型这个东西,俗话说的好,所有的事物不会凭空产生。原型诞生的意义就是可以实现代码复用。
我们平时生成的对象基本都是通过构造函数实现的,既然是通过构造函数实现,那么就
function Fn(){
this.a=function(){
return 15;
var fn1 = new
var fn2 = new
好,我们已经通过Fn()
这个构造函数生成了两个对象实例,不用猜每个对象实例中都有一套构造函数里的内容,如果fn1
不需要这些属性呢?这是什么?浪费内存啊,这是在作死啊!
我们每new一个Fn就相当于说你在内存中又开辟了一块地方存储了Fn里的所有属性和方法。所以呢,原型应运而生。
原型出现的目的就是把需要共享的属性方法放到一个对象里,这个对象就是原型(对象)。
既然我们知道了原型的来历和本质,我们再来探究一下原型和构造器的联系。
我们通过prototype属性可以为任意一个构造函数添加原型属性和方法:
& &this.i = 1;
Foo2.prototype.m =
0;//prototype属性就是一个可以让构造函数往原型中添加属性方法的方法。
var foo2 = new
这样就把m属性加入到构造函数Foo2的原型中去。
m并没有在Foo中定义,但是通过new关键字生成的foo2仍然可以访问m,充分说明有一个机制让实例去在构造函数之外继续搜索m这个变量,而这个函数
之外的区域就是原型对象。机制:当实例想要访问一个变量的时候,他会首先搜索本身是否有,没有就去搜索他的原型对象,如果还没有呢?继续向上,搜索原型链的
上游,直到找到原型链的最顶端,如果还没有,就返回undefined
这个图就充分说明了变量的搜索机制。
3.ECMAscript中的内置构造函数的本质是什么?
内置的构造函数例如(Array Date
String等等),他其实和普通的构造函数一样,也是内部通过function
Array(){}生成的。那他本质上也是一个函数。
4.原型链是什么?
&对于那些熟悉基于类的面向对象语言(Java 或者 C++)的开发者来说,JavaScript 的语法是比较怪异的,这是由于 JavaScript 是一门动态语言,而且它没有类的概念(&ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型)。
涉及到继承这一块,Javascript 只有一种结构,那就是:对象。在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。
5.构造函数和原型的关系?
构造函数里有一个属性prototype,这个属性里面存储着它原型对象的指针。这样构造函数就能找到原型对象。反过来,原型对象也有一个属性,constructor。这个属性指向构造函数的指针。这样就可以互相&认识&。
6.通过构造函数生成的实例对象与原型的关系?
在JS中任何方法(函数)都有一个__proto__属性,它指向构造它的函数的原型对象,也就是:实例.__proto__===function.prototype。
7.__proto__是什么?
在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。__proto__和prototype之间的关系或区别是什么,如下图所示?
(Function)方法这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性&&原型属性(prototype),
这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做
constructor,这个属性包含了一个指针,指回原构造函数。&
下面是另一种写法
上图中的知识点:
1.构造函数Foo()构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。
2.原型对象Foo.prototypeFoo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。
3.实例f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,
这样子就可以像上面1所说的访问原型对象的所有方法啦。另外:构造函数Foo()除了是方法,也是对象,它也有__proto__属性,指向谁呢?指向它的构造函数的原型对象。函数的构造函数不就是Function吗,因此这里的__proto__指向了Function.prototype。其实除
了Foo(),Function(),
Object()也是一样的道理。原型对象也是对象,它的__proto__属性,又指向谁呢?同理,指向它的构造函数的原型对象。最终都指向Object.prototype也是原型链的最顶端.最后,Object.prototype的__proto__属性指向null。
1.对象都有属性__proto__,指向该对象的构造函数的原型对象。
2.方法(函数)除了有属性__proto__,还有属性prototype,prototype指向该方法(函数)的原型对象。
3.任何原型对象都有个constructors属性,constructor指向该方法(函数)本身。
4.基本类型所拥有的属性和方法,都是继承自Date、String、Boolean、Number函数,它们又继承自Function。
5.__proto__是站在对象的角度讨论原型对象。
6.prototype是站在构造函数的角度讨论原型属性,或构造函数创建的对象的原型对象。
阅读(...) 评论()查看: 12682|回复: 0
一篇文章教你搞懂javaScript原型链
一篇文章教你搞懂javaScript原型链,有需要的朋友可以参考下。
JavaScript是被称为零入门的语言,但凡学习过编程的同学,入门起JavaScript这门语言都会觉得特别容易,但是深入之后也会有一些很基本却很容易被忽略的点,这篇文章教你重新认识JavaScript。
首先明确一点,JavaScript并不是一门完全面向对象的语言,这也是老生常谈的一个问题,这里就不多讲。只说一点我的理解,既然不是一门完全面向对象的语言,那么它肯定会有面向对象语言的特性,也有非面向对象语言的特性。而针对原型链,我们暂时将它认为是一门完全面向对象的语言。
完全面向对象的语言,经典代表是java,我们经常说中,万物万事皆对象。那么对于JavaScript,我们如果认为它是完全面向对象的,也可以说它万物万事皆对象,那么首先搞清楚最基本的对象。
最基本的对象
var obj = Object.create(null);复制代码
这个obj是一个最纯净的对象,它内部什么属性都没有。
请注意!请注意!请注意!(重要的话说三遍),这里的obj与下面这种写法初始化后的对象不同。
= {};复制代码
这样写法的得出的obj内部并不是空的。在js中,为了简化语法 这种写法其实是下面这种写法的简写:var obj1 = new Object();复制代码
两种写法区别主要在于obj内部没有任何属性和方法,obj1内部有一个属性__proto__指向一个Object定义的对象,这个之后再谈。
最基本的传递(Delegation)
所谓一生二,二生三,三生万物,有了这个最基本的 obj 我们就用它来构造整个JavaScript对象模型。首先是描述最基本的属性传递规则,也就是原型链的传递规则。Delegation是指当从一个对象中获取一个属性失败时,JS会自动再尝试去其__proto__链接指向的对象中去查找,如果还找不到,就沿着__proto__一路找上去,直至找到null。这个规则告诉我们一个很重要的事情,就是每个对象都必须有一个属性叫__proto__!按照这个规则,我们的最基本的对象可以描述成如下样子:{
__proto__:undefined}复制代码
还是请大家揣摩这个对象。
构造一个新对象 同时保留老对象的所有特点
有了上面那条最基本的规则,我们现在来构造一个新的对象,并且这个心对象有老对象的所有特性,基本的算法应该这么描述:
1.copy最原始对象得到一个副本
2.再次copy原始对象得到另一个副本
3.在第二个副本中添加新的属性或者方法
4.将第二个副本中__proto__指向第一个副本
更一般的,假设我们不是通过最原始的对象来构造一个新对象,则算法变为
1.copy父级对象得到一个副本
2.再copy原始对象得到另一个原始对象副本
3.在原始对象副本中添加新的属性或者方法
4.将原始对象副本中__proto__指向父对象副本
以上就是构造出一个新对象的过程,并且是继承的过程。这个过程用代码描述 假设a是已有的对象,现在要构造新对象b。var copyA = copy(a);
var b = copy({});
b.newProperty = &我是新的&;
b.__proto__ = copyA;
//大家体会这个过程。复制代码
大家对js的function应该不陌生,因为刚入门的时候我们就会使用它来封装我们的程序代码模块等等。JavaScript中的function 和 new关键字
其实function也是一个对象,但不同于上文中的那种对象(我们称上文中的对象为普通对象)。
function 这种对象是可以被new关键字激活并且返回一个普通对象,也就是说function是一种可以生产对象的对象,我们一般把这种对象叫做类,或者叫做类类型。
这种类类型有一个属性叫prototype,这个属性指向一个普通对象。而new关键字的作用就是
1.创造一个纯净的对象 obj,
2.执行fun.apply(obj,arguments);
3.将新对象obj的__proto__指向 fun的prototype
4.返回此对象obj
举例说明:var fun = function(){
this.aaa=&222&;
};fun.prototype={
property1:&111&}var funObj = new fun();//funObj 对象包含 //{//
aaa:'222',//
__proto__:{//
property1:&111&
}//}复制代码
而当调用funObj.property1时 按照delegation原则会找到value "111"
由上推断,js中普通对象和function使用new关键字联系在一起,普通对象的__proto__和function的prototype关系密切,其他更深层次的联系读者自己体会。
还有一点需要指明,当function内部有返回值时,new关键字会失效。
最基本的类类型
JavaScript中内置了一些类类型,上文中所说的纯净的对象在js中是不存在的,因为js在出生的那一刻就给我们提供了一些基类的类型,注意这里说的是类类型,也就是说接下来说的几种都是function
Object 是js中生产基础对象的类类型,使用new关键字创建的Object对象是js中最简单最基础的对象,是js中一切对象的源,表现为Object的对象的__proto__的__proto__指向null。注意这里用了两个__proto__,分析一下原因:var obj = new Object();//分析一下new的过程//
obj=Object.create(null);
创造一个纯净对象//
obj.__proto__ = Object.//
我们知道Object的prototype为 &{ __proto__:undefined,... }&//
所以最终我们得到的对象obj.__proto__.__proto__指向undefined复制代码
2.Function
Function 是js中生产function的类类型,相比Object,Function似乎更难理解一点,因为Function本身是一个function,同时它的prototype也是一个function,当用new关键字激活Function时,生产的对象也是一个function,但生成的这个funtion的prototype不再是function而是Object对象。
这点和上文中的规则有点相悖,但其实并不是相悖的。下面举例来说明Function内部大概的思路:
如下的实现模仿Function是错误的var fun1 = function(){}fun1.prototype=function(){}var fun2 = new fun1();复制代码
fun1的prototype是function(){} 此时用new关键字激活fun1返回一个对象,此时fun2的type应该是object而并非一个function,这是因为fun1并没有返回值,此时new关键字激活后会将生产的新对象返回,而这个对象是由fun1构造的普通对象,其实是Object派生出来的一个对象。
如下的实现模仿Function是类似的var fun11 = function(){
&span style=&white-space:pre&& &/span&return function(){}&span style=&white-space:pre&& &/span&}
fun11.prototype=function(){}
var fun12 = new fun11();复制代码
此时fun12是一个function,也就是一个类类型,可以继续使用new关键字激活,但是要注意的是,这个fun12和fun11没有继承派生关系,fun12指向的是fun11内部生成的一个function对象,此时的fun11就类似于一个生产function的工厂,作用就和Function类似了。
可以看出来,此时的new关键字已经失效了,因为不使用new关键字得到的fun12也是一样的。这点是js函数式的特点,返回值可以是函数。
再来看原型链继承
根据javascript delegation的特性,再去理解一般的继承写法就不难理解了。var fun1 = function(){
this.a=234}fun1.prototype = {
b:321} var fun2 = function(){
this.c=111;}fun2.prototype=new fun1();var obj1 = new fun1();var obj2 = new fun2(); //解析obj1内部结构如下// obj1 = {//
__proto__:{//
}//}//obj1.a==234//obj1.b==321 //解析obj2内部结构如下//obj2={
__proto__:{//
__proto__:{//
}//}//obj2.a==234//obj2.b==321//obj2.c==111复制代码
这是一般的继承写法,当然这表面上看似乎没有问题,其实却有一点问题,这里简单说明一下
还是以上的写法var fun1 = function(){
this.a=234}fun1.prototype = {
b:321} var fun2 = function(){
this.c=111;}fun2.prototype=new fun1(); var obj1 = new fun1();var obj2 = new fun2(); //此时改变一下fun1的prototypefun1.prototype.b=322;//再去看一下obj2的值//obj2.b==322 !!!复制代码
以上可以看出,obj2已经生成,按照一般的逻辑,此时改变类本身,不应该影响obj2,但显然现在不是这种情况。所以应该做如下处理:var fun1 = function(){
this.a=234}fun1.prototype = {
b:321} var fun2 = function(){
this.c=111;} function copy(obj){
var objnew = {};
for(var key in obj){
if(typeof obj[key] == &object&){
objnew[key]=copy(obj[key]);
objnew[key]=obj[key];
}}//这里多了一个copy方法fun2.prototype=copy(new fun1()); var obj1 = new fun1();var obj2 = new fun2();复制代码
但是这样问题就结束了么?当然没有,现在会出现一个新的问题,不太好描述,直接看下面的代码:var fun1 = function(){
this.a=234}fun1.prototype = {
b:321} fun1.prototype.b=fun1.//这里真的是神来之笔啊!!!!
var fun2 = function(){
this.c=111;} function copy(obj){
var objnew = {};
for(var key in obj){
if(typeof obj[key] == &object&){
objnew[key]=copy(obj[key]);
objnew[key]=obj[key];
}}//因为fun1的变态的prototype结构 下面的函数会递归调用直到栈溢出,直接崩掉了!fun2.prototype=copy(new fun1()); var obj1 = new fun1();var obj2 = new fun2();复制代码
这个问题似乎比刚才那个问题更严峻,直接屏蔽掉一种prototype结构的合法性。
由此可以看出单纯从代码规则上不能屏蔽掉这个bug问题,所以我们必须遵循一个比较严格的代码规范,即:
类的定义必须在实例化对象之前,对象实例化之后禁止修改类本身。这个规范并非说代码层次的强制禁止,只是告诉大家如果不遵循这个规范有可能会埋下不小的bug风险,其危害也是显而易见的。除非你有特别的产品需求需要这样做,否则建议千万别这么做。
这个问题的出现归根结底是因为js是解释型语言,在任何时候都可以对类本身做出调整,也没有内置的访问权限控制,即使可以使用闭包伪造私有域,但无论如何都不能杜绝对一个类的prototype的访问。
无论如何刚才的继承写法都有点太low的感觉,要直接控制子类的prototype,如果有很多很多类,出了问题排查都不容易!有更好的写法么?
接下来的文章会提供一套js继承系统,探究一下js框架代码中,是如何处理继承这个问题的。敬请期待!
转载请注明出处:http://gagalulu.wang/blog/detail?id=7您的支持是我最大的动力!
上一篇:下一篇:javascript 原型、原型链、对象复制等原理和示例分析(下)
原型是 Script 面向对象特性中重要的概念,也是大家太熟悉的概念。因为在绝大多
数的面向对象语言中,对象是基于类的(例如 Java 和 C++ ) ,对象是类实例化的结果。而在
语言中,没有类的概念
① ,对象由对象实例化。打个比方来说,基于类的语言中类
就像一个模具,对象由这个模具浇注产生,而基于原型的语言中,原型就好像是一件艺术品
的原件,我们通过一台 100% 精确的机器把这个原件复制出很多份。
前面小节的例子中都没有涉及原型,仅仅通过构造函数和 new 语句生成类,让我们看
看如何使用原型和构造函数共同生成对象。
function Person() {}
Person.prototype.name = &BYVoid&;
Person.prototype.showName = function() {
console.log(this.name);
var person = new Person();
person.showName();
上面这段代码使用了原型而不是构造函数初始化对象。 这样做与直接在构造函数内定义
属性有什么不同呢?
**构造函数内定义的属性继承方式与原型不同, 子对象需要显式调用父对象才能继承构
造函数内定义的属性。
构造函数内定义的任何属性, 包括函数在内都会被重复创建, 同一个构造函数产生的
两个对象不共享实例。**
构造函数内定义的函数有运行时闭包的开销, 因为构造函数内的局部变量对其中定义
的函数来说也是可见的。
下面这段代码可以验证以上问题:
function Foo() {
var innerVar = &hello&;
this.prop1 = &BYVoid&;
this.func1 = function() {
innerVar = &;
Foo.prototype.prop2 = &Carbo&;
Foo.prototype.func2 = function() {
console.log(this.prop2);
var foo1 = new Foo();
var foo2 = new Foo();
console.log(foo1.func1 == foo2.func1); // 输出 false
console.log(foo1.func2 == foo2.func2); // 输出 true
尽管如此,并不是说在构造函数内创建属性不好,而是两者各有适合的范围。那么我们
什么时候使用原型,什么时候使用构造函数内定义来创建属性呢?
**除非必须用构造函数闭包,否则尽量用原型定义成员函数,因为这样可以减少开销。
尽量在构造函数内定义一般成员, 尤其是对象或数组, 因为用原型定义的成员是多个
实例共享的。**
接下来,我们介绍一下JavaScript中的原型链机制。
JavaScript 中有两个特殊的对象: Object 与 Function ,它们都是构造函数,用于生
成对象。 Object.prototype 是所有对象的祖先, Function.prototype 是所有函数的原
型,包括构造函数。我把 JavaScript 中的对象分为三类,
一类是用户创建的对象,
一类是构造函数对象,
一类是原型对象。
用户创建的对象,即一般意义上用 new 语句显式构造的对象。
构造函数对象指的是普通的构造函数,即通过 new 调用生成普通对象的函数。
原型对象特指构造函数 prototype 属性指向的对象。
这三类对象中每一类都有一个 proto 属性,它指向该对象的原型,从任何对象沿着它开始遍历都可以追溯到 Object.prototype 。
构造函数对象有 prototype 属性,指向一个原型对象,通过该构造函数创建对象时,被创建对象的 proto 属性将会指向构造函数的 prototype 属性。
原型对象有 constructor属性,指向它对应的构造函数。让我们通过下面这个例子来理解原型:
function Foo() {}
Object.prototype.name = &My Object&;
Foo.prototype.name = &Bar&;
var obj = new Object();
var foo = new Foo();
console.log(obj.name); // 输出 My Object
console.log(foo.name); // 输出 Bar
console.log(foo.proto.name); // 输出 Bar
console.log(foo.proto.proto.name); // 输出 My Object
console.log(foo.proto.constructor.prototype.name); // 输出 Bar
我们定义了一个叫做 Foo () 的构造函数,生成了对象 foo 。同时我们还分别给 Object和 Foo 生成原型对象。
下图解析了它们之间错综复杂的关系。
对象的复制
JavaScript 和 Java 一样都没有像C语言中一样的指针,所有对象类型的变量都是指向对
象的引用,两个变量之间赋值传递一个对象并不会对这个对象进行复制,而只是传递引用。
有些时候我们需要完整地复制一个对象,这该如何做呢? Java 语言中有 clone 方法可以实
现对象复制,但 JavaScript 中没有这样的函数。因此我们需要手动实现这样一个函数,一个
简单的做法是复制对象的所有属性:vcD4NCjxwPk9iamVjdC5wcm90b3R5cGUuY2xvbmUgPSBmdW5jdGlvbigpIHs8YnIgLz4NCnZhciBuZXdPYmogPSB7fTs8YnIgLz4NCmZvciAodmFyIGkgaW4gdGhpcykgezxiciAvPg0KbmV3T2JqW2ldID0gdGhpc1tpXTs8YnIgLz4NCn08YnIgLz4NCnJldHVybiBuZXdPYmo7PGJyIC8+DQp9PGJyIC8+DQp2YXIgb2JqID0gezxiciAvPg0KbmFtZTogJmxzcXVvO2J5dm9pZCZyc3F1bzssPGJyIC8+DQpsaWtlczogWyZsc3F1bztub2RlJnJzcXVvO108YnIgLz4NCn07PGJyIC8+DQp2YXIgbmV3T2JqID0gb2JqLmNsb25lKCk7PGJyIC8+DQpvYmoubGlrZXMucHVzaCgmbHNxdW87cHl0aG9uJnJzcXVvOyk7PGJyIC8+DQpjb25zb2xlLmxvZyhvYmoubGlrZXMpOyAvLyDK5LP2IFsgJmxzcXVvO25vZGUmcnNxdW87LCAmbHNxdW87cHl0aG9uJnJzcXVvOyBdPGJyIC8+DQpjb25zb2xlLmxvZyhuZXdPYmoubGlrZXMpOyAvLyDK5LP2IFsgJmxzcXVvO25vZGUmcnNxdW87LCAmbHNxdW87cHl0aG9uJnJzcXVvOyBdPC9wPg0KPHA+yc/D5rXEtPrC68rH0ru49rbUz/PHs7+9sbSjqHNoYWxsb3cgY29weaOptcTKtc/Wo6y8tNa7uLTWxrv5sb7A4NDNtcTK9NDUo6y2+DxiciAvPg0KubLP7bbUz/PA4NDNtcTK9NDUoaMgx7O/vbG0tcTOysziysc8c3Ryb25nPsG9uPa21M/zubLP7bbUz/PA4NDNtcTK9NDUPC9zdHJvbmc+o6wgwP3I58nPwP3W0CBsaWtlcyDK9DxiciAvPg0K0NTWuM/ytcTKx82s0ru49sr91+mhozxiciAvPg0KyrXP1tK7uPbN6sirtcS4tNbGo6y78snuv72xtKOoZGVlcCBjb3B5o6myorK7ysfSu7z+yN3S17XEysKjrNLyzqqz/cHLu/mxvsr9vt08YnIgLz4NCsDg0M2jrLu509C24NbWsrvNrLXEttTP86OsttTP88Tasr+7udPQuLTU07XEveG5uaOs0vK0y9Do0qrTw7Xduem1xLe9yr3AtMq1z9ajujwvcD4NCjxwPk9iamVjdC5wcm90b3R5cGUuY2xvbmUgPSBmdW5jdGlvbigpIHs8YnIgLz4NCnZhciBuZXdPYmogPSB7fTs8YnIgLz4NCmZvciAodmFyIGkgaW4gdGhpcykgezxiciAvPg0KaWYgKHR5cGVvZih0aGlzW2ldKSA9PSAmbHNxdW87b2JqZWN0JnJzcXVvOyA="| typeof(this[i]) == &function&) {
newObj[i] = this[i].clone();
newObj[i] = this[i];
return newO
Array.prototype.clone = function() {
var newArray = [];
for (var i = 0; i & this. i++) {
if (typeof(this[i]) == &object& || typeof(this[i]) == &function&) {
newArray[i] = this[i].clone();
newArray[i] = this[i];
return newA
Function.prototype.clone = function() {
var that =
var newFunc = function() {
return that.apply(this, arguments);
for (var i in this) {
newFunc[i] = this[i];
return newF
var obj = {
name: &byvoid&,
likes: [&node&],
display: function() {
console.log(this.name);
var newObj = obj.clone();
newObj.likes.push(&python&);
console.log(obj.likes); // 输出 [ &node& ]
console.log(newObj.likes); // 输出 [ &node&, &python& ]
console.log(newObj.display == obj.display); // 输出 false
上面这个实现看起来很完美,它不仅递归地复制了对象复杂的结构,还实现了函数的深
拷贝。这个方法在大多数情况下都很好用,但有一种情况它却无能为力,例如下面的代码:
var obj1 = {
var obj2 = {
obj1.ref = obj2;
这段代码的逻辑非常简单,就是两个相互引用的对象。当我们试图使用深拷贝来复制
obj1 和 obj2 中的任何一个时,问题就出现了。因为深拷贝的做法是遇到对象就进行递归
复制,那么结果只能无限循环下去。对于这种情况,简单的递归已经无法解决,必须设计一
套**图论算法, 分析对象之间的依赖关系, 建立一个拓扑结构图, 然后分别依次复制每个顶点,
并重新构建它们之间的依赖关系**。
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'

我要回帖

更多关于 js原型对象 的文章

 

随机推荐