在JavaScript中,原型这个东西是比较复杂的,也是比较绕的一个知识点。稍有不慎就会把自己搞的晕头转向。要讲原型,就得先从JavaScript的类(构造函数)和类的实例开始了解,类的所有实例都从同一个原型对象上继承属性,原型对象是指每一个类都有一个prototype属性,prototype对象里面有个constructor属性,这个constructor指向当前的类(构造函数)。
因此,原型对象是类的核心。下面我们来看看代码
function Person(){ } // 构造函数
Person.prototype.name = "JavaScript权威指南";
Person.prototype.age = 5;
var person = new Person();
console.log(person.name); // JavaScript权威指南
console.log(person.age); // 5
其中上面的Person 是一个类(构造函数),prototype是Person的原型对象。下面我们来深度剖析一下类和类的实例。首先我们知道类有prototype属性,那么这个prototype属性是怎么在实例中反应出来的呢,为什么我们在类的prototype中定义了方法,实例却可以调用。这个是因为在new的时候做了如下几件事。
(1) 创建一个新对象
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
(3) 执行构造函数中的代码(为这个新对象添加属性)
(4) 返回新对象
转换成为代码就是
function create(clazz,args){// 类和 创建实例所需要的参数
var obj = { } ;//(1) 创建一个新对象
Person.apply(obj,args) ; // (2)将构造函数的作用域赋给新对象,(3)执行构造函数中的代码
obj.__proto__ = Person.prototype ; // 类的prototype 赋值给 __proto__
return obj ;
}
var person = create( Person );
console.log(person.name); // JavaScript权威指南
console.log(person.age); // 5
从上面的代码我们就可以清晰的看到了类的prototype和实例关系,实际上就是把类的prototype对象赋值给了实例的__proto__属性,实例属性__proto__引用了类的prototype。在这儿,我们称类的prototype为原型,实例的__proto__用来形成原型链。那么现在你知道实例的__proto__ 属性的constructor指向的是谁吗?没错,指向的就是Person,因为__proto__只是类Person.prototype的一个引用。当上面我们通过是实例.属性名称的时候,js引擎会在当前的实例中找这个属性,如果没找到则会去__proto__中找,所以对象本身的属性或方法的优先级要高于原型的属性或方法的。
默认的原型对象中包含了constructor属性,通过该属性我们可以确认实例是由哪一个构造函数实例化而来的。
我们一般可以通过instanceof关键字来判断对象是不是某一个类的实例。
function Person(){ } // 构造函数
Person.prototype.name = "JavaScript权威指南";
console.log(Person.prototype.constructor); // Person ,这里需要注意的是这里是隐式使用Person自带的prototype,没有显示赋值
var person = new Person();
console.log( person instanceof Person ) //输出 true
原型和原型链还有很多的知识点,后续会继续研究的。