最近参加公司内部技术分享,分享的同学提到了Js原型链的问题,从V8的角度进行了传播,刷新了我之前对原型链的认识。听完后,我决定重新学习原型链,巩固基础。理解原型链深入原型链总结与思考理解Js中的原型链是一个比较有意思的话题。它用一套巧妙的方法解决了Js中的继承问题。按照我的理解,原型链可以拆分为:原型(prototype)链(__proto__)原型(prototype)原型(prototype)是一个普通的对象,为构造函数的实例共享属性和方法。在所有情况下,引用的原型都是同一个对象。例如:functionStudent(name){this.name=name;this.study=function(){console.log("studyjs");};}//创建2个实例conststudent1=newStudent("xiaoming");conststudent2=newStudent("xiaohong");student1.study();student2.study();在上面的代码中,我们创建了2个Student实例,每个实例都有一个study方法打印“studyjs”。这样写会有一个问题:两个实例中的学习方法是独立的。它们虽然功能相同,但在系统中却占据了两份内存。如果我创建100个Student实例,它们将占用100份内存。往下算,会造成大量的内存浪费。所以Js创建了原型。functionStudent(name){this.name=name;}Student.prototype.study=function(){console.log("studyjs");};//创建2个实例conststudent1=newStudent("xiaoming");conststudent2=newStudent("小红");student1.study();student2.study();使用原型后,student的原型中保存了study方法,内存中只会保存一份,所有的Student实例都会Share它,内存问题就迎刃而解了。但是这里还有一个问题。为什么student1可以访问Student原型上的属性和方法呢?答案就在__proto__中,我们往下看。Chain(__proto__)Chain(__proto__)可以理解为一个指针,它是实例对象中的一个属性,指向构造函数(prototype)的原型。我们来看一个案例:functionStudent(name){this.name=name;}Student.prototype.study=function(){console.log("studyjs");};conststudent=newStudent("小明");student.study();//研究jsconsole.log(student.__proto__===Student.prototype);//true从打印结果可以得出结论:函数实例的__proto__指向构造函数的原型,上面剩下的问题就迎刃而解了。但是很多同学可能会有这样的疑问。为什么调用student.study时访问的是Student.prototype.study?答案就在原型链中,我们往下看。原型链原型链是指:实例对象在调用属性或方法时,会从实例本身、构造函数的原型、构造函数原型的原型……中查找是否有对应的属性或方法。这个查找方法就像一个链条,从实例对象到Object.prototype,专业上叫做原型链。或者看一个案例:functionStudent(name){this.name=name;}Student.prototype.study=function(){console.log("studyjs");};conststudent=newStudent("小明");student.学习();//学习js。//在实例中找不到,在构造函数的原型上找到。//实际调用是:student.__proto__.say即Student.prototype.say。学生.toString();//"[objectObject]"//在实例中找不到。//在构造函数的原型上也找不到。//在构造函数原型的原型上找到。//实际上调用的是student.__proto__.__proto__.toString即Object.prototype.toString。可以看出__proto__就像一条链条,串联连接着实例对象和原型。同样,上面的代码中仍然存在下面的问题。为什么Student.prototype.__proto__是Object.prototype?这里有一个推导步骤:首先找到__proto__前面的对象,就是Student.prototype的构造函数。判断Student.prototype的类型,typeofStudent.prototype是object。对象的构造函数是Object。原来Student.prototype的构造函数是Object。所以Student.prototype.__proto__是Object.prototype。这种推导方式非常实用,除了自定义构造函数对象外,其他对象都可以推导出正确答案。原型链常见问题原型链存在很多问题。下面是一些更常见的问题。什么是Function.__proto__?找到函数的构造函数。判断Function类型,typeofFunction为function。函数类型的构造函数是Function。产生Function的构造函数是Function。所以Function.__proto__=Function.prototype。什么是Number.__proto__?这只是一个细微的变化,很多同学都不知道。其实和上面的问题一样。找到Number构造函数。判断Number类型,typeofNumber是一个函数。函数类型的构造函数是Function。Number的构造函数是Function。所以Number.__proto__=Function.prototype。什么是Object.prototype.__proto__?这是一个特例。如果按照常理推导,Object.prototype.__proto__就是Object.prototype,但这是错误的,原型链会无限循环到Object。Js的创造者为了解决这个问题,直接将Object.prototype.__proto__指定为null,打破了原型链的死循环。了解了这些问题后,再看看这张经典的图,大家应该都明白了。深入原型链介绍完传统的原型链判断,我们就从V8层面来了解一下。V8如何创建对象Js代码在执行时会被V8引擎解析,V8会使用不同的模板来处理Js中的对象和函数。例如:ObjectTemplate用于创建对象,FunctionTemplate用于创建函数。PrototypeTemplate用于创建函数原型。仔细看一下V8中的定义,我们可以得到以下结论。Js中的函数都是由FunctionTemplate创建的,返回值是FunctionTemplate实例。Js中的对象都是通过ObjectTemplate创建的,返回值是ObjectTemplate的一个实例。Js中函数的原型都是通过PrototypeTemplate创建的,返回值是一个ObjectTemplate实例。所以Js中一个对象的原型可以这样判断:所有对象的原型都是Object.prototype,自定义构造函数的实例除外。自定义构造函数的实例,其原型是相应的构造函数原型。Js中的函数原型判断就更简单了。所有函数原型都是Function.prototype。下图是所有的内置构造函数,它们的原型都是Function.prototype。看到这里,是不是也能一眼看出任何物体的原型呢?附:V8中的函数解析案例了解了原型链之后,我们来看一下V8中的函数解析。functionStudent(name){this.name=name;}Student.prototype.study=function(){console.log("studyjs");};conststudent=newStudent('xiaoming')这段代码在V8中会这样执行://创建一个函数v8::Local
