宝哥软件园

深入分析js原型链和vue构造器

编辑:宝哥软件园 来源:互联网 时间:2021-08-29

1.什么是原型链?

简要回顾构造函数、原型和实例:之间的关系

每个构造函数都有一个原型对象,它包含一个指向构造函数的指针,而一个实例包含一个指向原型对象的内部指针。

Goose,js对象:中有这样一个规则

如果您试图引用对象(实例)的属性,您将首先在对象中查找该属性,直到找不到为止,然后在对象的原型(instance.prototype)中查找该属性。

废话少说,我们先来看一个例子:

fun 1(){ this . win=' SKT ' } fun 1 . prototype . getval=fun(){ return this . win } fun 2(){ this . other _ win=' RNG ' } fun 2 . prototype=new fun 1()我们让原型对象指向另一种类型的实例,即:Constructor1。属性=instance2。

那么他是如何找到instance.getVal()?中间发生了什么?

1).首先,在instance1的内部属性中查找它;

2).接下来,在实例1中查找它。_ _ proto _ _(constructor 1 . prototype)和构造函数1。prototype实际上是instance2,也就是说,在instance2中查找这个属性;

3).如果instance2中没有,程序此时不会灰心,会继续在Instance2中搜索原型对象。_ _原型_ _(构造器2。原型).直到对象

搜索轨迹:实例1-实例2-构造函数2 .原型…-对象.原型。

这种搜索轨迹看起来像一个长链,因为原型在这个游戏规则中充当一个链接,所以我们称这个实例和原型链为原型链

二、原型和__proto__是什么?

1.prototype是一个纯函数属性

让fun=function(){ } console . log(fun . prototype)//object console . log(fun。_ _ proto _ _)//函数2。_ _ proto _ _是对象的属性,但_ _ proto _ _不是规范属性,对应的标准属性是[[

让obj={ } console . log(obj . prototype)//undefinedconsole . log(obj。__proto__)//对象我们可以把_ _ proto _ _理解为构造函数的原型。在大多数情况下,_ _ proto _ _==constructor . prototype(object

第三,到底有什么新鲜事?

我们都知道new是一个实例化的过程,那么它是如何实例化的呢?让我们看一个简单的例子:

函数Fun(){这个。team=' RNG ' } let F=new fun()console . log(F . team)//RNG在上面的代码中,我们通过new命令实例化了一个名为Fun的函数,并将其赋给了F,这个新生成的实例对象F从Fun构造函数中获取了team属性。事实上,构造函数内部的这个表示新生成的实例。

这是什么原则?答案如下?

1.创建一个空对象作为要返回的对象实例。2.将这个空对象的原型指向构造函数的原型属性。3.将这个空对象分配给函数中的这个关键字。4.开始在构造函数中执行代码

也就是说,在构造函数内部,这是指一个新生成的空对象,对此的所有操作都将发生在这个空对象上。这就是为什么构造函数被称为“构造函数”,它是操纵一个空对象(即这个对象),并根据需要“构造”它。

如果我不添加新的呢?

fun()这个函数。team=' RNG'}让f=fun()console . log(f)//undefined console . log(team)//RNG我们可以看到上面打印的f是未定义的,但是team有值。为什么呢?

事实上,在这种情况下,构造函数变成了一个普通函数,不会被实例化。这时,这指向全局,而团队变成了全局变量,所以我们得到了这个值

4.__proto__指向哪里?

当指向__proto__时,它取决于对象创建时的实现。

辣,有哪些实现方法?

1.字面意思

Letobj={} console.log (obj。_ _ proto _ _)//object console . log(obj。_ _ proto _ _==obj。constructor.prototype)//true证明由文字量创建的函数,其_ _ proto _ _等于对象构造函数的prototype 2。

functionfunc () {}的方法让a=new func()console . log(a . _ _ proto _ _)//object console . log(a . _ _ proto _ _===a . constructor . prototype)//true 3 . object . create()

