摘要:的隐式原型是母,母是由构造函数构造的,但函数的隐式原型又是。。。。可能是考虑到它也是由构造函数生成的吧,所以返回的值也是。
首先,我们暂且把object类型和function类型分开来,因为 function是一个特殊的对象类型,我们这里这是便于区分,把function类型多带带拿出来。顺便一提,typeof也是多带带把function设为一种类型,我们可以用typeof来验证我们的猜想。
从上图中可以看到,所有的橘色箭头(constructor)都指向function Function(){},所有的黑色箭头(__proto__)开始有两条路可以走,但是最后都汇聚在了一起,指向null。而天蓝色箭头(prototype)不具有连续性,一般都是一个function指向一个object。然后我们可以得出以下结论:
所有对象的构造函数(constructor)最终指向function Function( ){ }这个函数,包括他自己。ps:为了方便,我们称它为母Function。
所有对象的__proto__最终指向null,即一切原型链的末端就是null。
所谓的一切源于对象,这里的”对象”指的是Object{}(红色虚线框内,也就是Object的prototype)。ps:为了方便,我们在这里叫它母Object。
除了Object的原型(prototype)直接指向母Object,其他所有类型的(构造器的)原型(prototype)都先指向一个由母Object派生出来的一个子Object,再指向母Object,Function的原型为function{}。ps:构造器,即构造函数。
哲学部分:这幅图包含两个”先有鸡还是先有蛋”的问题:
function(){}是由母Function构造的,但它同时又是母Function的原型。。
function(){}的隐式原型(__proto__)是母Object,母Object是由构造函数 function Object(){}构造的,但函数function Object(){}的隐式原型又是function (){}。。。。
当然除了”先有鸡还是先有蛋”,原型链中还有一个最最有哲理的问题:
构造函数function Function(){}的构造函数就是他自己,就是说,他自己把自己生下来了。。。。。 = =
至于为什么function(){}有俩个框框,其实是因为它是原型中最特殊的。它是原型中唯一一个的function类型的,其他的原型都是object类型。可能是考虑到它也是由构造函数Function生成的吧,所以typeof返回的值也是function。
我们再来说一下可能大家都很疑惑的问题,就是原型(prototype)和隐式原型(__proto__)的区别。
我说一下我的见解。相对于”原型”,我觉得”__proto__”更适合叫做父对象,因为在原型链中,负责连接各个对象的,就是”__proto__”。也就是说,我改写对象的”prototype”,并不会影响对象在原型链中的位置,想脱离或者改变原型链,只能是改写”__proto__”。
在原型链中,对象和它的”__proto__”指向的对象的关系,更像是别的语言中的子类和父类。子类会继承父类的方法和属性,而且会和父类保持一样的类型。
一般来说,”__proto__”都是指向某一个对象的”prototype”属性,所以对象会继承的也就是其父对象”prototype”中的属性和方法,但是并不会继承其父对象自身的属性方法。说的可能有点绕,我们举个栗子:
//以Object为原型创建一个对象,obj。 var obj=new Object /*这句话可以翻译为: var obj={} obj.__proto__=Object.prototype */
这个时候,obj的”__proto__”指向的是Object.prototype,所以obj的父对象是Object.prototype,obj会继承Object.prototype中的属性方法,但是并不会继承Object中的属性和方法。这时候我们用obj.toString()是可以的,但是用obj.length就会报错。这是因为toString()是写在Object.prototype里面的,而length是写在Object里面的。
在学习运算符的时候,相比很多初学者也会有和我一样的疑问,为什么instanceof向上查找会在prototype里查找,而不在”父对象”中查找。理解我上面所说,大家相比也会明白了吧。就拿上面的例子来说,其实Object只是obj的构造器,构造函数而已,obj真正的父对象是Object.prototype。
那讲到这里有的小伙伴就会有疑问了,那我要是想继承Object里面的属性怎么办?其实也很简单,设置obj的”__proto__”指向Object就可以了。不过有一点,”__proto__”属性毕竟是底杠开头,是官方不想暴露在外面的属性,能不用的时候最好不要用。其实就算不用”__proto__”也是可以达到想要的效果的。Object里面有一个可以创建对象的方法create,用它我们可以轻松达到我们的要求。举个栗子:
//以Object为原型创建一个对象,obj。 var obj= Object.create(Object) /*这句话可以翻译为: var obj={} obj.__proto__=Object 再说一下create的用法: Object.create(prototype,[{code}]);//返回一个对象 可以看到,这里有两个参数,第一个参数prototype相当于创建对象的__proto__,值得话随便一个对象就可以了,第二个参数可以不填,里面详细写创建对象的一些属性和他们的属性标签。注意,create创建的是一个对象,和new一样,无论以什么为父对象,返回值都是object。 */
再来说说prototype,通过上图大家会发现,prototype属性只是一个函数和一个对象之间的一个桥梁,在这里为什么要说是桥梁呢,为什么不说是函数的一个属性呢?我曾写过一个例子:
function fa(){} fa.prototype.fname=’fa’ var ch=new fa() ch.fname //’fa’ fa.prototype={fname:’newfa’} //改写prototype ch.fname //依旧是’fa’, ch.hasOwnProporty(‘fname’) //false ch.__proto__==fa.prototype //false
通过上述代码大家可以看出来了吧,ch继承fa时只是让ch.__proto__指向了fa.prototype指向的地址,而不是指向fa.prototype。所以就导致了改写fa.prototype并不会影响ch.fname的值。改写后ch.__proto__不等于fa.prototype了,也就是说ch和fa已经半毛钱关系都没有了。
基本上到这里,大家都会对原型链有一定的认识了,至于对象中的特殊存在——function,大家可以自己去探索一下。以上都是自己对于JS中原型链的认识,如有错误欢迎大家指正~
ps:sf的编排好难用 !
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79872.html
摘要:构造函数的属性指向原型对象原型对象的属性指向构造函数实例对象的指向原型对象所有引用类型默认都继承了,而这个继承也是通过原型链实现的。第一种方式是使用操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回。 理解对象 首先对象的定义是:无序属性的集合,其属性可以包含基本值、对象或者函数。严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名...
摘要:对应的关系图如下讲解了构造函数和原型对象之间的关系,那么实例对象和原型对象之间的关系又是怎么样的呢下面讲解。原型对象的指向的是构造函数和本身没有属性,但是其原型对象有该属性,因此也能获取到构造函数。 JavaScript进阶 - 1. 原型和原型链的概念 我们好多经常会被问道JavaScript原型和原型链的概念,还有关于继承,new操作符相关的概念。本文就专门整理了原型和原型链的概念...
摘要:在这个情况下我们可能需要使用构造函数,其以指定的模式来创造对象。构造函数也有自己的,值为,也通过其属性关联到。从逻辑上来说,这是以栈的形式实现的,它叫作执行上下文栈。 原文:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/ 对象 原型链 构造函数 执行上下文栈 执行上下文 变量对象 活动对象 作用域链 闭包 Thi...
阅读 799·2021-10-13 09:39
阅读 3674·2021-10-12 10:12
阅读 1705·2021-08-13 15:07
阅读 985·2019-08-29 15:31
阅读 2864·2019-08-26 13:25
阅读 1755·2019-08-23 18:38
阅读 1859·2019-08-23 18:25
阅读 1844·2019-08-23 17:20