当前位置: 首页 > 科技观察

看完这篇,再也不会害怕别人问我什么是原型了

时间:2023-03-17 23:36:26 科技观察

看完这篇文章,我再也不怕别人问我原型是什么了。转载本文请联系无名宝计划元公众号。PrefacePrototype和prototypechain应该是大部分前端er都说不好的词了,但是应该有很多人不能完全解释清楚这两个内容,当然也包括我自己。最早的原型链文章是2019年7月写的,当时费了很大力气才理解了70%到80%,到现在基本都忘记了。时隔两年,我又开始重新审视原型和原型链的内容。JavaScript中的对象在JavaScript中,对象被称为属性的集合。创建对象的方式也有很多种,最常见的是双花括号的形式:varobj={};obj.name='小土豆';obj.age=18;这种方式其实就是下面的方式语法糖:varobj=newObject();obj.name='小土豆';obj.age=18;另外,在JavaScript中,对象也可以通过构造函数进行自定义。functionCat(){}varcatMimi=newCat();//自定义对象如果用new关键字调用一个函数,那么这个函数可以称为构造函数,否则就是一个普通函数。什么是原型原型的简要概括:原型是一个对象。在下面的总结中,原型可以描述为原型对象,相当于原型从哪里来?原型对象存在于何处,是否需要通过代码创建?我们说对象是属性的集合,那么原型对象包含哪些属性呢?如何操作和使用原型机?接下来,我们将一一探究。?原型从何而来?JavaScript为所有函数创建原型。functionCat(){}在上面的代码中,我们创建了一个Cat函数,那么这个Cat函数有一个原型,用代码表示:Cat.prototype。同样,我们创建一个函数Fn1,函数Fn1有一个原型,用代码表示为Fn1.prototype。函数名的大小写本质上没有区别?原型包含了哪些属性?我们之前说过以下两点:原型是一个对象。对象是属性的集合。原型包含哪些属性?我们已经知道原型使用的代码表示是:functionName.prototype,那么我们在代码中console.log它。functionCat(){}console.log("Cat.prototype:");console.log(Cat.prototype);functionDog(){}console.log("Dog.prototype:");console.log(Dog.prototype);在Firefox浏览器中输出如下:可以看到函数的原型默认有两个属性:constructor和.其中,函数原型的constructor属性指向函数本身。函数原型的属性称为隐式原型,隐式原型我们后面会单独介绍。?如何操作和使用原型一般情况下,我们操作一个普通对象的方式如下:varobj={};//创建对象obj.name='小土豆';//给对象添加属性obj.age=18;//给对象添加属性varname=obj.name;//由于访问对象属性原型也是一个对象,所以操作原型的方式同上。functionCat(){}Cat.prototype.type='cat';Cat.prototype.color='White';Cat.prototype.sayInfo=function(){console.log(this.type+'is'+this.color);}这时候再次打印Cat.prototype可以看到我们给原型添加的属性:原型对象上的访问方法和属性:以上操作原型的方法对实际项目开发没有参考价值,但是不要不着急,我们稍后会详细解释隐式原型。我们在总结函数的原型对象时提到了隐式原型。事实上,JavaScript为所有对象创建了称为隐式原型的属性。我们一直在说原型是一个对象,所以在上面的截图中,原型也有一个隐式的原型属性。?隐式原型的代码表示隐式原型是对象的私有属性,可以在代码中访问:obj.__proto__。obj.__proto__是非标准的,一些低级浏览器不支持这种写法。我们实际上是在浏览器的控制台访问它:从打印结果可以看出,隐式原型也是一个对象。原型对象包含哪些属性?一起来看看吧。?隐式原型存在的意义首先我们写一个简单的例子:functionCat(){}varcatMimi=newCat();varcatJuju=newCat();在上面的代码中,我们创建了一个Cat函数,并通过newkeyword创建了两个实例对象catMimi和catJuju,以Cat为构造函数。接下来我们在浏览器的控制台工具中看一下这两个实例对象的隐式原型所包含的属性。可以看到catMimi.__proto__和catJuju._proto__的结果好像是一样的,眼尖的同学应该也发现这个打印结果好像和上一节打印的Cat.prototype一样【Prototype包含那些属性]的。话不多说,我们用==运算符来判断一下:可以看到判断结果全部为真。由于catMimi和catJuJu对象是Cat函数创建的实例,所以结论是对象的隐式原型__proto__指向创建该对象的函数的原型对象。原型链:原型和隐式原型存在的意义我们已经总结了原型和隐式原型的概念,以及如何使用代码来操作原型和隐式原型。总的来说,原型和隐式原型似乎并不是特别强大。他们有什么用??所有实例对象共享原型上定义的属性和方法。我们看下面的例子:functionCat(name,age){this.type='RagdollCat';//Ragdollcatthis.eyes=2;this.name=name;this.age=age;this.sayInfo=function(){console.log(this.type+''+this.name+'is'+this.age+'yearsold');}}在这个例子中,我们创建了一个Cat函数,Cat函数有五个属性:type、eyes、name、age、sayInfo,其中type和eyes属性已经有了初值,name和age通过参数和任务;sayInfo对应一个打印出type、name和age的值的函数。然后我们创建Cat的两个实例对象,catMimi和catJuju,传入不同的name和age参数。varcatMimi=newCat('咪咪',1);varcatJuju=newCat('Juju',2);在控制台查看我们创建的对象:可以看到这两个对象的属性是一样的,因为type和eyes在Cat函数创建的时候有固定的初始值,所以两个属性值是一样的;sayInfo函数也有同样的功能,打印出一些属性信息;通过参数传递的只有name和age,它们各自的值是不一样的。另外,catMimi和catJuju是两个不同的对象,两者的属性值是相互独立的,修改其中任何一个的属性值都不会影响另一个对象的属性值。如果我们以后有更多这样的对象,JavaScript仍然会为每个对象创建相同的属性,并且所有这些对象将具有相同的类型、eyes属性值和相同的函数sayInfo函数。这无疑造成了内存的浪费,那么此时我们可以在函数的原型对象上定义这些属性:functionCat(name,age){this.name=name;this.age=age;}Cat.prototype.type='RagdollCat';//布偶猫Cat.prototype.eyes=2;Cat.prototype.sayInfo=function(){console.log(this.type+''+this.name+'is'+this.age+'yearsold');}varcatMimi=newCat('Mimi',1);varcatJuju=newCat('Juju',2);那么我们再来看看这两个对象:可以看到这两个对象现在只包含了两个属性,就是Cat构造函数内容里面定义的两个属性:name和age。然后我们要访问对象上的type、eyes、sayInfo:我们的实例对象仍然可以正常访问属性,方法也打印出了正确的信息。你是如何访问它的??原型链在前面的示例代码中,我们在函数的原型上定义了一些属性和方法,最终使用这个函数创建的实例对象可以正常访问原型上定义的属性和方法。方法,这是如何工作的?前面我们说过:对象的隐式原型指向创建该对象的函数的原型对象,所以当实例对象中没有属性时,JavaScript会按照实例对象的隐式原型去查找,这就是我们所说的调用原型链。既然是链条,我们就应该想到事物是一个一个连接起来的,所以不应该只是当前实例对象的隐式原型指向创建该对象的函数的原型对象,所以我们在做给catMimi对象一些东西:在上面的操作中,我们调用了catMimi的hasOwnProperty方法。显然,我们并没有为这个对象定义这个方法,那么这个方法是从哪里来的呢?答案还是原型链:在实例对象catMimi中调用catMimi.hasOwnProperty()方法查找属性,发现在catMimi.__proto__中没有要查找的属性,因为catMimi.__proto__=Cat.prototype(隐含的实例对象的原型指向创建实例的函数的原型),即在Cat.prototype中寻找hasOwnProperty属性,显然Cat.prototype没有这个属性,所以我们继续沿着Cat.prototype.__proto__,又因为Cat.prototype.__proto__=Object.prototype(我们一直在强调原型是一个对象,既然是对象,就是通过Object函数创建的,所以Cat.prototype的隐式原型指向Object函数的原型)我们打印一下Object.prototype是否包含hasOwnProperty属性:可以看到,Object.prototype中有hasOwnProperty属性,所以catMimi.hasOwnPrototype其实调用的是Object.prototype.hasOwnProperty。总结这篇文章到这里基本就结束了。相信大家对原型和原型链应该都有一定的了解。最后,我们对本文做一个总结。原文链接:https://mp.weixin.qq.com/s/59p32Xe03YCGhP2uTBjTUg