类成员与类继承
一、类表达式
//类表达式
let PersonClass = class {
//构造方法
constructor(name) {
this.name = name;
}
sayName() {
return this.name;
}
};
let person = new PersonClass("小丽");
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;
}
})("孙悟空");
console.log(user.getEmail());
二、类的构造方法
class User {
constructor() {
console.log(this);
return Object.create(null);
}
}
const user = new User();
console.log(user);
三、类的实例属性和原型属性
class User {
constructor(id, name) {
//实例属性:直接绑定到实例(this)上面的属性
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());
console.log(user.show());
四、存取属性
function User1(age) {
this.age = age;
}
//公共方法或属性定义在函数的原型对象上
User1.prototype.email = "admin@qq.com";
console.dir(User1);
const user1 = new User1();
console.log(user1.email);
//在User.prototype原型对象上设置"存取器属性"
Object.defineProperty(User1.prototype, "verfiyAge", {
get: () => this.age,
set: (value) => {
if (value >= 18 && value <= 60) this.age = value;
},
//configurable控制属性是否可配置
configurable: true,
//writable控制属性是否可直接修改
//enumerable控制属性是否可被枚举出来
/*
1.writable只限制了能否直接赋值。
2.configurable为false依然能直接修改属性的值。
3.若writable和configurable都是false,则无法进行属性的修改。
*/
enumerable: true,
});
let user = new User1(30);
console.log(user);
console.log(user.age);
console.log(user.verfiyAge);
user.verfiyAge = 40;
console.log(user.age);
//对象的原型永远等于它的构造函数的原型对象,对象上从它的构造函数的原型对象上继承成员(属性和方法)
console.log(user.__proto__ === User1.prototype);
console.log(user.constructor);
console.log(user.__proto__ === user.constructor.prototype);
五、存取属性实现数据的双向绑定小案例
<input type="text" />
<p></p>
<script>
const obj = {};
Object.defineProperty(obj, "hello", {
set(value) {
document.querySelector("input").value = value;
document.querySelector("p").innerHTML = value;
},
});
document.addEventListener("input", function (ev) {
obj.hello = ev.target.value;
});
console.log(obj);
六、静态成员
//静态成员的特征
//1.不能被实例访问
//2.不能被实例共享
//3.必能通过类才可以访问
//原生
function User() {}
//函数对象的静态成员就是函数的属性,直接添加
User.site = "php中文网";
User.say = () => `我是 "${User.site}" 的朱老师`;
console.dir(User);
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.say());
七、类中的私有成员
//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 console.log((this.#salary = value));
}
//私有方法
#max(arr) {
return Math.max(...arr);
}
//私有方法只能在类中用
getMax(arr) {
return this.#max(arr);
}
//静态私有属性
static #hobby = "摄影";
static #eat() {
return "我爱吃肉";
}
static getHobby() {
return UserClass.#eat() + "和" + UserClass.#hobby;
}
// 原型方法中访问静态成员
getHobby() {
return UserClass.#eat() + "哈哈, 和 " + UserClass.#hobby;
}
}
const user = new UserClass(5000);
console.log(user);
console.log(user.salary);
user.salary = 2000;
user.salary = 3000;
console.log(UserClass.getHobby());
console.log(new UserClass().getHobby());
八、类的继承:原生
//原生是通过原型链来实现继承
const obj = { name: "damin", age: 90 };
//创建一个对象继承obj
const newObj = Object.create(obj);
console.log(newObj);
console.log(newObj.__proto__.age === newObj.age);
console.log(newObj.name, newObj.age);
//1.父类
function Vehicle(fuel, purpose) {
this.fuel = fuel;
this.purpose = purpose;
}
//公共/原型方法
Vehicle.prototype.show = function () {
return `燃料:${this.fuel}\n用途:${this.purpose}\n`;
};
console.dir(Vehicle);
//2.子类
function Car(fuel, purpose, color) {
Vehicle.call(this, fuel, purpose);
this.color = color;
}
//关键代码
//更新当前子类的原型对象,这样才能实现真正意义上的继承
Car.prototype = Object.create(Vehicle.prototype);
//手工补上constructor
Car.prototype = Car;
//子类中重写父类的原型方法
Car.prototype.show = function () {
return Vehicle.prototype.show.call(this).concat(`颜色:${this.color}\n`);
};
//实例化子类
const car = new Car("汽油", "家用", "白色");
console.log(car.show());
console.dir(Car);
九、es6中的类继承
//父类
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() {
return super.show().concat(`颜色:${this.color}\n`);
}
}
//super:
//1.当成函数:super()代表父类的构造方法,必须用在子类的构造方法中的第一行
//2.当成对象:super用在原型方法/静态方法,代码父类的原型对象
//实例化子类
const car = new Car("新能源", "出租车", "蓝色");
console.log(car.show());
学习总结
1.类表达式
类表达式是用来定义类的一种语法。和函数表达式相同的一点是,类表达式可以是命名也可以是匿名的。如果是命名类表达式,这个名字只能在类体内部才能访问到。JavaScript 的类也是基于原型继承的。
语法
const MyClass = class [className] [extends] {
// class body
};
类可以作为参数使用
可以立即实例化一个类表达式,(函数/类声明)(调用参数列表) 类的前面加new
2.类的构造函数
constructor
是一种用于创建和初始化class创建的对象的特殊方法。在一个类中只能有一个名为 “constructor” 的特殊方法。
在一个构造方法中可以使用
super
关键字来调用一个父类的构造方法。如果没有显式指定构造方法,则会添加默认的 constructor 方法。
如果不指定一个构造函数(
constructor
)方法, 则使用一个默认的构造函数(constructor)。
3.类的实例属性和原型属性
实例属性:直接绑定到实例(
this
)上面的属性原型属性:绑定在原型(
prototype
)上的属性
4.存取属性
get
语法将对象属性绑定到查询该属性时将被调用的函数。get关键字可在对象内部使用,可为此对象创造一个伪属性
在我们调用这个方法时,可以像访问对象的属性一样,不用加括号,就可以执行此方法,而这个方法会返回一个数,所以我们可以用get关键字为此对象创造一个伪属性。并且这个伪属性只可读,不可写。
只需使用
delete
,就可删除getter
使用
Object.defineProperty()
在现有对象上定义getter
set
关键字与get
关键字的区别是,用set
时,必须有且仅能有一个参数,而用get
时,不能有参数。否则将会报错。并且,用set
时,只能赋值,而不能取值通过get和set配合,对同一属性操作,就可以完成对实例对象赋值的权限的控制。由set,get关键字的特性可知,只有set,get同时用,才可以对同一属性进行存取。
5.静态成员
静态成员的特征
1.不能被实例访问
2.不能被实例共享
3.必能通过类才可以访问
函数对象的静态成员就是函数的属性,直接添加
类(class)通过
static
关键字定义静态方法静态方法调用同一个类中的其他静态方法,可使用
this
关键字非静态方法中,不能直接使用
this
关键字来访问静态方法。而是要用类名来调用
6.类中的私有成员
原生
- 私有成员:是函数中的私有变量/局部变量来充当
es6实现类中的私有成员
私有实例字段使用
#
名称(发音为“哈希名称”)声明,这些名称以#
开头。 #是名称本身的一部分, 声明和访问时也需要加上从作用域之外引用
#
名称是语法错误私有字段可以被类的构造方法(constructor)从内部声明
七、类的继承:原生
原生是通过原型链来实现继承
创建一个对象来继承
八、es6中的类继承
extends
关键字用于类的继承super()
调用父类的构造方法super()
必须是第一条,否则不能正确生成子类的thissuper的两种应用场景:
1.当成函数:
super()
代表父类的构造方法,必须用在子类的构造方法中的第一行2.当成对象:
super
用在原型方法/静态方法,代码父类的原型对象