原型的一些理解

原型的一些理解

首先要想明白原型prototype属性就要知道这个属性是针对谁而言的,也就是说prototype这个属性是谁可以调用的。一般来说我们都是针对于构造函数(用于创建对象的函数)来谈原型以及原型链的,因为原型链必须要有对象之间的类与类之间的继承关系才能实现一根根链条,没有继承原型链的也就没有什么长度,构造函数也是函数,构造函数也不是一个全新的数据类型,只是函数的一种人为定义的特殊叫法,把用于创建对象的函数称为构造函数并且首字母大写,目的就是为了在代码检查的时候一看首字母是大写的就可以知道这个函数需要被new关键字调用生成对象如果没有被new关键字调用就会给全局或者某个命名空间生成全局性的变量污染环境,原型prototype这个属性是函数有的,我们称他为显示原型,而__proto__这个属性并不是JavaScript官方规定的而是浏览器厂商规定的一个属性 针对于对象使用的 一个对象的__proto__的属性会返回这个对象的隐式原型,这又是什么呢? 这就要考虑对象是怎么产生的,所有的对象都是由构造函数来产生的,obj = {};这种语法糖的写法只是简化了生成对象的繁琐代码,本身就是obj = new Object();对象都是由对象的构造函数创建出来的,obj.__proto__和obj这个对象构造函数的prototype指向的是同一块内存空间,对象是引用类型所以指向用一块内存空间就说名这两个对象是相等的。我认为obj.__proto__就是帮助那些构造函数不容易找到的对象来找寻原型对象。所以obj.__proto__ === Obj.prototype 所以我可以认为__proto__是对象的属性,prototype是构造函数的属性。

函数是一种对象

原型也是一种对象,所以这个对象也会有__proto__属性这个属性就肯定要指向Object.prototype,所有的构造函数的原型对象都是一个对象,由Object new 出来的所以Object.prototype === Function.prototype._proto; 这句话我的理解是 对象的构造函数的原型对象就是Function构造函数的显示原型的隐式原型 所以Object就是一切原型链继承的末端。 函数都是怎么来的呢? 函数其实就是 Function()构造函数new出来的 function fn(){}; fn._proto_ === Function.prototype 所以

函数也是一种对象。都是有Function new出来的 比较奇怪的事情出现了Function也是函数那么他是怎么来的,他就是他自己new出来的?,这个真的十分难以理解和想象吧。但是我们可以测试下 Function._proto_ === Function.prototype Function instanceof Function 这都是true的 而且Function instanceof Object也是true的。这也就看出来了Function也是个对象,一切的函数也是对象。是对象就是由Object new出来的 原型链的末端也就是Object.prototype 这个末端再往下就是null啦 Objec,prototype上有一些常见的方法 tostring valueof等等 所以所有的对象函数都有这些方法都是这里继承过来的 Function上并没有这些方法,却可以调用就可以说明Function上的tostring方法是继承自Objec.prototype上的。

修改原型

在讨论修改原型的时候,我们一定要注意修改与覆盖的区别。这就涉及到一个堆栈以及左查找右查找的过程了,在我们创建对象的时候,基本的语法糖形式 var obj = {};这个过程其实是堆中开辟一段空间然后在栈中保存这个对象在堆中的地址,

将这个地址放在obj这个容器中,obj这个对象我们在对他进行操作的时候如果是修改的时候我们对这个obj变量进行右查询找到obj变量空间内部保存的地址,根据地址找到这个对象真正的内存空间然后进行操作,所以我们修改obj这个对象的时候本质就是在修改堆内存空间的这个对象,而对obj进行赋值操作,把之前的内容进行覆盖,我们就相当于用一个新的地址把之前的地址进行了覆盖由于地址都是在栈中存储,也就不存在销毁(类比基本类型)是进行覆盖,这个覆盖过程切断了obj这个容器与堆内存中的对象的联系,将obj这个容器内我们重新赋值一个新的地址,让它指向堆内存一块新的空间。这个赋值过程是对obj这个变量的一个左查找,找到这个obj对象的空间,切断它与之前堆内存那段空间的联系,然后让它重新指向一段新的内存。所以我们在修改原型的时候要注意,我们如果本意是扩充原型对象中的属性或者方法,我们就要对原型对象的属性或者方法进行增加删除或者修改,一定不可以将原型对象重新赋值,这个过程会切断原型链,切断后就不可以沿着原型链进行访问了。在一些类库的源码中都是将一些数组或者数组对象(一个对象内的每一项都是数组)的原型进行覆盖,数组就没有了push等方法,而是原型对象中自己定义的方法。

查找属性方法

在查找一个对象的属性或者方法的时候,我们首先做的事情是先找这个对象本身是否存在这个属性或者方法,本身不存在会沿着原型链查找原型对象上有没有这个方法,原型对象的隐式原型依次类推最后找到Object.prototype,这个对象是原型链的末端。在这个过程中我们可以类推链表的那种结构,沿着链表向末端查找,在链表中的任何一个位置找到这个方法或者属性,这个对象就可以调用。如果想知道这个属性方法是不是对象本身自己的就用hasOwnProperty来检测

修改属性方法

在修改属性或者方法的时候,我们就要考虑数据类型了

基本类型:虽然原型上很少有基本类型的属性但是也不好说是不是一定没有。在修改一个基本类型的属性时,我们的做法是在这个对象上查找有没有这个属性,有就修改,没有就创建一个。

引用类型:像是原型上的数组,我们针对这个对象进行属性的查找的时候,我们还是先找本身再找原型,如果我们是对这个属性进行覆盖操作 obj.array = [];那么就相当于在这个对象本身创建这个属性然后赋值

           如果是修改 obj.array.push();那么这个操作会把原型上的array属性进行修改。影响所有的继承这个属性的对象(前提是在这个修改之后的操作,之前的操作不影响)

方法 : 类比引用类型。

 

原文地址:https://www.cnblogs.com/lllmx/p/7976377.html