其实不知道该怎么总结class才是一针见血,就先根据自己的理解来写这篇笔记吧:
1,类class是ES6新增的一个语法,class是对象的模板,class比function用起来更接近于面向对象编程,实例属性和类原型属性都在class体内定义(当然,原型上属性还可以这样定义:someClass.prototype.propName = ''),但class的本质是function,class是function的一个语法糖,class的使用依然基于原型(prototype);怎么理解?那先看看class的语法,目前class的语法已被广泛使用,尤其是在三大前端框架中以及其他的一些库。
子类extends关键字后可以是另一个类作为父类,也可以是个普通函数(此时的普通函数就是一个类似constructor方法的构造函数),此时普通函数中的this指向是子类实例,同样在constructor方法调用super方法给父类传递数据;extends的行为相当于是将基类的实例属性和公共方法作为子类的实例属性和prototype的原型链上的方法;类的constructor可省略,那么若有基类,基类的constructor也会接收到相同的参数;用代码表示:
//class继承的基类是个函数,此时的函数相当于基类的constructor方法;---------
function BaseCL(firstName){
this.firstName = firstName;
}
BaseCL.prototype.sayHello = function(){
console.log('Hi' + this.firstName);
};
class Person extends BaseCL{
//省略constructor的显式调用;
}
//Person在继承一个基类时,Person.prototype.__proto__ === BaseCL.prototype;
//所以表面上 sayHello 就像是从Person.prototype copy过来的,但实际上
//是Person.prototype对象的原型链上的属性(方法)-------------------
console.log(BaseCL.prototype.sayHello === Person.prototype.sayHello );
//true
//在创建一个实例时,基类中的this指向是子类的实例;所以就能拿到基类中定义的实例属性;
//所以表面上看着,就像是从基类copy过来的实例属性;----------------------
var person = new Person('kaka');
console.log( person);
//Person {firstName: "kaka"}
// firstName: "kaka"
// __proto__: BaseCL
console.log(person.firstName);
//'kaka'
以下Code Snippets稍微展现了class类的语法和this的规则。
//1,首先,可以这样定义一个class,每个class仅有一个constructor构造方法,------------
//该构造方法可显式声明,也可以不声明,会隐式执行该构造函数,也会隐式返回实例
//class内的代码始终是strict mode的代码,所以不用在class内声明strict mode,
//不然'use strict'会被当作实例属性。
class Person{
name='unknown'; //实例属性,不同于方法,方法被挂在原型上,
//若想定义实例上的方法,可以在构造方法constructor内定义在this上。
age='unknown'; //实例属性
constructor(name, age, favorite){ //构造方法隐式返回类的实例
this.name = name || this.name;
this.age = age || this.age;
this.favorite = favorite || '睡觉'; //this.favorite 是实例属性
}
//存在某些class不需要显式声明构造方法constructor,可以考虑去掉上面的构造方法constructor
//实例化一个类时,会默认执行没有参数的constructor方法,也会隐式返回类的实例,像这样
//constructor(){
//
//}
//方法是原型对象上的属性,也可以说是公共方法,不同的实例访问到同一份原型对象上的方法
sleep(){
console.log('每天至少睡8个小时');
}
//方法是原型对象上的属性
eat(){
console.log('每日三餐,只能多,不能少');
}
//静态方法,所以不在原型对象上,而是通过类本身获取像Person.attribute
static attribute(){
console.log('会通过学习获取技能');
}
static group = '人类';
}
console.log(new Person());
//或者传入参数console.log(new Person('No.1', 9, '棉花糖', '故意传如的第四个参数'));
//为了展现类体内属性和方法的去向,以下尽量比较多列出实例的部分结构,
// Person {
// age: "unknown" //9
// favorite: "睡觉" //'棉花糖'
// name: "unknown" //'No.1'
// __proto__:{
// constructor: class Person
// eat: ƒ eat()
// sleep: ƒ sleep()
// __proto__: Object
// }
// }
console.log(Person.attribute);
//类体内定义的静态方法,像面向对象那样的静态方法的调用方式
//还有静态属性,也是这种调用方式:Person.group
// ƒ attribute(){
// console.log('会通过学习获取技能');
// }
console.log(Person.prototype);
//class就像function一样,也有原型对象prototype,
//实例的原型对象指向的都是class或function的prototype对象;
//就上上面的new Person实例的__proto__对象指向Person.prototype,
//或许这可以看出为什么说class的实质还是function,class依然基于原型,!!!---
//但是class跟function有不一样的地方,就是接下来要讲的class的继承(继承依然基于原型)。
// {
// constructor: class Person
// eat: ƒ eat()
// sleep: ƒ sleep()
// __proto__: Object
// }
//2,类的继承,使用关键字extends、super,一个类只能继承一个父类、只能有一个构造方法,----------
//不像面向对象语言一个类可以根据参数列表的长度有多个构造方法
//extends 表示继承,继承基于原型链,就像普通构造函数的继承:
//子类实例的原型对象是Object.create(('父类').prototype)+子类的原型对象+子类构造器;
/class的继承与构造函数的继承稍微有点不同,可以对比以下代码:类的继承和构造函数的继承。
//super在不同的位置,super的绑定是不一样,在静态方法中,super是父类本身;在原型方法中,!!!---
//super是父类的原型;在子类的构造方法中,必须先调用被当作父类的构造方法的super:super(),
//且在执行super之前,不能这样super[XX]引用super,也不能引用this,否则会报错;
//执行super()之后,super只能被做是父类的原型。(以上所说的类的原型,指的都是prototype);
//在类完成了定义,那么不同位置的super的绑定就固定了,不会因执行环境而改变。
//另外,super不被允许出现在class体外,否则会报错。
class Animal{
constructor(food, language){
this.food = food;
this.language = language;
}
sleep(){
console.log('need to sleep');
}
eat(){
console.log('need to eat:');
console.log(this.food);
}
say(){
console.log('has their own language:');
console.log(this.language);
}
static isAnimal = true;
}
class Dog extends Animal{
constructor(food, language){
super(food, language); //在执行这句之前,
//this是undefined,也就是说实例被创建,或者可以说当有父类时,实例优先在父类中创建。
//console.log(this);
this.food = this.food || '鱼';
this.language = this.language || '汪';
}
say(){
super.say();//Animal.prototype.say(),但不同的是用super调用,this指向实例
console.log('dog will say something with happily or sadly');
}
static isAnimal(){
console.log(super.isAnimal);//Animal.isAnimal
return super.isAnimal;
}
}
Dog.prototype; //是个Object.create(Animal.prototype)+Dog的原型对象+class Dog构造器
//Animal {constructor: ƒ, say: ƒ}
// constructor: class Dog
// say: ƒ say()
// __proto__:
// constructor: class Animal
// eat: ƒ eat()
// say: ƒ say()
// sleep: ƒ sleep()
// __proto__: Object
var dog = new Dog();
dog;//dog.__proto__ 指向Dog.prototype,这点跟构造函数的实例一样的实现
//Dog {food: "鱼", language: "汪"}
// food: "鱼"
// language: "汪"
// __proto__: Animal
// constructor: class Dog
// say: ƒ say()
// __proto__:
// constructor: class Animal
// eat: ƒ eat()
// say: ƒ say()
// sleep: ƒ sleep()
// __proto__: Object
Dog.isAnimal();
//true
dog.say();
//'has their own language:'
//'汪'
//'dog will say something with happily or sadly'
//以上代码换成构造函数的继承如下,构造函数不能使用super关键字--------------------
//构造函数的继承可能是如下实现,这跟类的继承稍有不同,看注释:
//可以用Object.create(Animal.prototype),使其跟类的继承一样的效果,看注释;
function Animal(){
//当构造Animal实例时,这里跟class Animal一样,food和language都会是实例的属性,
//但作为父类时,class Dog的实例在原型链上没有父类实例的属性,但这有。
this.food='unknown';
this.language='unknown';
}
Animal.prototype.sleep = function(){
console.log('need to sleep');
};
Animal.prototype.eat = function(){
console.log('need to eat:');
console.log(this.food);
};
Animal.prototype.say = function(){
console.log('has their own language:');
console.log(this.language);
};
Animal.isAnimal = true;
Dog.prototype = new Animal();//这样的实现,回使原型链上有Animal实例的属性。
//Dog.prototype = Object.create(Animal.prototype); //这样的继承就没有Animal实例的属性。
Dog.prototype.constrcutor = Dog;//!!!
function Dog(food, language){
console.log(this);//这里就能访问this了。
this.food = food||'鱼';
this.language = language||'汪';
}
Dog.prototype.say = function(){
this.__proto__.__proto__.say();//然而这样调用,原型链上的say方法的this不能指向实例。
//Animal.say.call(this); //或许可以用这句代替。
console.log('dog will say something with happily or sadly');
};
Dog.isAnimal = function(){
console.log(Animal.isAnimal);
return Animal.isAnimal; //显式引用Animal类本身。
};
Dog.prototype;
//Animal {food: "unknown", language: "unknown", constrcutor: ƒ, say: ƒ}
// constrcutor: ƒ Dog(food, language)
// food: "unknown"
// language: "unknown"
// say: ƒ ()
// __proto__:
// eat: ƒ ()
// say: ƒ ()
// sleep: ƒ ()
// constructor: ƒ Animal()
// __proto__: Object
var dog = new Dog();
dog;
//Dog {food: "鱼", language: "汪"}
// food: "鱼"
// language: "汪"
// __proto__: Animal
// constrcutor: ƒ Dog(food, language)
// food: "unknown"
// language: "unknown"
// say: ƒ ()
// __proto__:
// eat: ƒ ()
// say: ƒ ()
// sleep: ƒ ()
// constructor: ƒ Animal()
// __proto__: Object
Dog.isAnimal();
true;
dog.say();
//'has their own language:'
//undefined
//'dog will say something with happily or sadly'
//通过上面的例子,可以理解为什么说class是function的语法糖之类的,不过拥有了class的语法--------
//构建实例和继承就变得简单易懂了。
//3,class中的this -----------------
//class只能通过new操作符创建实例,直接className()会报错;
//或者引用类本身调用静态属性className.xx。
//class中的this取决于调用方式,父类中的方法也遵循同样的规则,
//使用super调用的方法的this是跟super被调用所处环境的this;
//以下示例来表现this的指向:
class Animal{
say(){
console.log(this.language);
}
static isAnimal(){
console.log(this.isAnimal, 'which animal:' + this.name);
}
}
class Dog extends Animal{
constructor(language){
super();
this.language = language || '汪'
}
say(){
console.log('Say to you:');
super.say();
}
say1(){
return super.say;
}
static isAnimal = true;
static assertAnimal(){
super.isAnimal();
}
}
Dog.assertAnimal(); //this指向类本身class Dog,用super调用的方法中,this指向的也是Dog本身--
//true 'which animal:Dog'
var assertAnimal = Dog.assertAnimal;
assertAnimal();//报错,this的值是undefined
//TypeError: Cannot read property 'isAnimal' of undefined
var dog = new Dog();
dog.say();//this指向实例本身dog,用super调用的方法的this的也是实例dog本身
//'say to you:'
//'汪'
var say = dog.say;
say();//this的值是undefined。类本身是严格模式。super不管执行环境,super的值都是父类的prototype
//'say to you:'
//TypeError: Cannot read property 'language' of undefined
var say1 = dog.say1(); //返回super.say;
say1();//报错
//TypeError: Cannot read property 'language' of undefined
//类中this的规则跟构造函数中的this的规则是一样的。 -------------------
2,其他文档
3,参考文档:想知道更多,可以点击以下链接