ES5和ES6的类原理
ES5的类原理
1.首先看下面实例
function Father(name,age) {
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
Father.prototype.getAge = function() {
console.log("age:"+this.age);
}
var father = new Father("testFather", 30);//new出新的对象
console.log(father);
运行结果图:
实例分析:
1 创建了一个构造函数Father,此构造函数相当于"类",并定义了类的两个属性(name和age)和一个方法(getName)。
2 Father.prototype对象定义了一个方法(getAge)。
3 用new关键字实例化一个对象father。
4 对象直接访问getAge访问不到时,会访问Father.prototype的getAge方法。(_proto_原型链)
下面将对father对象结构进行分析,如果对js的原型链不太熟悉的朋友可以先学习深入理解javascript原型和闭包(3)——prototype原型
father对象的结构分析:
1 new关键字在创建对象时,会先创建一个father的简单对象,构造函数的this会指向这个对象;构造函数执行完毕后,father对象会有两个属性和一个方法;
2 father对象是由Father构造函数创建出来的,所以它的_proto_属性指向
Father.prototype对象;
3 Father.prototype._proto_是由Object创建出来的,所以Father.prototype._proto_属性指向Object.prototype对象;
4 当访问father.constructor访问不到时,会访问Father.prototype.constructor。
console.log(father.__proto__ ==== Father.prototype);//true
console.log((Father.prototype.__proto__) === Object.prototype);//true
console.log(father.constructor=== Father);//true
console.log(father.constructor=== Father.prototype.constructor);//true
原型链图:
ES6的类(Class)原理
1.首先看下面实例
class Father {
constructor(name, age){
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
getAge(){
console.log("age:"+this.age);
}
}
var father = new Father("testFather", 30);//new出新的对象
console.log(father);
运行结果图:
对比上面两张程序运行结果图发现,ES5和ES6的类实现都是建立在原型链的基础之上,他们的原型链结构几乎完全一样。
ES6的类(Class)继承
类继承原理:
ES6中提供了extends关键字实现类的继承,相比较ES5的原型继承,更方便易懂,extends在实现继承方面,本质上也是原型链继承,该方法实现了两步原型链继承。
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性(father.proto === Father.prototype)。
Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
- (1)子类的__proto__属性,表示构造函数的继承,总是指向父类,子类构造函数(Child)的原型(Child.proto)指向了父类构造函数(Father)。
- (2)子类prototype属性的__proto__属性(Child.prototype.proto),表示方法的继承,总是指向父类的prototype属性(Father.prototype)。
实现原理代码:
Child.prototype = Object.create(Father.prototype, {constructor:{value: Child}})
即将Child.prototype.__proto__ = Father.prototype
类继承实例:
class Father {
constructor(name, age){
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
getAge(){
console.log("age:"+this.age);
}
}
class Child extends Father {
constructor(name, age, sex){
super(name, age);
this.sex = sex;
}
}
var child = new Child("xiaoming", 10, "男");
console.log(child);
运行结果图:
ES6的类(Class)静态方法和方法的区别
ES6 中规定,Class 内部只有静态方法,没有静态属性。静态方法用static关键字修饰,用static关键字定义的方法属于类的方法,不属于对象的方法。
class Father {
constructor(name, age){
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
//方法加入了satic修饰
static getAge(){
console.log("age:"+this.age);
}
}
class Child extends Father {
constructor(name, age, sex){
super(name, age);
this.sex = sex;
}
}
var child = new Child("xiaoming", 10, "男");
console.log(child);
console.log(child.getAge);
console.log(Child.getAge);
console.log(Father.getAge);
运行结果图:
分析上面运行结果,得出结论:
1 静态方法是属于类级别的方法,只能被类直接访问。
2 父类的静态方法能够被子类直接继承。原因是上面说的两条继承链中的第一条(Child.proto === Father)。
ES6类的getter / setter方法
getter 不可单独出现,getter 与 setter 必须同级出现
class Father {
constructor(name, age){
this.name = name;
this.age = age;
}
//方法加入了satic修饰
get name(){
console.log('getter name');
return this._name;
}
set name(_name){
console.log('setter name');
this._name = _name;
}
get age(){
console.log('getter age');
return this._age;
}
set age(_age){
console.log('setter age');
this._age = _age;
}
}
class Child extends Father {
constructor(name, age, sex){
super(name, age);
this.sex = sex;
}
get sex(){
console.log('getter sex');
return this._sex;
}
set sex(_sex){
console.log('setter sex');
this._sex = _sex;
}
}
var child = new Child("xiaoming", 10, "男");
console.log(child);
运行结果图:
分析例子运行结果可以发现:
1 构造函数给属性赋值时,会调用属性的set函数;运行结果中起始三行打印了set函数调用的日志;
2 对象可以通过_name、_age和_sex间接访问对象属性;
3 对象直接访问name、age和sex属性时,会调用get函数;运行解脱最后面四行打印了get函数调用日志;
ES6类的super关键字
子类的构造函数constructor中super方法实现对父类构造函数的调用
注意事项:
1 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
class Father {
constructor() {}
}
class Child extends Father {
constructor() {}
// or
// constructor(a) {
// this.a = a;
// super();
// }
}
let test = new Child(); // Uncaught ReferenceError: Must call super
// constructor in derived class before accessing 'this' or returning
// from derived constructor
2 调用父类构造函数,必须出现在子类的构造函数。
class Father {
test(){
return 0;
}
static test1(){
return 1;
}
}
class Child extends Father {
constructor(){
super();
}
}
class Child1 extends Father {
test2() {
super(); // Uncaught SyntaxError: 'super' keyword unexpected
// here
}
}
3 子类方法(非静态的方法)中可以通过super调用父类的方法(非静态方法)
class Father {
constructor(name, age){
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
//方法加入了非satic修饰
getAge(){
console.log("Father getAge");
console.log("age:"+this.age);
}
}
class Child extends Father {
constructor(name, age, sex){
super(name, age);
this.sex = sex;
}
getAge(){
console.log("child getAge");
super.getAge();
}
}
var child = new Child("xiaoming", 10, "男");
console.log(child);
child.getAge();
运行结果图:
分析运行结果发现:
super其实等价于Father.prototype即Father.prototype.call(this),所以通过super调用getAge方法时,方法中的this执行了child对象,返回对象的age属性;
4 子类静态方法中可以通过super调用父类静态方法
此时super等价于Father,而不是Father.prototype
class Father {
constructor(name, age){
this.name = name;
this.age = age;
this.getName = function(){
console.log("name:"+this.name);
}
}
//方法加入了satic修饰
static getAge(){
console.log("Father getAge");
console.log("age:"+this.age);
}
}
class Child extends Father {
constructor(name, age, sex){
super(name, age);
this.sex = sex;
}
static getAge(){
console.log("child getAge");
super.getAge();
}
}
var child = new Child("xiaoming", 10, "男");
console.log(child);
Child.getAge();
console.log("给Father类赋值");
Father.age = 20;
Child.getAge();
运行结果图:
分析例子运行结果发现:
Father静态方法中的this,指向的是Father。