深度理解原型和原型链(看完不懂来打我)

作者&投稿:保功 (若有异议请与网页底部的电邮联系)
~ Object.prototype.type="x";Function.prototype.type="y";functionA(){};consta=newA();console.log(A.type);console.log(a.type);

一道很常见的原型链的面试题,如果你知其然并且知其所以然,那我想你可以不用往下看了,如果你只是知其然却不知其所以然,或者其然都不知,那你就继续往下看,也许看完之后,你可以知其然并且知其所以然。

理解原型对象

首先我们来了解一下原型对象到底是什么?有什么用?

我们创建的每个构造函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象就是原型对象,原型对象中包含所有通过调用构造函数生成的实例对象所共享的属性和方法。很明显,使用原型对象的好处就是可以让所有实例对象共享他所包含的属性和方法。

functionPerson(name){this.name=name}Person.prototype.sayName=function(){console.log(this.name)}constperson1=newPerson('Npz')person1.sayName()//Npz

这里面主要有三个主体,构造函数,原型对象,实例对象。实例对象是通过调用构造函数生成的,然后实例对象调用了原型对象中的方法。那么这三者具体的关系是什么呢?

构造函数、原型对象、实例对象之间的关系

构造函数的prototype属性指向他的原型对象,然后原型对象有一个constructor属性,指向其构造函数,当调用构造函数生成一个实例对象之后,该实例的内部包含一个指针,指向其构造函数的原型对象,这个指针在Safari、Chrome、Firefox中的实现叫__proto__。

Person.prototype.constructor===Person//trueperson1.__proto__===Person.prototype//true

当我们梳理清楚这些之后,再来看原型链是什么

原型链是什么functionPerson(name){this.name=name}Person.prototype.sayName=function(){console.log(this.name)}constperson1=newPerson('Npz')person1.sayName()//Npz

还是这段代码,想一下,person1为什么可以调用sayName方法,其实,当读取一个实例对象的属性时,会首先从这个对象本身查找该属性,如果没有找到就会去他的原型对象上查找,也就是这个对象的__proto__属性指向的那个对象,所以,person1调用的其实是Person.prototype上的sayName方法,那么如何判断这个属性是存在于实例本身还是原型对象中呢,可以使用hasOwnProperty方法

person1.hasOwnProperty('sayName')//false

如果这个属性不存在于实例对象本身,就会返回false,反之返回true。

咦?这个hasOwnProperty方法又是哪来的呢?在Person.prototype上也并没有这个方法啊,其实这个方法是来自于Object.prototype,那又是怎么找到Object.prototype上的呢,其实就是通过原型链查找的,在这里我们要明确一个事情,原型对象本质上还是一个对象,而对象其实都是newObject()出来的,所以在这里,Person.prototype.__proto===Object.prototype,没错,如你所见,所有的原型对象其实也可以说是调用Object构造函数生成的实例对象,所以这里的原型对象的__proto指向的就是Object构造函数的prototype属性指向的对象,也就是Object.prototype。我们再来看一下hasOwnProperty这个方法的查找过程,首先实例对象本身没有,然后再往上他的原型对象Person.prototype中也没有,再接着就找到了Object.prototype上,这就是原型链了,通过__proto__一层一层的向上查找,如果找到Object.prototype还没有找到的话,再往上就是null了

Object.prototype.__proto__===null//true

刚才我们有提到所有的原型对象其实就是调用Object构造函数生成的实例对象,那么Object构造函数又是怎么来的呢?

再看Object构造函数

构造函数也是函数,每个函数都是调用Function构造函数生成的实例,所以函数的声明还可以这么写

constObject=newFunction()//等价于//functionObject(){}

但是我们一般不会这么写,也不推荐这么写,但是从这个表达式,我们可以推断出Object构造函数其实就是调用Function构造函数生成的一个实例,所以Object构造函数的__proto指向的就是Function.prototype,那我们再往上推,Function构造函数又是哪来的呢,没了,Function构造函数就是最顶级的构造器了,就好比Object.prototype是最顶级的原型对象一样。但是在js的实现中,我们会发现,Function构造函数的__proto指向的是Function.prototype,而Function.prototype.contructor指向的还是Function构造函数,看起来像是Function构造函数又当?又当?一样,要我说,不用纠结这个,没有意义。

看图说话

这里再放一下这张经典的原型链的图,我上面的分析其实已经cover了这张图的大部分了,还没有提到的就是Foo构造函数的__proto__指向的是Function.prototype对象,这也很好理解,因为每个函数都是通过调用Function构造函数生成的实例嘛。

现在再来看下文章开头的面试题

Object.prototype.type="x";Function.prototype.type="y";functionA(){};consta=newA();console.log(A.type);console.log(a.type);

A.type,查找type属性,首先A本身没有,然后顺着他的__proto属性往上找,这里A是一个函数,所以他的__proto指向的就是Function.prototype了,接着就找到了type属性,值为y

