原型与原型链
条评论 帮助理解前端常考知识点原型与原型链,介绍他们的相关概念以及他们是如何指向的。
基本概念
显示原型:prototype
- 每个class/函数都有显示原型prototype,它默认指向一个Object空对象(即称为原型对象)
- 原型对象中一个属性constructor,它指向函数对象
隐式原型:proto
- 每个实例都有隐式原型__proto__
- 实例的__proto__指向对应的class/函数的prototype。
相关概念:
- Function是所有函数(function)的父亲,所有函数都是它的实例。
- Object也是一个函数,所以Object是Function的实例对象。
- 任何对象的原型链顶端最终都指向Object.prototype,Object.prototype再往上已经没有东西了,所以指向null。
- Function比较特殊它的原型指向自己,即Function.proto === Function.prototype。
- 同时,Function是对象,由(3)可知Function必然有一个地方(即Function.prototype.__proto__)指向Object.prototype。
先来看一个简化版的原型链图:
再来看一个完整版的:
总之,记住一句话:A是B的实例,则A.__ proto __ === B.prototype;只要A是对象,那么A.prototype.__ proto__ 就指向Object.prototype;prototype中的constructor指向的是自己;
原型链与函数的继承
ES5中的继承
构造函数继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function Parent() {
this.name = 'parent';
}
Parent.prototype.say = function () {
console.log('say');
};
function Child(age) {
Parent.call(this);
this.age = age;
}
var p = new Parent(); //new一个Parent对象用来对比
p.say(); //输出say
var c = new Child(12);
c.age // 12
c.name //'parent'
c.say(); //undifined
/*
say是Parent原型链上的方法,Parent对象调用方法时,如果自身不存在就回去原型链上寻找,在原型链上找到了say方法,而Child对象没有继承Parent对象的原型链,所以它在向上寻找时就找不到,输出undifined。
*/原型链继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function Parent() {
this.name = 'parent';
}
Parent.prototype.say = function () {
console.log('say');
};
function Child(age) {
this.age = age;
}
Child.prototype = new Parent();
var c = new Child(12);
console.log(c.name); //输出parent
c.say() //输出say
/*
原型链继承是直接让Child构造函数的prototype直接指向Parent对象,这样Parent的东西Child对象可以直接从它的原型链上找到。缺点就是:当创建多个实例时,如果不同实例可能互相存在影响。
*/构造函数与原型链组合继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function Parent() {
this.name = 'parent';
this.arr = [1,2,3,4]
}
Parent.prototype.say = function () {
console.log('say');
};
function Child(age) {
Parent.call(this);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var c1 = new Child(12);
var c2 = new Child(12);
console.log(c1.arr); //[1,2,3,4]
console.log(c2.arr);//[1,2,3,4]
c1.arr.push(5);
console.log(c1.arr); //[1,2,3,4,5]
console.log(c2.arr); //[1,2,3,4]ES6中的继承
ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法,才可使用this关键字,否则报错。),然后再用子类的构造函数修改this实现继承。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Parent2 {
constructor() {
this.name = 'parent';
}
}
Parent2.prototype.say = function () {
console.log('say');
};
class Child2 extends Parent {
constructor(age) {
super();
this.age = age;
}
}
var c2 = new Child2(12);
console.log(c2.name); //输出parent
c2.say(); //输出say
console.log(c.constructor);
//输出function Child(age) {Parent.call(this);this.age = age;}
console.log(new Parent().constructor);
//输出Parent() {this.name = 'parent';this.arr = [1,2,3,4];}