1、简介
类的由来
javascript语言中,生成实例对象的传统方法是通过构造函数
es5的写法
function Point(x,y){
this.x=x;
this.y=y;
}
Point.prototype.toString=function(){
console.log('toString');
}
var p=new Ponit(1,2);
es6的class写法
class Point{
constructor(x,y){
this.x=x;
this.y=y;
}
toString(){
console.log('toString');
}
}
类的所有方法都定义在类的prototype属性上面
class Point {
constructor(){
// ...
}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
类的内部所有的方法,都是不可枚举的,这一点和es5不一致(可枚举可以用for in,
或者Object.keys()遍历出来)
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype) //["constructor","toString"]
Point === Point.prototype.constructor
constructor()方法默认返回实例对象(即this),完全可以指定返回另一个对象
与Es5一样,类的所有实例共享一个原型对象
var p1=new Ponit();
var p2=new Ponit();
p1.__proto__===p2.__proto__
p1和p2都是Point的实例,它们的原型都是Point.prototype
Object.getPrototypeOf 方法来获取实例对象的原型
point.__proto__===Point.prototype
constructor方法
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法
constructor()方法默认返回实例对象(即this),完全可以返回另外一个对象
class Foo{
constructor(){
return Object.create(null)
}
}
new Foo() instanceof Foo //false
类的实例
与ES5一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则是定义在原型上(即定义在class上),类的所有实例共享一个原型对象
class Point{
}
var point=Ponit(2,3) //报错
取值函数(getter)和存值函数(setter)
与es5一样,在‘类’的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
存值和取值函数是设置在属性的Descriptor对象上的
var descriptor=Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "html")
'get' in descriptor;
'set' in descriptor;
class表达式
let person=new class {
constructor(name) {
this.name=name;
}
sayName(){}
}('章三')
注意点⚠️
(1)、严格模式
(2)、不存在变量提升
(3)、name属性
class Point {}
Point.name // "Point"
(4)、this的指向
class Logger {
printName(name='there') {
this.print(`Hello`)
}
print(text){
console.log(text)
}
}
const logger=new Logger();
const {printName}=logger;
printName();
上面方法printName方法中的this,默认指向Logger类的实例话,但是这个方法单独取出来使用,this会指向该方法
运行时所在的环境,由于class是严格模式,所以this实际指向的是undefined。
一个比较简单的解决方法是在构造方法中绑定this.
class Logger {
constructor(){
this.printName=this.printName.bind(this)
}
}
另一种解决方案是使用箭头函数
class Obj {
constructor(){
this.getThis=()=>this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承,如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为’静态方法’;
⚠️如果静态方法包含this关键字,这个this指向的是类,而不是实例
静态方法可以被继承
静态方法也可以从super对象上调用的
class Foo {
static classMethod(){
return 'hello'
}
}
class Bar extends Foo {
static classMethod(){
return super.classMethod()+',too';
}
}
Bar.classMethod() //'hello,too'
静态属性
静态属性指的是Class本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性
因为Es6明确规定,Class内部只有静态方法,没有静态属性。现在有一个提案,在实例属性的前面,加上static关键字。
实例属性
实例属性除了定义在constructor方法里面的this上面,也可以定义在类的最顶层。
class Point {
constructor(){
this._count=0;
}
}
class Point {
_count=0;
}
new.target属性
new.target属性可以用来确定构造函数是怎么调用的
方法一
function Person(name) {
if(new.target!==undefined) {
this.name=name;
}else{
throw new Error('必须使用new命令生成实例');
}
}
方法二
function Person(name) {
if(new.target===Person) {
this.name=name;
}else{
throw new Error('必须使用new命令生成实例');
}
}
1、class内部调用new.target,返回当前Class
2、子类继承父类时,new.target会返回子类 (利用这一点可以写出不能独立使用,必须继承后才能使用的类)
super关键字
super关键字,即可以当作函数使用,也可以当作对象使用
- 第一种:super作为函数调用时,代表父类的构造函数,返回的是子类的实例,即super内部的this指的是B的实例,ES6要求,子类的构造函数必须执行一 次super函数
第二种:super作为对象时,在普通方法中,指向父类的原型对象;
在静态方法中,指向父类;在ES6规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向 当前的子类实例 ```javascript class A{ constructor(){ this.x=1; } print(){ console.log(this.x) } }
class B extend A{ constructor(){ super(); this.x=2; } m(){ super.print(); } } let b=new B(); b.m() //2
由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
```javascript
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined,这里太绝了吧,表示没看懂💔
console.log(this.x); // 3
}
}
let b = new B();
上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。
注意⚠️使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。