a.type,查找type属性,同样的a自身没有,然后a.__proto__指向的应该是A.prototype,因为a是调用A构造函数生成的实例对象,然后也没有,再往上就是Object.prototype了,找到了属性type,值为x

基于原型链的继承

在我们了解了原型链之后,再来看一下如何基于原型链实现继承。其基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。再来简单回顾一下构造函数、原型以及实例之间的关系,每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例对象又包含一个指向原型对象的内部指针(__proto__),那么,假如我们让原型对象等于另一个构造函数的实例,结果会怎么样呢,根据我们上述分析,这个原型对象将会包含一个指向另一个原型对象的指针,相应的,另一个原型对象也有一个指向其构造函数的指针。可能这么说有点绕,还是来看一下代码会清晰一点

functionSuperType(){this.property=true}SuperType.prototype.getSuperTypeValue=function(){returnthis.property}functionSubType(){this.subproperty=false}SubType.prototype=newSuperType()SubType.prototype.getSubproperty=function(){returnthis.subproperty}constinstance=newSubType()console.log(instance.getSuperTypeValue())

实现继承的关键是这一行代码

SubType.prototype=newSuperType()

调用SuperType构造函数,把生成的实例直接赋值给SubType的原型对象,按照我们上面的分析,这行代码实现的就是让原型对象等于另一个构造函数的实例,现在SubType.prototype的__proto__指向的就是SuperType.prototype,换言之,SuperType.prototype就是SubType.prototype在原型链上的上一层。

好,让我们再来看一下上面这段代码,当我们在instance实例上调用getSuperTypeValue方法,首先会在自身查找,没有,然后再去SubType.prototype里面查找,也没有,接着再去SuperType.prototype里面查找,好,在这就找到了。

这里有一个小问题需要注意一下,当我们重写了SubType的原型对象之后,现在SubType.prototype.constructor指向的就不是构造函数SubType了,因为SubType.prototype现在已经是构造函数SuperType的实例了,所以他的constructor属性的值其实是继承的SuperType.prototype的constructor属性,因此他的constructor和SuperType.prototype的constructor现在指向的都是SuperType,通常情况下,这不会有什么问题,除非你刻意的去使用constructor,比如像下面这样

functionPerson(name){this.name=name}Person.prototype.sayName=function(){console.log(this.name)}constperson1=newPerson('Npz')person1.sayName()//Npz0

这段代码会报错,create不是一个function,其实就是没找到这个方法,注意,没找到的是第二个create,来分析一下为什么没找到,newCreatedConstructor().create(),这行代码执行的其实就是CreatedConstructor.prototype上的create方法,然后这个方法内部,newthis.constructor(),因为这段代码中重写了CreatedConstructor.prototype,所以这里的constructor指向的其实是构造函数Parent,所以这里返回的是调用Parent构造函数生成的实例,返回之后,接着去调用了它的create方法,但是在返回的这个实例中根本找不到这个方法,所以就报错了。其实这段代码的本意是想去调用CreatedConstructor.prototype的create方法,所以这里只需要把constructor重新指回CreatedConstructor就好了

functionPerson(name){this.name=name}Person.prototype.sayName=function(){console.log(this.name)}constperson1=newPerson('Npz')person1.sayName()//Npz1

OK,到这基本上就讲完了,希望本文能让你对原型和原型链有一个更加深刻的理解。




原型链介绍
, 如果是方法就返回xxx is not a function.js中的对象都是由构造函数创建的 1.自定义构造函数 2.查看内置对象的原型链 3.函数也是对象类型: 验证:对象拥有点语法动态添加属性,如果函数也能像对象一样拥有点语法动态添加属性 由原型对象看 instanceof ...

js toString详解
要想弄懂tostring,我们首先要理解原型和原型链。上图为原型链的示意图。toString()** 方法返回一个表示该对象的字符串。 每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下, toString() 方法被每个 Object ...

JavaScript原型,原型链 有什么特点
所有的构造函数的prototype都不能为空, 就是说Superman.prototype = null 会被解释引擎无视; 另一方面, Object构造函数也有prototype属性(该属性是只读的, 可以为原型增加属性,但不能赋予不同的对象), 故因此可以有多层的原型链, 但原型链的根必定会是Object.prototype . 这意味着给Object.prototype...

深入分析JS原型链以及为什么不能在原型链上使
二、JS原型链的深层原理:首先要引入一个名词__proto__,__proto__是什么?在我的理解里,__proto__才是真正的原型链,prototype只是一个壳。如果你使用的是chrome浏览器,那么你可以尝试使用alert(Tom.__proto__.ability.run),你发现这样的写法完全可行,而且事实上当只有原型链上存在ability属性的...