让obj1={name:'rng'}让obj 2=object . create(obj 1)console . log(obj 2。_ _ proto _ _)//{ name : ' RNG ' } console . log(obj 2。_ _ proto _ _===obj2。构造函数。原型)//false Note : Object。创建(原型,描述符)用指定的原型创建一个对象,并可选地包含指定的属性

5.如何确定原型和实例的关系?

坦率地说,要确定原型和实例之间的关系,有两种方法:实例和isPrototype()

1 .实例

我们使用这个运算符来测试已经出现在实例和原型链中的构造函数,如果它们已经出现,则返回true,否则返回false

来吧,让我们测试:

Fun1(){ this . laji=' Uzi ' } Fun2(){ this . strong=' faker ' } Fun2 . prototype=new Fun1()让fun 2=new fun 2()console . log(fun 2 instance of fun 1)//true console . log(fun 2 instance of fun 2)//true因为有了prototype链,我们可以说fun 2是一个object,fun 1的任何类型fun 1或fun 2的实例,所以三个结果都返回true

2.isPrototype()

同样,只要是已经出现在原型链中的原型,这个方法就会返回true。用法如下

console . log(fun 1 . prototype . is prototype of(fun 2))//true console . log(fun 2 . prototype . is prototype of(fun 2))//true console . log(object . prototype . is prototype of(fun 2))//true VI。原型

什么事?原型链有问题吗?我买了佛寒,为什么?

原因1 :当原型链包含具有引用类型值的原型时,引用类型值将被所有实例共享;

原因2:创建子类型时,不能将参数传递给超类型的构造函数。

七、如何解决原型链问题?

1.借用一个构造函数,也称为经典继承

基本思想:在子类型构造函数中调用超类型构造函数

函数只是在特定环境中执行的代码的对象,因此构造函数也可以通过使用apply()和call()方法在(未来)新创建的对象上执行

看看例子:

