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构造器,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!