什么是原型链,它们的区别,在js中它们具体指什么
什么是作用域链,什么是原型链。作用域是针对变量的,比如我们创建了一个函数,函数里面又包含了一个函数,那么现在就有三个作用域 全局作用域==>函数1作用域==>函数2作用域 作用域的特点就是,先在自己的变量范围中查找,如果找不到,就会沿着作用域往上找。如:var a = 1;function b(){ ...

js原生语法之prototype,__proto__和constructor
(这个过程称之为继承,而且是一个链式过程,即可以a.constructor.prototype.constructor.prototype.constructor.prototype这样查找,直到找到最顶端,这个过程可以由a.__proto__.__proto__.__proto__加速,这个就叫做原型链,js的继承只有这一种实现方式.) (上面只是引导思考过程,其实查找原型对象并不会通过a.constructor....

Vue3 的 Provide \/ Inject 的实现原理
总结来说,Vue3 的 Provide \/ Inject 实现原理是利用原型链实现数据的继承与传递,简化了组件间数据共享的复杂度。扩展说明:Object.create() 方法用于创建一个新的对象,并将现有对象作为新对象的原型。在 Vue3 中,通过 Object.create() 来设置组件实例的 provides 属性为父组件的 provides 的原型...

web前端技术基础?
从内存角度到理解JS面向对象、基本类型、复杂类型、原型链、ES6中的面向对象、属性读写权限、设置器、访问器。 面向对象三大特征: 继承性、多态性、封装性、接口。 设计模式: 面向对象编程思维、单例模式、工厂模式、策略模式、观察者模式、模板方法模式、代理模式、装饰者模式、适配器模式、面向切面编程。 第五阶段...

深入探究 Function & Object的原型问题
关于Function.__proto__ === Function.prototype,解释有两种:一是看作Function实例,符合"实例的定义";二是视为内置对象,避免了无限递归。个人倾向于后者,认为Function的原型指向是为了保证原型链的完整性。JavaScript内置类型的构建过程涉及C\/C++底层操作,包括创建Object.prototype和Function.prototype,...

js原型链和继承的理解
Object.prototype. proto 为原型链顶端 proto 定义了尚未使用所以为null故Object.prototype. proto ===null约定俗成。instanceof 用来判断某实例是否为某构造函数的实例 isPrototypeOf 用于判断某实例是否拥有某构造函数的原型对象指针 1.原型模式有忽略构造函数定义初始值步骤及原型中操作引用类型的缺点。

濉溪县19489632601: 如何更加简单的理解JS中的原型原型链概念 -
呼欧银杏: 原型是上一辈,原型链就是直系血缘关系,上一辈的很多东西可以遗传到下一辈.这样理解有误差,但是也差不多了.

濉溪县19489632601: javascript中原型原型链有什么特点 -
呼欧银杏: 原型链一直是个很抽象的概念,看不到,摸不着.随着最近对JavaScript进一步的学习,我对原型链有了一点理解,下面讲出来. 基础知识 在JavaScript中,一共有两种类型的值,原始值和对象值.每个对象都有一个内部属性[[prototype]],我们通常...

濉溪县19489632601: js中什么是原型对象和原型链 -
呼欧银杏: 每个 JavaScript 对象内部都有一个指向其它对象的“指针”或者 “引用“, 并通过这种方式在对象之间建立了一种联系,形成了一种链式结构,我的理解这就是所谓的原型链.function F() {} // F 是一个函数,函数也是对象,而且每个函数都有...

濉溪县19489632601: js里面的继承该怎么理解,原型链是什么概念,这方面的内容很重要么 -
呼欧银杏: 继承字面意思是 子集继承父集所有特性并且还具备自己的特性. 并且支持有多个爹 .😄

濉溪县19489632601: 面试问js原型怎么理解 -
呼欧银杏: 一、基于原型链的继承1.继承属性 JavaScript 对象是动态的属性“包”(指其自己的属性).JavaScript 对象有一个指向一个原型对象的链.当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原...

濉溪县19489632601: JavaScript作用域链和原型链怎么理解 -
呼欧银杏: JavaScript语言精髓与编程实践 这本书对这个讲解的很透彻,可以看一下.

濉溪县19489632601: 如何理解javascript中,原型也是一个对象? -
呼欧银杏: 推荐你看下一篇经典的介绍javascript的原型以及原型链的博文,很有深度又感觉通俗易懂.我原先对这个概念也很模糊,理解了,好像有没理解,看过该文以后,基本有了一个清晰的认识,通过几天的深入理解,可以说现在基本掌握了.

濉溪县19489632601: js里面的继承怎么理解,原型链呢,,好难理解啊, -
呼欧银杏: function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object(); console.log(typeof Object); //function console.log(typeof Function); //function console.log(typeof o1); //object console....

濉溪县19489632601: 为什么说知觉是人对感觉信息的组织和解释过程 -
呼欧银杏: 知觉是客观事物直接作用于人的感觉器官,人脑对客观事物整体的反映. 例如,有一个事物,我们通过视觉器官感到它具有圆圆的形状、红红的颜色;通过嗅觉器官感到它特有的芳香气味;通过手的触摸感到它硬中带软;通过口腔品尝到它的酸...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网