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

图解Javascript原型(prototype)链

时间:2023-03-18 01:40:46 科技观察

本文试图解释Js中原型和原型链(prototypechain)的概念及其作用机制。我们知道Js中一切都是对象(Object),但是Js中没有类;js是一种基于原型(prototype-based)的面向对象(OOP)编程范式,但并不是所有的对象都具有原型的属性:vara={};console.log(a.prototype);//=>undefinedvarb=function(){};console.log(b.prototype);//=>{}varc='Hello';console.log(c.prototype);//=>undefinedprototype是每个函数定义都自带的属性,但是Js中的函数本身也是一个对象。下面我们来看一下Concept的区别:1.function,Function,Objectand{}function是Js的一个关键字,用来定义函数类型的变量,有两种语法形式:functionf1(){console.log('这是f1函数!');}typeof(f1);//=>'函数'varf2=function(){console.log('这是函数f2!');}typeof(f2);//=>'function'如果你以更面向对象的方式定义一个函数,你可以使用Function:varf3=newFunction("console.log('Thisisfunctionf3!');");f3();//=>'这是函数f3!'typeof(f3);//=>'函数'typeof(函数);//=>'function'其实是一个用来构造函数类型变量的类,或者说函数类型实例的构造函数(constructor);类似于一些Object或者String、Number等,都是Js内置类型实例的构造函数。比较特殊的是Object,用于生成对象类型,其简写形式为{}:varo1=newObject();typeof(o1);//=>'对象'varo2={};typeof(o2);//=>'object'typeof(Object);//=>'功能'2。prototypeVS__proto__理清以上概念再看原型:每个函数都有两个属性:length和prototypeprototype和length分别是函数类型有两个自带的属性,其他非函数类型则没有(例子中有说明)在开头)。原型属性://Nodeconsole.log(Object.prototype);//=>{}console.log(Function.prototype);//=>[函数:空]console.log(String.prototype);//=>[String:'']Js中所有对象(undefined、null等特殊情况除外)除了prototype外,都有一个内置的[[Prototype]]属性,指向其“父类”的原型",这个内置属性在ECMA标准中并没有给出明确的获取方式,但是很多Js实现(比如Node,大部分浏览器等)都提供了一个__proto__属性来引用这个[[Prototype]],我们通过下面的Example来说明实例中的__proto__是如何指向构造函数的原型的:varPerson=function(){};Person.prototype.type='Person';Person.prototype.maxAge=100;varp=newPerson();console.log(p.maxAge);p.name='rainy';Person.prototype.constructor===人;//=>truep.__proto__===Person.prototype;//=>trueconsole.log(p.prototype);//=>undefined上面的代码示例可以用下图来解释:Person是一个函数类型的变量,所以它自带了一个prototype属性,prototype属性中的constructor指向Person本身;通过new关键字生成的Person类的实例p1通过__proto__属性指向Person的原型。这里的__proto__只是为了说明实例p1在内部实现时与父类的关系(指向父类的原型)。在实际运行过程中,实例可以通过.直接获取父类原型中的属性,从而实现继承的功能。3.原型链弄清了原型和__proto__的概念和关系之后,我们对“Js中的一切都是对象”这句话会有更深的理解。那么我们会想,既然__proto__是(几乎)所有对象的内置属性,指向父类的原型,那是不是意味着我们可以“逆流而上”找源头呢?让我们看下面的例子://NodevarObj=function(){};varo=newObj();o.__proto__===Obj.prototype;//=>trueo.__proto__.constructor===Obj;//=>trueObj.__proto__===Function.prototype;//=>trueObj.__proto__.constructor===函数;//=>trueFunction.__proto__===Function.prototype;//=>trueObject.__proto__===Object.prototype;//=>falseObject.__proto__===Function.prototype;//=>trueFunction.__proto__.constructor===函数;//=>trueFunction.__proto__.__proto__;//=>{}函数.__proto__.__proto__===o.__proto__.__proto__;//=>trueo.__proto__.__proto__.__proto__===null;//=>true从上面的例子和图可以看出,原型对象还有一个__proto__属性,可以向上追溯到null。new关键字的作用是完成上图所示实例与父类原型的连接,创建一个新的对象;从上图也可以看出instanceof关键字的作用,其实就是判断__proto__(而__proto__.__proto__...)是父类的原型:varObj=function(){};varo=newObj();oinstanceofObj;//=>trueoinstanceof对象;//=>trueoinstanceof函数;//=>falseo.__proto__===Obj.prototype;//=>trueo.__proto__.__proto__===Object.prototype;//=>trueo.__proto__.__proto__===函数;//=>错误