class类的基本写法
- es6引入了
class类
的概念,可通过class关键字来定义类 - 每个类都会有一个构造函数,即
constructor()
方法,用于创建和初始化class对象 - 要注意,如果一个类没有指定
constructor()
方法则会默认添加,其中this
关键字代表实例对象
class CustomData {
constructor(x,y){
this.x = x;
this.y = y;
}
// 定义方法
toString() { return '(' + this.x + ', ' + this.y + ')'; }
}
- 使用时和构造函数一样,通过
new
命令生成对象实例
let p = new CustomData(1, 2);
console.log(p) // Point {x: 1, y: 2}
console.log(p.toString()) // "(1, 2)"
- 类的数据类型就是函数,类本身指向构造函数
constructor
console.log(typeof CustomData) // function
console.log(CustomData === CustomData.prototype.constructor) // true
- 类的所有方法都定义在prototype对象上,所有类的新方法可以添加在prototype对象上。
- 其中Object.assign()方法可以很方便地一次向类添加多个方法
p.add = function (){ return this.x + 5 }
console.log(p.add()) // 6
===================================
Object.assign(CustomData.prototype, {
add(){ return this.x + 5 },
addSuffix(){ return this.x + 'Suffix' }
});
console.log(p.add()) // 6
console.log(p.addSuffix()) // 1Suffix
- 类内部定义的所有方法都是不可枚举的
Object.getOwnPropertyNames()
返回一个数组,元素为对象自身属性的所有名称,包括不可枚举值,它不考虑继承的属性
console.log(Object.keys(CustomData.prototype)) // []
console.log(Object.getOwnPropertyNames(CustomData.prototype)) // ["constructor", "toString"]
x
和y
都是实例对象自身的属性 ( 定义在this对象上 ),所以CustomData.hasOwnProperty('x') // true
toString
是原型对象的属性 ( 定义在CustomData上 ),所以CustomData.hasOwnProperty('toString') // false
私有属性/私有方法———只能在类的内部访问的属性和方法,外部无法访问
- 给class类添加私有属性,可在属性面前添加#,私有方法同理
class CustomData {
#count = 'count' // 私有属性
constructor(x,y) {
this.x = x;
this.y = y;
}
toString() { return this.#count + this.#handler(); } //可内部调用
// 私有方法
#handler() { return this.x }
}
let p = new CustomData(1,2)
console.log(p.toString()) // count1
console.log(p.#count) // 报错
console.log(p.#handler) // 报错
- 另外,私有方法也可通过命名方式加以区别,比如通过
在方法名前添加_下划线
来定义此方法为私有方法,命名的方式只是为了使用时区分,本身在类的外部还是可以正常调用的 - 因为类的所有方法都是对外可见的,或许我们可以直接将私有方法移出类,通过类内部调用私有方法,同时利用call()改变被调用的私有方法this指向
class CustomData {
constructor(x,y) {
this.x = x;
this.y = y;
}
toString() {
return handler.call(this,this.x,this.y)
}
}
function handler(parameter1,parameter2) {
return parameter1 + parameter2;
}
let p = new CustomData('参数1',"参数2")
console.log(p.toString()) // 参数1参数2
静态属性/静态方法———在方法面前添加static关键字表明此方法为静态方法
- 在类中定义的所有方法都会被实例继承,但如果是静态属性/静态方法,则表示该属性/方法不会被实例继承,而是直接通过类来调用
export class CustomData {
// 静态属性
static prop = '静态属性';
// 静态方法
static toString() { return '我是一个静态方法'; }
}
console.log(CustomData.prop) // 我是一个静态方法 // 直接通过类来调用
console.log(CustomData.toString()) // 我是一个静态方法 // 直接通过类来调用
let p = new CustomData(1,2)
console.log(p.toString()) // [object Object] 不会被实例继承,故不可以通过实例对象调用
console.log(p.prop) // undefined
- 注意:父类的静态方法是可以被子类继承的,不要和实例无法继承这一点混淆
- 另外在子类中,可以通过super对象调用父类的静态方法
export class Custom_object extends CustomData{
static toString_object(){ return super.toString() + ',子类'}
}
console.log(Custom_object.toString()) // 我是一个静态方法
console.log(Custom_object.toString_object()) // 我是一个静态方法,子类
class类的继承
- class通过
extends
关键字实现继承,让子类继承父类的属性和方法 - es6中的
extends
继承必须先调用super()
方法,因为这一步会生成一个继承父类的this
对象,不调用无法继承父类 - 也是在子类的构造函数中,只有调用了
super()
方法后,才可以使用this
关键字,否则会报错 - 另一方面来讲调用
super()
,在新建子类实例时,父类的构造函数必定会先运行,即 “继承在前,实例在后”
class CustomData {
constructor(x,y) {
this.x = x;
this.y = y;
console.log('父类') // 1 先输出“父类”
}
toString() { return '(' + this.x + ', ' + this.y + ')'; }
}
class Custom_object extends CustomData{
constructor() {
super();
console.log('子类') // 2 再输出“子类”
}
}
let p = new Custom_object()
console.log(p) // 3最后Custom_object{x: undefined, y: undefined}
- 继承父类的子类,通过super()继承父类,同时通过传参继承父类的属性
- 在继承关系中,实例对象同时是父类和子类的实例
class CustomData {
constructor(x,y) {
this.x = x * 2 ;
this.y = y;
}
}
class Custom_object extends CustomData{
constructor(x,y,color) {
super(x,y);
this.color = this.x + this.y + color
}
}
let p = new Custom_object(1,2,'color')
console.log(p); // Custom_object {x: 1, y: 2, color: "4color"}
console.log(p instanceof Custom_object) // true
console.log(p instanceof CustomData) // true
- 子类中,除了私有属性和私有方法,父类的所有属性和方法都会被子类继承,其中包括静态属性和静态方法
Object.getPrototypeOf()
方法
- 可从子类上获取父类,因此可通过此法来判断一个类是否继承了另一个类,类似
instanceof
作用
class CustomData {
constructor(x,y,) {
this.x = x ;
this.y = y;
}
toString() { return '(' + this.x + ',' + this.y + ')' }
}
class Custom_object extends CustomData{
constructor(x,y,color) {
super();
this.color = this.x + this.y + color
}
}
console.log(Object.getPrototypeOf(Custom_object)) // ƒ CustomData(x, y) {}
console.log(Object.getPrototypeOf(Custom_object) === CustomData) // true
console.log(new Custom_object() instanceof CustomData) // true
super
关键字
super
既可以当做函数使用,也可当做对象使用- 当作为函数使用时,代表父类的构造函数。且在子类的构造函数中必须执行一次
super函数
- 当作为对象时,在普通方法中,指向父类的原型对象
class CustomData {
constructor(x,y) {
this.x = x ;
this.y = y;
}
toString() { return '(' + this.x + ',' + this.y + ')' }
}
class Custom_object extends CustomData{
constructor(x,y) {
super(x,y); // 函数时
console.log(super.toString())
}
}
let p = new Custom_object(1,2)
====================================================================
call、apply、bind的区别
call
、apply
、bind
每个函数都有这三个属性,它们的作用都是可以改变函数内部的this
指向,且三个的第一个参数是this
要指向的对象
-
call
与apply
传参方式不一样,call
可以枚举多个参数;apply
只有两个参数,第二个参数为数组
Function.call(this,parameter1,parameter2)
Function.apply(this,[parameter1,parameter2])
-
bind
与call
使用方式一致,不过需要多一步手动调用
let foo = Function.bind(this,[parameter1,parameter2])
foo()
====================================================================
instanceof
- 在js中判断一个变量的类型常用
typeof
,但如果是用引用类型,无论是什么类型的变量,都只会返回Object
,为此引入了instanceof
,用以验证引用类型为那种类型
let arr = []
let obj = {}
console.log(typeof arr) // Object
console.log(typeof obj) // Object
console.log(arr instanceof Array,arr instanceof Object) // true true
console.log(obj instanceof Object) // true
- 可以判断实例对象是否是类的实例
- 在继承关系中判断实例对象是否是父类的实例
function Father(){}
function Son(){}
Son.prototype = new Father(); // 原型继承
let sonDate = new Son()
console.log(sonDate instanceof Son) // true 判断变量sonDate是否是Son的实例
console.log(sonDate instanceof Father) //true 在继承关系中判断实例对象是否是父类的实例
待续。。。