js中class类的基本使用
class类
说明:
class就是构造函数的语法糖,在使用继承的时候会更加方便,并且在思想上和java等统一起来
class Man {
// ...
}
typeof Man // "function"
Man === Man.prototype.constructor // true
一、创建类
class的属性、方法
作用:
批量创建对象,功能和构造函数相似,只是写法不同
场景:
插件、复用的功能、vue(每一个组件都是一个实例)、js动画(批量创建小球实例,每一个小球都有自己的运动轨迹)
class Man {
// 构造器
// 设置实例属性,内容都是非共享的原型(非共享)
constructor(name,age){
this.name = name;
this.age = age;
this.sex = '男'
this.car = {
car1:'奔驰',
car2:'宝马',
car3:'奥迪'
}
this.eat = function(){}
}
// 设置原型方法,内容都是共享的原型(共享)
smoking(){}
play(){}
}
// 和构造函数一样,通过new关键字调用
let yj1 = new Man('yj1',18)
let yj2 = new Man('yj2',18)
// {
// age: 18,
// name: "yj2",
// sex: "男",
// this.car:{...省略}
// eat(){} ,
// prototype: {smoking(){}, play(){}}
// }
yj1.car === yj2.car // false 实例本身的方法(非共享)
yj1.eat === yj2.eat // false 实例本身的方法(非共享)
yj1.smoking === yj2.smoking // true 实例原型身上的方法(共享)
注意:
-
constructor会默认存在,并调用
-
constructor中写入的都是实例属性和方法,属于非共享内容
-
constructor同级写入的都是原型方法,属于共享内容
class的属性新写
作用:
简化代码,没有功能优化,只是让书写属性时更加方便、简洁(少写了this,将属性从constructor释放出来)
class Man {
sex = '男'
height = 180
_height = 180
constructor(name,age){
this.name = name;
this.age = age;
}
smoking(){}
play(){}
}
// 和构造函数一样,通过new关键字调用
let yj1 = new Man('yj1',18)
// {
// age: 18,
// name: "yj1",
// sex: "男",
// height = 180,
// _height = 180,
// prototype: {smoking(){}, play(){}}
// }
注意:
- 实例属性新写法,程序员一般喜欢写在constructor顶部
- 实例属性新写法,程序员一般在前面加下划线
class的静态属性、方法
作用:
调用方便,直接在类身上调用属性和方法,不需要通过new出实例才可使用(实例对象无法调用属性和方法),比如:Date.now()
定义:
在普通属性和方法前面加入 static 即可,使用时不需要携带static
class Man {
_sex = '男'
static _age = 18
constructor(){}
static smoking(){
console.log(this.age)
this.play()
}
static play(){}
}
Man._age // 18
Man.smoking() // 先调抽烟,再掉玩的方法
注意:
- 静态方法里面的this指向class本身,不指向实例对象
- 静态方法在调用时,里面可以使用静态属性和静态方法,但不允许调用实例方法和实例属性
- 静态属性可以使用,但暂未列入语法规则中
class的私有属性、方法
作用:
提供一些只能自己调用的属性和方法,防止被外部篡改(只允许类在内部自己调用,实例无法调用)
定义:
在普通属性和方法前面加入# 号即可,#属于私有属性、方法的一部分,所以调用也需要加入#号
class Man {
#sex = '男'
constructor(){
}
init(){
this.#smoking()
this.#play()
console.log(this.#sex)
}
#smoking(){}
#play(){}
}
let yj = new Man()
yj.init() // 先抽烟再玩、再打印男属性
yj.#sex //报错
yj.#smoking() //报错
class的get和set方法
作用:
自定义get和set的方法
类似于数据劫持Object.definedProperty(),获取和设置的时候都会触发对应get和set方法
class Man {
#_age = 18
constructor(name,age){
this.name = name;
}
get age(){
console.log('获取属性');
return this.#_age
}
set age(v){
console.log('设置属性');
this.#_age = v
}
}
// 和构造函数一样,通过new关键字调用
let yj = new Man('yj')
console.log(yj.age) // 打印:获取属性 18
yj.age = 19 // 打印:设置属性
console.log(yj.age) // 打印:获取属性 19
场景:
一些框架中的私有数据,在class中定义了使用了私有数据后,不方便直接对外暴露(可以修改,但不允许随意修改,所以要提供一些固定方法),可以通过get和set代理
注意:
- 调用get、set时,千万别直接获取、赋值给自己,会导致递归死循环(get/set触发设置,又会重新触发新的get/set)
二、继承类
class的继承
作用:
让功能更加强大、站在巨人的肩膀上开发
使用function也可以实现继承,并且方法很多,但是都是非常麻烦
而class类可以非常清晰和方便的解决这个问题
子类和父类名字一样(重写父类,采用就近原则)
场景:
给轮播图插件新增几个功能、在IT的基础类上功能变成前端类
class Father {
constructor(name,money){
this.name = name
this.money = money
}
makeMoney(){
console.log(this.name+'赚钱');
}
}
class Son extends Father{
constructor(name,money,toy){
super(name,money)
this.toy = toy
}
study(){
console.log(this.name+'学习');
}
}
let son = new Son('son',100,'变形金刚')
let fa = new Father('father',1000)
son.makeMoney() // son赚钱
son.study() // son学习
fa.makeMoney() // fa赚钱
fa.study() // 报错 子继承父,所有子有父的方法、父没有子的方法
注意:
-
实现继承时,constructor中必须第一时间执行super(),super()相当于先创造父类,先有父再有子(Es5和es6的继承思路不一样)
- Es5 先创建子实例,再把父方法和属性继承到子实例上
- Es6 先把父方法和属性放到子类中,再创建子实例
-
父子存在相同的属性或者方法时,优先使用自己的属性和方法
-
静态属性和方法可以继承的(子类可以直接调用父类的属性和方法)
-
私有属性和方法是无法继承的(子类中无法调用父类的私有属性和方法,但可以调用父类的普通方法,间接调用父类的私有属性和方法)
-
super当作函数调用时指向父类(存在继承时调用super(),相当于把父类执行一遍),但返回子类实例
-
super当做对象使用时指向父级原型,但this指向子类实例对象(把父级方法拿过来,让儿子调用)
// 在普通函数中把super当对象使用,super相当于在父级的原型上调用 class F { constructor() { this.fn1 = function () { console.log('父级实例方法'); } } fn2() { console.log('父级原型方法'); } } class S extends F { constructor() { super() super.fn1() // 报错(实例方法) super.fn2() // 父级原型方法(原型方法) } } new S()
// 在静态函数中把super当对象使用,super相当于在父类 class F { constructor() {} static fn() { console.log('父类静态方法'); } fn(){ console.log('父类实例方法'); } } class S extends F { constructor() { super() } } S.fn() // 父类静态方法-----子类调静态方法会直接找父级调静态方法
// 在静态函数中把super当对象使用,super相当于在父类 class F { constructor() {} static fn() { console.log('父类静态方法'); } fn(){ console.log('父类实例方法'); } } class S extends F { constructor() { super() super.fn() } } new S() // 父类实例方法----子级new的是实例,调的也是实例方法
-
子级中只能把super当对象和函数使用,无法单独打印或使用变量 super
三、class和ts结合
class的abstract(ts的抽象类、抽象方法)
作用:
抽象类,只能用于继承,不能单独实例化(ts内容)
抽象方法,子类在继承的时候,必须重写(ts内容)
定义:
在普通class前面加上abstract
abstract class Person {
abstract sayHei():void // 这个抽象类,只能被继承,不能被实例化
}
let p = new Person() // 不允许,只能被继承
class Man extends Person{
sayHei(){} // 必须重写父类的方法,否则ts报错
} // 允许,可以被继承
class的修饰符
作用:
public 公共属性(默认值),属性可以在任意位置访问和修改
private 私有属性,属性只可以在class内部访问和修改–一般在类中添加方法,使得私有属性可以被外部访问和修改
protected 受包含属性,只能在当前class和子class中访问和修改,实例无法访问
定义:
在属性前面加上public 、private 、protected
class P {
private _name: string
public _age: number
protected _sex: number
constructor(_name:string,_age:number) {
this._name = _name
this._age = _age
}
go1(){
this.
}
}
let yj = new P('yj',99)
yj._name = 'zd2' // ts报错,提示,私有的_name属性无法控制
yj._age = 999 // ts不报错,公共属性可以修改
console.log(yj);
class P {
private name: string
public age: number
protected sex: number
constructor(name: string, age: number, sex: number) {
this.name = name
this.age = age
this.sex = sex
}
changeSex() {
this.sex = 999
}
}
class PPPP extends P {
changeSex() {
this.sex = 999
}
}
let yj = new P('yj',9,0)
let yjj = new PPPP('yj',9,0)
yj.name = 'yjj' // ts报错,提示,属性name是私有的,只能在当前类中修改
yj.age = 99
yj.sex = 1 // ts报错,提示,属性sex受到保护,只能在当前类和子类中修改
yj.changeSex()
yjj.changeSex()
注意,不可以在interface实现(implements)的时候写私有private
interface Person {
_name?: string //报错,接口不允许出现私有
}
class P implements Person {
private _name: string
}
更多推荐
所有评论(0)