什么是原型
在js中,每一个函数都拥有一个特殊的属性,这个属性叫做原型(prototype),这个属性其实是一个指针,指向一个对象,这个属性只有函数才会拥有。
什么是原型链?
Js中几乎所有的对象都是Object的实例,JS被称为基于原型的语言,每一个对象都有一个原型对象(prototype),对象以这个原型为模板从原型中继承属性和方法。原型对象也有自己的原型,并从中继承属性和方法,以此类推,这种关系被称为原型链。这就是为什么一个对象可以拥有定义在其他对象中的属性和方法。其实这些属性和方法都是定义在Object的构造器函数之上的prototype属性上的,而非对象实例本身。
在传统的面向对象编程中,是靠类来实现继承,首先定义一个类,然后为类创建一个实例,把类的属性和方法复制到实例上,但是JS不是直接复制的,js是在对象实例和它的构造器之间建立一个连接(_proto_属性,是从构造函数的prototype属性派生出来的),之后通过上溯原型链,在构造器中找到属性和方法。
function Person() {
this.name='zs';
this.age='23';
}
let p1 = new Person();
p1.__ proto __ 属性其实就是Person.prototype,当访问p1的一个属性或者方法时,浏览器会首先查找p1是否有这个属性,如果没有,则会向p1.__ proto __ 中查找这个属性,如果还是没有,则继续向p1.__ proto __ . __ proto__ 中寻找。默认情况下,所有函数的原型属性的__proto__都是Object.prototype。所以一直查找到p1. __ proto__ . __ proto__ 为止,最后,原型链上面的所有的__proto__都被查找完了,没有找到这个属性,则会返回undefined
例如我们调用p1.valueOf()方法时,会发生什么?
- 首先检查p1对象上有没有valueOf这个方法
- 如果没有,会检查p1的原型对象(p1. __ proto __ ),也就是构造函数的prototype属性所指的原型对象(Person.prototype)中是否有valueOf方法
- 如果也没有,会接着检查构造函数的prototype对象的原型对象(也就是Object构造函数的prototype属性所指向的对象)是否具有这个方法,这里有个方法,所以就被调用了
当创建一个函数之后,其实只为它的原型对象增加了一个constructor属性,其他的属性都是从Objec.prototype属性中继承过来的。
function Person(){}
Person.prototype.name='Nicholas';
Person.prototype.age=29;
Person.prototype.job='Software Enngineer';
Person.prototype.sayName=function(){}
下图是不同情况下实例和原型的关系,省略了与Person构造函数的关系
注意以下写法会打破实例与原型的对应关系
function Person() {}
Person.prototype={
name: 'Nicholas',
age:29,
job:'Software Enngineer',
sayName:function() {
console.log(this.name);
}
};
上面的代码中,Person的原型对象是等于一个以字面量形式创建的新对象。但是有个问题,我们之前说过,每创建一个函数,就会同时创建它的prototype对象,这个对象自动获得了一个constructor属性,而在我们使用的语法中,完全重写了默认的prototype对象,所以,constructor属性也就变成了新的对象的constructor属性(指向Object构造函数),不再指向Person构造函数。此时,尽管instanceof操作符还能返回正确的结果,但是已经无法通过constructor属性确定对象的类型了,如下所示 instanceof 用来测试一个对象是否是一个构造函数的实例
let person1 =new Person();
console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(person1.constructor === Person); // false
console.log(person1.constructor === Object); // true
instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
可以看出用instanceof 操作符测试Object和Person仍然返回true,但是constructor属性则是Object而不是Person了。我们可以像下面这样特意指定constructor的属性的值
function Person() {}
Person.prototype={
constructor: Person,
name: 'Nicholas',
age:29,
job:'Software Enngineer',
sayName:function() {
console.log(this.name);
}
};
let person1 =new Person();
console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(person1.constructor === Person); // true
console.log(person1.constructor === Object); // false