首页 >> 大全

JS 原型与原型链 学习笔记和归纳

2024-01-01 大全 25 作者:考证青年

js 原型与原型链: 原型:

​ “原型是一 种对象,被用在 中实现继承结构、状态和行为。当构造函数创建对象时,那个对象隐含引用构造函数的关联原型,以此分解属性引用。通过程序中的表达式 . 可以引用到构造函数的关联原型,通过继承,添加给对象的属性 会被所有共享此原型的对象共享。”----------《规范-第三版_中文版》

首先我们根据规范可以知道,原型是一个具体的对象。

几条总结:

相关函数与对象的关系图:

ws5wOx.jpg

上面的关系图清楚的显示了相关的对象联系。

原型链:

原型链是一种链式的结构,我们都知道在数据结构中,有一种数据结构叫做链表,可以通过指针来寻找到该对象的前面后后面是什么数据。和链表类似,在js当中,我们也使用了类似于链表的结构来实现相关数据的访问。

“当谈到继承时, 只有一种结构:对象。每个实例对象( )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象( )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。几乎所有 中的对象都是位于原型链顶端的 的实例。” ----------《MDN:继承与原型链》

当我们去访问一个对象的属性的时候,假如本对象中没有该属性,则会向更高层去寻找,更高层没有继续向更高层去寻找,以此类推。层层寻找的关系,我们把它称之为原型链。

原型链查找原理:

当我们去访问一个对象的属性的时候,假如本对象中没有该属性,此时并不会报错或者返回,js会进入到该对象的所指向的对象中找,如果还没有找到,继续进入到现在这个对象所指向的对象中去找,直到找到然后停止在这个链上的查找。如果没有找到就会抛出一个错误。

通常情况下我们遇到的是这样的情况:

实例:

_原型链js简书_原型和原型链的理解js

function Animo(name){this.name = name
}
Animo.prototype.eat = function(){console.log("this is a action for " + this.name)
}
var tiger = new Animo("tiger")
tiger.eat()
//this is a action for tiger

我们从这个实例中我们可以看到在tiger实例当中并没有eat这个方法,但是js会去向tiger.所指向的对象中查找,发现有eat这个函数,于是就对调用隐式原型里的eat函数。

实例:

function Animo(name){this.name = name
}
Animo.prototype = {eat:function(){console.log("this is a action for " + this.name)}
}
var tiger = new Animo("tiger")
tiger.eat()
//this is a action for tiger

上面的实例,我们把Animo.存了一个新的对象的地址引用,我们在前面说过,当我们声明了一个函数后它就有自身的原型,通过函数的去指向它,虽然是一个对象。凡是当我们进行上面的赋值操作后,就和原来的对象失去了联系,原本我们默认的查找顺序是:tiger-> Animo.->.->null,这是我们默认的原型链。但是当我们重新赋值的时候,查找的顺序变了:tiger->obj->.->null。其实这不是我们想要的结果。假如我们在原型改变之前就有一个实例:

function Animo(name){this.name = name
}
var tigerA = new Animo("tigerA")
Animo.prototype = {eat:function(){console.log("this is a action for " + this.name)}
}
var tiger = new Animo("tiger")

此时的两个变量是有着完全不同的原型链的。

的原型链:->Animo.->.->null.

tiger的原型链:tiger->obj->.->null

为什么会出现这样的情况,是因为你在没有改变原型的情况下就已经实例化了一个对象。那么,该对象的存储的就是Animo.里的地址值,也就是默认原型。当你改变原型的时候,也就是说,将Animo.的值改变成了另一个地址值,当你实例化的时候,js会把构造函数中村的原型的地址值给。但是此时你的的值已经改变了,所以实例化对象的的值就是改变了的值。

还有一点需要注意,当你重新给构造函数.赋新的对象的时候,一定要带上这个属性,

:

​ “返回创建实例对象的 构造函数的引用。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。” -------《MDN:属性》。

所有的对象都会从他的原型上继承一个属性。

var o = {};
o.constructor === Object; // truevar a = [];
a.constructor === Array; // true

我们手动实例化的对象本身没有这个属性,但是在对象的中有这个属性,该属性指向的是构建实例对象的构造函数。

以下内容来自MDN文档,他们写的很好。可以用来做笔记。

改变函数的:

大多数情况下,此属性用于定义一个构造函数,并使用new和继承原型链进一步调用它。

function Parent() {}
Parent.prototype.parentMethod = function parentMethod() {};function Child() {}
//Object.create的第一个参数是创建该函数的原型。
Child.prototype = Object.create(Parent.prototype); // re-define child prototype to Parent prototypeChild.prototype.constructor = Child; // return original constructor to Child

但为什么我们需要在这里执行最后一行?很不幸正确答案是 - 看情况而定。让我们来尝试定义在哪些情况下,重新分配原始构造函数会发挥重要作用,以及在什么时候它就是额外的未使用的(无效的)代码行。

试想下一种情况:该对象具有创建自身的方法。

function Parent() {};
function CreatedConstructor() {}CreatedConstructor.prototype = Object.create(Parent.prototype);CreatedConstructor.prototype.create = function create() {return new this.constructor();
}new CreatedConstructor().create().create();
// error undefined is not a function since constructor === Parent

我们可以来分析一下这段代码的原型链构成:

实例对象->obj->.->.->null。这里的obj是.创建出来的函数。现在,函数存在于obj对象中。当我们new ()时,生成一个实例对象。当我们执行.()时我们会:

return new this.constructor();
//this.constructor()会被执行,这个this就是我们生成的实例对象。
//这个实例对象的原型指向的是Prarent.prototype。我们在上面可以知道。
//constructor的指向的是其原型所对应的构造函数,也就是我们这里的Parent构造函数。
//return new this.constructor() <===> return new Parent()
//但是我们很清楚的知道,Parent的prototype里并没有create()这个函数,所以会报错。最根本原因是因为
//construtor指向的是Parent而不是CreatedConstructor,所以说会报错。

我们来改一下代码:

function Parent() {};
function CreatedConstructor() {}CreatedConstructor.prototype = Object.create(Parent.prototype);
CreatedConstructor.prototype.constructor = CreatedConstructorCreatedConstructor.prototype.create = function create() {return new this.constructor();
}new CreatedConstructor().create().create();

在这里我们将..指向了,而在的原型中有我们想要的函数。所以我运行该代码的时候不会报错。

总结:手动设置或更新构造函数可能会导致不同且有时令人困惑的后果。为了防止它,只需在每个特定情况下定义构造函数的角色。在大多数情况下,不使用构造函数,并且不需要重新分配构造函数。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了