从网上找了一张图,比较清楚的描述了原型链
![](https://img.haomeiwen.com/i11544508/8c66066bd3840612.png)
原型链
1.原型链的定义
每个对象都可以有一个原型proto,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找...... 这个操作被委托在整个原型链上,这个就是我们说的原型链了。
2.原型指针
接下来我们先理解以下这三个属性prototype、__proto__和constructor
-
prototype:
prototype属性,它是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象; 这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象); -
__proto__:
__proto__ 是原型链查询中实际用到的,它总是指向 prototype,换句话说就是指向构造函数的原型对象,它是对象独有的。注意,为什么Foo构造也有这个属性呢,因为在js的宇宙里万物皆对象,包括函数; -
constructor:
每个函数都有一个原型对象,该原型对象有一个constructor属性,指向创建对象的函数本身(构造函数)。所有的实例对象都可以访问constructor属性,constructor属性是创建实例对象的函数的引用。
prototype 是函数所独有的,在定义构造函数时自动创建,它总是被 __proto__ 所指。所有对象都有__proto__属性,函数这个特殊对象除了具有__proto__属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和__proto__属性。prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。
class继承
1.基本用法
传统方法
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
es6
class Point {
constructor(x, y) {
// constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加
// this默认指向类的实例
this.x = x;
this.y = y;
}
static classMethod() {
// 静态方法,可以直接在Point类上调用(Point.classMethod()),而不是在Point类的实例上调用
// 这里的this指的是类,而不是实例
return 'hello';
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
// x和y都是实例对象point自身的属性
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
// toString是原型对象的属性
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
上面代码中,ES6语法中,toString方法是Point类内部定义的方法,它是不可枚举的。采用 ES5 的写法,toString方法就是可枚举的。
2.class的继承
class Point {
constructor(x, y) {
this.color = 'red';
this.x = 1;
console.log(x,y)
}
toString(){
return 'Point + ' + this.color
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
// super()在这里相当于ColorPoint.prototype.constructor.call(this,x,y)。
super(x, y); // 调用父类的constructor(x, y)
this.color = 'yellow';
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
// 由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
// 上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是Point.prototype.x,所以返回undefined。
}
toString() {
// 在这里调用super()会报错
console.log('ColorPoint + ' + super.toString()) // ColorPoint +Point + yellow
}
}
let a = new ColorPoint();
a.toString(); // ColorPoint + Point + yellow
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
- 当super作为函数时:super虽然代表了父类Point的构造函数,但是返回的是子类ColorPoint的实例,即super内部的this指的是ColorPoint的实例,因此super()在这里相当于Point.prototype.constructor.call(this)。作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
- 当super作为对象时:在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
注意:
当super作为对象时,由于super指向父类的原型对象,所以定义在父类
(constructor)实例上的方法或属性,是无法通过super调用的。
上面代码中,super.toString()虽然调用的是Point.prototype.toString(),但是Point.prototype.toString()内部的this指向子类ColorPoint的实例,导致输出的是yellow,而不是red。也就是说,实际上执行的是super.toString.call(this)
3.类的 prototype 属性和__proto__属性
Class 作为构造函数的语法糖,同时有prototype属性和proto属性,因此同时存在两条继承链
-
子类的proto属性,表示构造函数的继承,总是指向父类。
-
子类prototype属性的proto属性,表示方法的继承,总是指向父类的prototype属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
网友评论