function神父(){ this.team=['letme ',' mlxg'] } function Son () {父王. call(this)} let Son=new Son(). Son . team . push(' Uzi ')console . log(Son . team)/[' letme ',' mlxg ',简自豪']let little _ Son=new Son(). console . log(little _ Son . team)/[' let me ',' mlxg']我们可以看到借用构造函数一下子解决了原型链的两大问题。

首先,它确保了原型链中引用类型值的独立性,不再被所有实例共享;

其次,子类型还可以在创建时将参数传递给父类型。

但是,还是有一个问题。如果只借用构造函数,就无法避免构造函数模式:的问题

方法都是在构造函数中定义的,所以函数重用不可用。此外,超类型(如父类型)中定义的方法对于子类型是不可见的。因此,借用构造函数的技术很少单独使用。

2.组合遗传

组合继承,有时也称为伪经典继承,是指将原型链与借用构造函数的技术相结合,充分发挥其优势的继承模式。

基本思想:使用原型链继承原型属性和方法,借用构造函数继承实例属性。

这样,在原型上定义的方法不仅实现了功能重用,还保证了每个实例都有自己的属性。

然后看例子:

函数父亲(team){ this . team=team this . people=[' mlxg ',' letme'] }父亲. prototype . say team=function(){ return console . log(this . team) }函数儿子(team,age) { this.age=age父亲. call(this,team)}儿子. prototype=新父亲(Son . prototype . say age=function(){ return console . log(this . age)}让儿子=新儿子(' faker ',8)儿子. people.push('uzi ')控制台. log(son.people) /。 3)console . log(little _ son . people)//[' mlxg ',' letme ']little _ son . say age()//3 little _ son . say team()//bang我们可以看到组合继承不仅保证引用类型不再被所有实例共享,它还可以在创建子类型时将参数传递给父类型。 同时原型中的方法可以重用,可以说避免了原型链中的两大问题和借用构造函数的缺陷。因此,它也是js中最常用的继承方法。此外,

Instanceof和isPrototypeOf()也可以用来识别基于组合继承创建的对象。

3.原型继承

在prototype的帮助下,您可以基于现有对象创建新对象,而无需创建自定义类型

绳子是什么意思?

例如,在fun()函数中,我们首先创建一个临时构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。

函数fun(o){函数F()} { F . prototype=o;返回新的F();}让obj={arr: [11,22]好玩(obj)。控制台日志。arr)//[11,22,33]在这个例子中,obj对象可以作为另一个对象的基础,所以我们Then函数返回一个新的对象。这个新对象以arr为原型,所以它的原型包含一个引用类型值属性。然后我们向这个属性添加另一个元素,这样我们就可以打印出来了。

*在原型继承中,具有引用类型值的属性将始终共享相应的值,就像使用原型模式一样。

4.寄生遗传

:的基本思想是创建一个只用于封装继承过程的函数。该函数以某种方式在内部增强对象,最后返回对象,就好像它真的完成了所有工作一样。

函数fun(o){函数F()} { F . prototype=o;返回新的F();} letobj={a: [11,22]}函数通过调用函数创建另一个(z){//创建一个新对象var clone=fun(z);clone . SayHi=function(){ alert(' hi ');}返回克隆;} create other(obj)在上面的例子中,我们将obj传递到create other()函数中,返回的新对象clone不仅有这个属性,还进行了增强,并且有sayHi()方法;

等一下,这里需要注意的是,使用寄生继承给对象添加函数,会因为不能重用函数而降低效率;这类似于构造函数模式。

5.寄生组合遗传

如前所述,组合继承是JavaScript最常用的继承模式;然而,它也有自己的缺点。组合继承最大的问题是父类构造函数:会被调用两次,一次在创建子类型原型时,另一次在子类型构造函数内部。寄生组合继承的诞生是为了减少调用父类构造函数的开销

基本思想:不需要调用超类型的构造函数来指定子类型的原型

函数inheritPrototype(subType,super type){ var protoType=object . create(super type . protoType);//Create object prototype . constructor=subType;//增强对象subType.prototype=protoType//指定对象}函数父(名){this。name=namethis.colors=['red ',' blue ',' green '];}神父. prototype . SayName=function(){ console . log(this . name);}函数Son(姓名、年龄){父王. call(此,姓名);this.age=年龄;} inheritPrototype(子,父)Son . prototype . sayage=function(){ console . log(this . age);} var实例=new Son('uzi ',3);instance . SayName();//Uzi instance . SayAge();//3inheritPrototype函数接收两个参数:子类型构造函数和超类型构造函数。

1.创建超类型原型的副本。

2.将构造函数属性添加到创建的副本中,以弥补由于重写原型而丢失的默认构造函数属性

3.将新创建的对象(即副本)分配给子类型的原型

inheritPrototype的高效率在于不调用超类构造函数,所以避免了在subClass.prototype上创建不必要的、冗余的属性,同时原型链可以保持不变,可以说是相当的Naisi

由于寄生组合遗传,它结合了寄生遗传和组合遗传的优点,是实现基于类型遗传最有效的方法。

八.vue构造器

我们在使用vue的时候,经常会用新的运算符来实例化它,这说明vue也是一个构造函数,那么它是怎么创建的呢?我非常兴奋地克隆了vue的源代码,并仔细研究了它

Vue源代码地址

我第一次找到src/core/instance/index.js文件,打开后很震惊

在第八行代码中,创建了Vue的一个函数。这不就是Vue的构造器吗,在12行的警告中我更确定了。他说:Vue是构造函数,应该用关键字“new”来调用。然后他在下面,和他分别在

initMixin()

stateMixin()

eventsMixin()

lifecycleMixin()

renderMixin()

在这五种方法中,Vue作为形式参数引入,最后导出Vue。

那么这五种方法是做什么的呢?我们先来看看initMixin()方法,打开。/init.js文件,并找到方法

我们不关心其他代码。让我们看看方法的前几行。他将_init方法注入到Vue的原型中。这个方法有点熟悉。我们好像在什么地方见过。是的,它就在index.js文件中。

这个this_init(options)看起来像是一种内部初始化的方法,而option应该是初始化期间的一些配置项。当Vue被实例化时,这。_init()方法将被执行

接下来,让我们看看。/state.js文件并找到stateMixin方法

摘要

以上是边肖介绍的js原型链和vue构造器,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!

更多资讯
游戏推荐
更多+