一、初识Class
1.1 Class是什么
传统面向对象的编程序语言都是【类】的概念,对象都是由类创建出来,而早期 JavaScript 中是没有类的,面向对象大多都是基于构造函数和原型实现的,但是 ECMAScript 6 规范开始增加了【类】相关的语法,使得 JavaScript 中的面向对象实现方式更加标准
a.认识Class
类可以看做是对象的模板,用一个类可以创建出许多不同的对象
1.2 Class的基本用法
类名一般首字母大写
语法:class 类名 {}
// 创建类
class Person{
// 此处编写封装逻辑
}
class Person {}
// 实例化类
let re = new Person();
console.log(re);
let o = new Person();
console.log(o);
class Dog {}
let d = new Dog();
示例:
<script>
class Person {
// 实例化时执行构造方法,所以必须有构造方法即constructor,但是可以不写出来
// 如果我们不写 constructor ,那么会自动的帮我们创建这个构造方法
constructor(name, age) {
// console.log('实例化时执行构造方法');
//this 代表实例对象,上面定义的是实例属性/方法
this.name = name;
this.age = age;
// 一般在构造方法中定义属性,方法不在构造方法中定义
// this.speak = () => {};
}
// 各实例共享的方法
speak() {
console.log('speak');
}
}
const zs = new Person('ZS', 18);
const ls = new Person('LS', 28);
console.log(zs.name);
console.log(zs.age);
console.log(zs.speak);
zs.speak();
console.log(ls.name);
console.log(ls.age);
console.log(ls.speak);
console.log(zs.speak === ls.speak);
</script>
总结:
-
关键字
class
封装了所有的实例属性和方法 -
类中封装的并不是变量和函数,因此不能使用关键字
let
、const
或var
1.3 constructor()
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法
一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加
class Point {
}
// 等同于
class Point {
constructor() {}
}
constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
<script>
class Person {
// 实例化时 立即执行
// 构造函数、构造方法、构造器
constructor (name, age) {
this.name = name;
this.age = age;
}
// 实例方法
walk () {
console.log(this.name + '正在走路...');
}
}
// 实例化
let p1 = new Person('小明', 18);
p1.walk();
</script>
总结:
-
constructor
是类中固定的方法名 -
constructor
方法在实例化时立即执行 -
constructor
方法接收实例化时传入的参数 -
constructor
并非是类中必须要存在的方法
1.4 Class与构造函数
类:
//类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
// this.speak = () => {};
}
speak() {
console.log('speak');
}
run() {}
}
//类也可以在其圆形上声明方法(不推荐这种写法)
Person.prototype.run = function() {};
console.log(typeof Person);
console.log(Person.prototype.speak); //类也可以在其圆形上找到该方法
构造函数:
<script>
//构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
//构造函数的方法一般写在其原型上
Person.prototype.speak = function() {};
</script>
1.5 Class的两种定义形式
a.声明形式
class Person {
constructor() {}
speak() {}
}
b.表达式形式
const Person = class {
constructor() {
console.log('constructor');
}
speak() {}
};
new Person();
c.立即执行的匿名类
new(class {
constructor() {
console.log('constructor');
}
})();
1.6 取值函数(getter)和存值函数(setter)
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: ' + value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'gette
二、Class的属性和方法
2.1 实例属性和实例方法、静态属性和静态方法
a.实例属性
方法就是值为函数的特殊属性
class Person {
//设置默认属性
age = 14;
name = 'zhangsan';
sex = 'male';
//实例方法
getSex = function() {
return this.sex;
};
//在这里可以对默认属性进行修改
constructor(name, sex) {
this.name = name;
this.age = 18;
this.sex = sex;
}
// speak() {
// this.age = 18;
// }
}
const p = new Person('Alex');
console.log(p.name);
console.log(p.age);
b.静态方法和静态属性
类相当于实例的原型,所有在类中定义的方法,都会被实例继承
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性
<script>
class Person {
// 实例属性
constructor(name) {
this.name = name;
}
// 不要这么写,目前只是提案,有兼容性问题
// static version = '1.0';
//静态方法 写法1 --推荐
static getVersion() {
//推荐将静态属性写成这种“静态方法”的形式
return '1.0';
}
//实例方法--实例对象的方法
speak() {
console.log('speak');
// 实例对象中的this指向实例对象
console.log(this);
}
}
//静态方法 写法2--不推荐
// Person.getVersion = function() {
// return '1.0';
// }
//1
// Person.version = '1.0'; //静态属性写法,但是不推荐,没得必要写在类的外面
// console.log(Person.version);
//2 静态属性,必须使用new进行实例化后再调用
const p = new Person('Alex');
p.speak(); //调用实例对象的方法
console.log(p.name); //Alex
//3.静态方法可以不使用new 进行实例化即可调用
console.log(Person.getVersion()); //1.0
</script>
总结:
-
static
关键字用于声明静态属性和方法 -
静态属性和方法直接通过类名进行访问
2.2 私有属性和方法
a.为什么需要私有属性和方法
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问
一般情况下,类的属性和方法都是公开的
公有的属性和方法可以被外界修改,造成意想不到的错误
示例:
class Person {
//实例属性
constructor(name) {
this.name = name;
}
//实例方法
speak() {
console.log('speak');
}
// 实例方法
getName() {
return this.name;
}
}
//实例化属性
const p = new Person('Alex');
console.log(p.name);
p.speak();
// ....
p.name = 'zs';
console.log(p.name);
b.模拟私有属性和方法
1._ 开头表示私有(不具备很强的约束力)
class Person {
constructor(name) {
this._name = name;
}
speak() {
console.log('speak');
}
getName() {
return this._name;
}
}
const p = new Person('Alex');
console.log(p.name);
p.name = 'zd';
console.log(p.getName());
2.将私有属性和方法移出类(具有很强的约束力)
<script>
(function() {
let name = '';
class Person {
constructor(username) {
// this.name = name;
name = username;
}
speak() {
console.log('speak');
}
getName() {
return name;
}
}
window.Person = Person;
})();
(function() {
const p = new Person('Alex');
console.log(p.name);
console.log(p.getName());
})();
</script>
三、Class的继承
一种简单的继承:
//一种简单的继承,通过拷贝其原型来实现
var lynkCoProtype = {
model: "领克",
getModel: function() {
console.log("车辆模具是:" + this.model);
}
};
var volvo = Object.create(lynkCoProtype, {
model: {
value: "沃尔沃"
}
});
volvo.getModel();
3.1 extends
子类继承父类
<script>
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
this.say = function() {
console.log('say');
};
}
speak() {
console.log('speak');
}
static speak() {
console.log('static speak');
}
}
Person.version = '1.0';
class Programmer extends Person {
constructor(name, sex) {
super(name, sex); //子类中调用父类的构造方法利用super();调用父类的constructor()
}
}
const zs = new Programmer('zs', '男');
console.log(zs.name);
console.log(zs.sex);
zs.say();
zs.speak();
Programmer.speak();
console.log(Programmer.version);
</script>
注意:
子类必须在constructor方法中调用super方法,否则新建实例时会报错
这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法
如果不调用super方法,子类就得不到this对象
在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错--这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例
改写继承的属性或方法
<script>
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
this.say = function() {
console.log('say');
};
}
speak() {
console.log('speak');
}
static speak() {
console.log('static speak');
}
}
Person.version = '1.0';
// 子类
class Programmer extends Person {
constructor(name, sex, feature) {
// this.feature = feature; ×
// this 操作不能放在 super 前面
super(name, sex);
this.feature = feature;
}
hi() {
console.log('hi');
}
// 同名覆盖
speak() {
console.log('Programmer speak');
}
static speak() {
console.log('Programmer static speak');
}
}
Programmer.version = '2.0';
const zs = new Programmer('zs', '男', '秃头');
console.log(zs.name);
console.log(zs.sex);
console.log(zs.feature);
zs.say();
zs.speak();
zs.hi();
Programmer.speak();
console.log(Programmer.version);
</script>
3.2 super
super这个关键字,既可以当作函数使用,也可以当作对象使用,就是用来调用父类的方法或属性
在继承的过程中子类中 constructor
中必须调 super
函数,否则会有语法错误
a.作为函数调用
代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错
super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例
<script>
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
}
// hi() {
// super(); // ×
// }
}
// new Person();
new Programmer();
</script>
b.作为对象使用
b.1.在构造方法中使用或一般方法中使用:
super 代表父类的原型对象 Person.prototype
所以定义在父类实例上的方法或属性,是无法通过 super 调用的
通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
speak() {
console.log('speak');
// console.log(this);
}
static speak() {
console.log('Person speak');
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
// console.log(super.name);
// super.speak();
}
// hi() {
// super(); // ×
// }
speak() {
super.speak();
console.log('Programmer speak');
}
}
new Programmer();
b.2.在静态方法中使用:
指向父类,而不是父类的原型对象
通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
speak() {
console.log('speak');
// console.log(this);
}
static speak() {
console.log('Person speak');
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
// console.log(super.name);
// super.speak();
}
// hi() {
// super(); // ×
// }
speak() {
super.speak();
console.log('Programmer speak');
}
static speak() {
super.speak();
console.log('Programmer speak');
}
}
// new Person();
// new Programmer();
Programmer.speak();
c.注意事项
使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错
<script>
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log('speak');
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
// console.log(super);
console.log(super()); //作为函数
// console.log(super.speak); //作为对象
}
}
</script>