前边写过一篇文章叫做《JavaScript面向对象继承》,里边提到原型的知识点,通常原型和继承都是放在一块儿说的,所以这篇就对原型和原型链做一个全面说明。
构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name)
};
};
let p1 = new Person('gjr', 16);
let p2 = new Person('kris', 18);
如上例所示,p1和p2都是Person的实例。这两个实例都有一个constructor属性,该属性指向Person。也就是:
console.log(p1.constructor == Person); // true
console.log(p2.constructor == Person); // true
所以记住:
- p1 和 p2 都是new出来的构造函数Person的实例
- 实例的构造函数属性(constructor)指向构造函数本身
原型对象(prototype)
每一个函数都有一个prototype属性,此属性指向函数的原型对象。
每一个JS对象(除null外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象也都会从原型继承属性。
function Person() {
this.age = 18;
};
Person.prototype.name = 'gjr';
Person.prototype.sayName = function() {
console.log(this.name)
};
let p1 = new Person();
let p2 = new Person();
p1.sayName(); // gjr
p2.sayName(); // gjr
console.log(p1.sayName == p2.sayName); // true
要记住:prototype是函数才会有的属性。
上边Person除了name、age、sayName四个属性外,其实它还有一个默认的constructor的属性。所有原型对象(prototype)都会有一个constructor(构造函数)属性,这个属性指向prototype属性所在的函数本身。用Person这个栗子来简单总结一下就是:
Person.prototype.constructor == Person;
上边的构造函数章节里我们也提到p1.constructor == Person,p1是Person的实例,那同理这里也可以看做Person.prototype也是Person的实例。
就例总结一下:
- 原型对象(Person.prototype)是构造函数(Person)的一个实例
- 除Function.prototype函数对象以外的原型对象其实都是普通对象,因为Function.prototype对象没有prototype属性,其他的都有
普通对象与函数对象
凡是通过new Function() 方式创建的对象都是函数对象,其他的都是普通对象。
原型指针(__proto__)
JS在创建对象(无论是普通还是函数对象,除null以外)的时候,都会有一个__proto__的内置属性,叫做原型指针,用于指向创建它的构造函数的原型对象。
也就是:p1.__proto__ == Person.prototype
Person.prototype.constructor === Person;
p1.__proto__ === Person.prototype;
p1.constructor === Person;
从上述关系中需要明白:__proto__的连接是实例和原型对象prototype之间的连接,不存在于实例与构造函数之间。
看个栗子:
function Person() {};
Person.prototype.name = 'gjr';
let p1 = new Person();
p1.name = 'kris';
let p2 = new Person();
console.log(p1.name); // kris
console.log(p2.name); // gjr
delete p1.name;
console.log(p1.name); // gjr
当我们给实例p1添加了自己的name属性,那打印p1.name的时候自然是找自己的私有属性,打印出了kris。而p2没有添加自己的私有name,所以就通过原型指针__proto__,也就是prototype找到了公有属性name,打印出了gjr。
当删除p1的name时,读取了p1的私有name给删除掉了,当我们再打印p1.name时,查找name就和p2的方式一样了,最后也打印出了gjr。
注意:__proto__最初是一个非标准属性,ES6已将其标准化,可用标准Object.getPrototypeOf() 代替。
原型链
前边我们总提到'除null以外',是因为:
- null处于原型链的顶端,没有__proto__的属性。
- Object.prototype的__proto__属性为null。
就像人类进化史,从现在的人类倒推到原始的类人猿,那类人猿从何而来呢?一直追溯下去的时候就是无。所以原型也是,一直追溯下去就是null了。"原型始于NULL"这个特点只需记住就可以了。
疑点解释:
Object.__proto__ === Function.prototype
解释:Object是函数对象,是通过new Function() 创建的,所以Object.__proto__指向Funtion.prototype
Function.__proto__ === Function.prototype
解释:Funtion 也是函数对象,通过new Function创建的,所以Function.__proto__指向Function.prototype。
Funtion.prototype.__proto__ === Object.prototype
按道理Funtion是函数对象,Function.__proto__指向Function.prototype。但是我们都知道一句话叫“万物皆对象”,所以Function.prototype.__proto__指向了Object.prototype,然后Object.prototype.__proto__为null,原型链结束。
附加:
Comments | NOTHING