主要内容:类成员与类继承
- 类表达式:其中先有一个构造函数,然后再有方法返回
- 类的构造方法
- 类实例属性和原型属性
- 存取属性 / 存取属性实现数据的双向绑定
- 静态成员
- 类中的私有成员(私有属性、私有方法)
- 类的继承:原生及es6中的class
1. 类表达式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类表达式</title>
</head>
<body>
<script>
// 类表达式
let Person = class {
constructor(name) {
this.name = name;
}
sayName() {
return this.name;
}
};
let person = new Person("小丽");
console.log(person.sayName());
// 类做为参数使用
let createObj = function (className, ...args) {
return new className(...args);
};
let obj = createObj(
class {
hello() {
return "hello JS";
}
}
);
console.log(obj.hello());
// 立即实例化一个类表达式, 创建一个单例
// 通常的格式是两个括号:(函数/类声明)(调用参数列表),如果要创建就在前面加个new,否则直接调用就行了。
let user = new (class {
constructor(email) {
this.email = email;
}
getEmail() {
return this.email;
}
})("peter@qq.com");
console.log(user.getEmail());
</script>
</body>
</html>
2. 类的构造方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类的构造方法</title>
</head>
<body>
<script>
class User {
constructor() {
// return this;
// return {};
// __proto__
return Object.create(null);
//这种情况才能产生出真正的空的类。
}
}
const user = new User();
console.log(user);
</script>
</body>
</html>
3. 类实例属性和原型属性
- 实例属性/方法: 直接绑定到实例上面
- 原型属性/方法:proto中的东东(o)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类实例属性和原型属性</title>
</head>
<body>
<script>
class User {
constructor(id, name) {
// 实例属性: 直接绑定到实例上面
this.id = id;
this.name = name;
// 实例方法:绑定到实例上的方法
this.getName = () => console.log("hello ", this.name);
}
// 原型方法
show() {
console.log(this.id, this.name);
}
}
const user = new User(1, "admin");
console.log(user);
console.log(user.show());
</script>
</body>
</html>
4. 存取属性
- 在User.prototype原型对象上设置”存取器属性”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>存取属性</title>
</head>
<body>
<script>
function User(age) {
this.age = age;
}
// 公共方法或属性定义在函数的原型对象上
// 在User.prototype原型对象上设置"存取器属性"
Object.defineProperty(User.prototype, "verifyAge", {
get() {
return this.age;
},
set(value) {
if (value >= 18 && value <= 60) this.age = value;
},
configurable: true, //说明这个属性是可以删除的。
enumerable: true, //这样就可以遍历了。
});
let user = new User(30);
console.log(user);
console.log(user.age);
console.log(user.verifyAge);
user.verifyAge = 40;
user.age = 50;
user.verifyAge = 80;
console.log(user.age);
// 对象的原型永远等于它的构造函数的原型对象,对象上从它的构造函数的原型对象上继承成员(属性和方法)
// console.log(user.__proto__ === User.prototype);
// console.log(user.constructor);
// console.log(user.__proto__ === user.constructor.prototype);
</script>
</body>
</html>
5. 存取属性实现数据的双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>存取属性实现数据的双向绑定</title>
</head>
<body>
<input type="text" />
<p></p>
<script>
// 最终的结果是实现input中录入,下面同步将对应信息展示出来
const obj = {};
Object.defineProperty(obj, "hello", {
set(value) {
document.querySelector("input").value = value;
document.querySelector("p").innerHTML = value;
// p就是下面的展示行
},
});
document.addEventListener("input", (ev) => (obj.hello = ev.target.value));
// 这个地方监听input,keyup都可以。
</script>
</body>
</html>
6. 静态成员
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>静态成员</title>
</head>
<body>
<script>
// 静态成员的特征
// 1. 不能被实例访问
// 2. 不能被实例共享
// 3. 必能通过类才可以访问
// 原生
function User() {}
// 函数对象的静态成员就是函数的属性,直接添加
User.site = "php中文网";
User.say = () => `我是 "${User.site}" 的朱老师`;
console.dir(User);
// 下面是对方法的检查,返回true or false
console.log(User.hasOwnProperty("site"));
console.log(User.hasOwnProperty("say"));
console.log(User.hasOwnProperty("constructor"));
console.log(User.hasOwnProperty("call"));
console.log(User.hasOwnProperty("name"));
// ES6中的类实现静态成员
class UserClass {
// 下面这个是原型方法,因为实例方法是写在构造函数中的
hello() {
return "欢迎大家来上我的课程";
}
// 静态属性
static position = "讲师";
// 静态方法
static say() {
return `我是一名 : ${UserClass.position}`;
}
}
// 访问原型方法
console.log(new UserClass().hello());
// 访问静态成员
console.log(UserClass.position); // 这个就需要用类来访问,而非是用实例
console.log(UserClass.say());
</script>
</body>
</html>
7. 类中的私有成员
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类中的私有成员</title>
</head>
<body>
<script>
// 1. 原生实现私有成员
function User() {
// 私有成员: 是函数中的私有变量/局部变量来充当
let course = "es6编程";
let hello = () => `大家一定要好好学习 [${course}] 课程`;
this.say = () => console.log(hello());
}
new User().say();
// 2. es6实现类中的私有成员
class UserClass {
// 1. 私有属性
#salary;
constructor(salary = 0) {
this.#salary = salary;
}
// 私有属性应该设置getter, setter
// getter
get salary() {
return this.#salary;
}
// setter
set salary(value) {
if (value < this.salary) console.log("禁止降薪,否则辞职");
else this.#salary = value;
}
// 私有方法
#max(arr) {
return Math.max(...arr);
}
// 私有方法只能在类中用
getMax(arr) {
return this.#max(arr);
}
// 静态私有属性
static #hobby = "摄影";
static #eat() {
return "我爱吃肉";
}
static getHobby() {
// 需要设置下这块的方法,否则外部无法访问。因此需要有return。
return UserClass.#eat() + "和 " + UserClass.#hobby;
}
// 原型方法中访问静态成员
getHobby() {
return UserClass.#eat() + "哈哈, 和 " + UserClass.#hobby;
}
}
const user = new UserClass(8888);
console.log(user);
console.log(user.salary);
user.salary = 6666;
console.log(user.salary);
user.salary = 9999;
console.log(user.salary);
console.log(UserClass.getHobby());
// 访问静态私有方法
console.log(new UserClass().getHobby());
// 尽管同名,但其实是访问原型方法
</script>
</body>
</html>
8. 类的继承:原生
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类的继承:原生</title>
</head>
<body>
<script>
// 原生是通过修改原型链来实现继承
// 1.父类
function Vehicle(fuel, purpose) {
this.fuel = fuel;
this.purpose = purpose;
}
// 公共/原型方法
Vehicle.prototype.show = function () {
return `燃料: ${this.fuel}\n用途: ${this.purpose}\n`;
};
// 2. 子类
function Car(fuel, purpose, color) {
Vehicle.call(this, fuel, purpose);
this.color = color;
}
// 关键代码
// 更新当前子类的原型对象,这样才能实现真正意义上继承
Car.prototype = Object.create(Vehicle.prototype);
// 手工补上constructor
Car.prototype.constructor = Car;
// 子类中重写父类的原型方法
Car.prototype.show = function () {
return Vehicle.prototype.show
.call(this)
// 这段相当于前面父类中的“return `燃料: ${this.fuel}\n用途: ${this.purpose}\n`”
// 在这个后面继续加上了新的子类中加的属性color
.concat(`颜色: ${this.color}\n`);
};
// 实例化子类
const car = new Car("天然气", "商用", "红色");
console.log(car.show());
</script>
</body>
</html>
9. es6中的类继承
- es6中用类来实现上面用原生实现的类继承。具体对应关系如下图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>es6中的类继承</title>
</head>
<body>
<script>
// 父类
class Vehicle {
// 构造方法
constructor(fuel, purpose) {
this.fuel = fuel;
this.purpose = purpose;
}
// 原型方法
show() {
return `燃料: ${this.fuel}\n用途: ${this.purpose}\n`;
}
}
// 子类
class Car extends Vehicle {
constructor(fuel, purpose, color) {
// 调用父类的构造方法
// super()必须是第一条,否则不能正确的生成子类的this
super(fuel, purpose);
this.color = color;
}
// 重写父类的原型方法
show() {
// super:代码父类的原型对象
console.log(super.show === Vehicle.prototype.show);
return super.show().concat(`颜色: ${this.color}\n`);
}
// super(两种身份):
// 1. 当成函数: super()代表父类的构造方法,必须用在子类的构造方法中的第一行
// 2. 当成对象: super用在原型方法/静态方法,代码父类的原型对象
}
// 实例化子类
const car = new Car("新能源", "商用", "绿色");
console.log(car.show());
</script>
</body>
</html>