在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
复制代码
原型和原型链还有很多的知识点,后续会继续研究的。