最开始看了MDN的JavaScript指南,没看懂。主要原因是关于构造函数(constructor)、原型(prototype)、对象实例(object,instance)的关系描述太少;直接给我整个原型链让我很困惑。所以靠百度了解一下。我觉得先从这三者的关系入手,再回过头来理解原型链比较容易。相关资料:(强调关系)构造函数、对象实例、原型对象之间的关系(强调原型链)JS着重对JS原型链进行深入理解1.关系:构造函数、原型、对象实例在一个层级中I认为在原型链和层次体系中,最不重要的是对象实例。但是,它可以作为一个切入点,帮助我们访问和探索原型和构造函数。同时,讨论三者的关系,只讨论一个层次内的三者,而忽略了继承和其他层次。1.准备工作:构建原型链和分层“柴犬代码”接下来,使用代码构建Shiba->Dog->Animal原型链,共三层:(我们称这段代码为“ShibaInu代码”,可以先跳过柴犬的代码,看下面构造函数、原型、对象实例相互访问的代码)functionAnimal(){this.type='animal';this.name='default'this.age=0;}动物。prototype.sayHi=function(){console.log('嗨,我是a(n)'+this.type+'。我的名字是'+this.name+'.');}functionDog(){this.type='dog';}Dog.prototype=newAnimal();functionShiba(name='default',age=0){this.type='shiba';this.name=名称;this.age=age;}Shiba.prototype=newDog();2.从对象实例访问原型和构造函数我们声明一个Shiba对象实例gougou,然后通过gougou访问Shiba级别的原型和构造函数:vargougou=newShiba('gou??gou',2);console.log(gougou.__proto__);//原型console.log(gougou.__proto__.constructor);//构造函数console.log(gougou.__proto__.constructor===Shiba);//true这里要说明的问题是:(1)对象实例可以通过__proto__来访问原型。(不要问为什么)(2)原型可以通过构造函数访问构造函数。(因为最后一行的输出为真)2.从构造函数访问原型其实构造函数和原型是可以双向访问的:console.log(Shiba.prototype);//原型console.log(Shiba.prototype===gougou.__proto__);//true这里说明的问题是:构造函数可以通过原型访问原型。(因为第二行的输出为true)3、三者的访问方式总结:(1)对象实例:自己声明的变量名。(2)Prototype:使用对象实例的__proto__属性进行访问;如果知道构造函数名(类名),就可以使用构造函数的原型属性来访问。(3)构造函数:如果是自定义构造函数,知道构造函数的名字;但在很多情况下,它是JavaScript中内置的构造函数,可以通过原型的constructor属性访问。一般来说,如果要访问当前层级的构造函数和原型,以对象实例为入口,先访问原型,再从原型访问构造函数。4、构造函数、原型和对象实例的关系和分工我认为构造函数、原型和对象实例的访问方法已经可以说明它们的关系了。那么,为什么要有这样的结构性分工呢?有什么好处?在功能上:(1)构造函数为核心并起主导作用,持有原型,指导对象实例(new)的生成。(2)一方面,原型帮助构造函数存储方法,供构造函数生成的对象调用,避免为每个对象实例存储一个方法(避免浪费空间);另一方面直接形成原型链,构建层次结构和继承关系。(3)对象实例应该依附于原型链和层次结构。对象实例的属性是在构造函数的指导下生成的;对象实例的方法由构造函数持有的原型提供。构造函数可以产生无限数量的对象实例。2.原型链:将原型串成链并构建关卡1.原型链继承:“显式系列”关卡让我们回顾一下柴犬的代码。代码中已经搭建了Shiba->Dog->Animal的原型链,注意到“串在一起”的两行代码:Dog.prototype=newAnimal();Shiba.prototype=newDog();这里要说明的问题是:将Dog层次结构中的原型指定为Animal层次结构中的对象实例,形成了Dog->Animal的原型链,Dog继承了Animal的属性和方法。(狗对动物说:给你拿来)柴和狗的关系也是一样的。于是构建了Shiba->Dog->Animal的原型链。2、继续往上追:堆__proto__结束。既然当前层级的原型是上一层的对象实例,那么我们就可以循着线索,沿着原型链追溯各个层级,直到null为止。null没有原型,它是原型链的顶端或末端。在柴犬代码中,以对象实例gougou为入口,访问其原型gougou.__proto__,是柴犬级别??的;同时,prototype是Dog层面的一个对象实例,继续通过__proto__属性来访问……所以一层一层往上走。代码如下:console.log('芝级原型:\n',gougou.__proto__);console.log('芝级构造器:\n',gougou.__proto__.constructor);console.log('Dog关卡的原型:\n',gougou.__proto__.__proto__);console.log('Dog关卡的构造函数:\n',gougou.__proto__.__proto__.constructor);console.log('TheAnimal级原型:\n',gougou.__proto__.__proto__.__proto__);console.log('Animal级构造器:\n',gougou.__proto__.__proto__.__proto__.constructor);这里已经有点不清楚有多少__proto__了。无论如何,这里解释的问题是:(1)你确实可以访问原型链上每一层的原型和构造函数。(2)当前层级的原型是更高层级的对象实例(在柴犬代码中用type属性标示,可以区分是Animal对象实例还是Dog对象实例)。3.完整的原型链:回溯到null,注意在Animal层打印的原型直接是一个对象字面量。其实这是Object级别的对象实例。完整的原型链为:Shiba->Dog->Animal->Object->null。不想再从gougou写那么多__proto__了,还有一种方法可以访问Animal级别的prototype:构造函数Animal的prototype属性。回溯代码如下,这次主要验证:console.log(Animal.prototype.__proto__===Object.prototype);//trueconsole.log(Object.prototype.__proto__===null);//这里解释为true问题是:上层的原型是通过Animal.prototype.__proto__得到的,正好等于Object层的原型,也就是说Animal的上层是Object。但是在Objectlevel的最上面没有所谓的level,只剩下一个null。“空层次结构”只有原型null而没有构造函数。结束。最后,分享一下我对JavaScript原型链学习的个人感受:(1)摆脱传统继承中子类继承父类的惯性思维。JavaScript中的构造函数看起来像一个“类名”,但它与继承无关。原型链的继承依赖于原型。(2)去掉“原型”这个词隐含的继承意义。这对我自己来说可能是理所当然的。看到“对象的原型”这几个字,我想当然地认为原型就是对象的“父类”。事实上,原型和对象应该在同一层次上讨论;我们使用原型来在不同的层次上说话。
