es6入门5--class类的基本用法

在ES6以前,准确来讲JavaScript语言并没有类的概念,却有模拟类的作法。相比在相似java这类传统面向对象语言中经过类来生成实例,js则经过构造函数模拟类来生成实例。java

这是由于在JS设计初期,做者Brendan Eich选择使用原型来描述对象而非类,但被管理层要求模仿java,所以引入了new this等语言特性,也就是咱们所使用的构造函数作法。es6

那么自ES6起,JavaScript正式引入了class关键字,自此咱们也能够经过class来定义类了。ecmascript

但须要清楚的是ES6中class只是构造函数的一种语法糖,并不是新鲜玩意,class能实现的,咱们经过ES5构造函数一样能够实现。函数

本篇文章只是ES6入门学习笔记,想了解详细文档请阅读阮一峰大神的 ECMAScript 6 入门,那么本文开始。学习

 1、class写法与构造函数的部分区别

1.写法变化this

在ES6以前,咱们模拟一个类的作法是通构造函数spa

let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name)
};
let child = new Parent('echo', 26);
child.sayName();//echo

ES6 class实现更像类的写法,咱们改写上面的方法:prototype

class Parent {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    };
    sayName() {
        console.log(this.name);
    };
};
let child = new Parent('echo', 26);
child.sayName(); //echo

简单对比下,写法上主要这几点变化:设计

1.构造函数名Parent在class写法时变成了类名,但调用方式不变,依然经过new关键字建立实例。code

2.构造函数中this相关操做,在class写法时概括到了constructor方法中,也就是说ES5的构造函数Parent对应ES6的Parent类中的constructor构造方法。

3.ES5中原型上的方法sayName在ES6 class写法中直接写在了内部,同时省略了function关键字。

因此对于方法添加,下面这两种写法是等效的:

// ES6
class Parent {
    constructor() {};
    sayName() {};
    sayAge() {};
};
// ES5
let Parent = function () {};
Parent.prototype = {
    constructor: function () {},
    toString: function () {},
    sayAge: function () {}
};

能够看到,ES5写法同样能够一次批量在原型上添加方法,但我发现,class类不能直接这么作,如下有三种给class类原型添加方法的作法,我分别输出了它们:

// 写法一
class Point {
    constructor() {}
    toString() {}
    toValue() {}
};

// 写法二
class Point {};
Point.prototype = {
    constructor() {},
    toString() {},
    toValue() {},
};

// 写法三
class Point {};
Point.prototype = {
    constructor: function () {},
    toString: function () {},
    toValue: function () {},
};

经过对比能够发现,第2、三种写法先定义class类,再使用prototype在原型上添加方法对于class类无效,方法并无被添加进去。

你确定纳闷了,外部添加无效,那为啥那恰恰有个constructor呢,这是由于constructor方法是class类自带的

class Parent {

};
//等同于 class Parent { constructor() {} };

也就是说当要给class类原型添加方法时,若是使用ES5的添加作法并不会生效;固然也不是写在外部就会失效,经过assign方法仍是能够作到这一点:

class Point {
    constructor() {};
};

Object.assign(Point.prototype, {
    toString() {},
    toValue() {}
});

2.类建立实例必须使用new

经过class类建立实例必须使用new关键字,不使用会报错,这点与构造函数不一样,在ES5中其实咱们不使用new也能调用构造函数建立实例,虽然这样作不符合规范。

class Parent {
    constructor() {}
};
let son = Parent();//报错

let Parent = function (name) {
    this.name = name;
};
let son  = Parent();//不符合规范

3.类的内部方法没法枚举

最后一点区别是,class类内部定义的方法没法枚举,也就是没法经过Object.keys方法获取,但在ES5中keys方法是有效的。

class Point {
    constructor() {};
    toString() {};
    toValue() {};
};
Object.keys(Point);//[]
Object.getOwnPropertyNames(Point.prototype);//['constructor','toString','toValue']

2、constructor方法

前面已经说了,当咱们建立一个类,即使内部没写constructor方法,类也会自带。

因为类的方法都在原型上,因此当咱们调用实例上的方法等同于调用类原型上的方法:

class Parent {
    constructor() {}
};
let son = new Parent();
console.log(son.sayName === Parent.prototype.sayName);//true

类实例的constructor指向类,这与构造函数实例的constructor指向构造函数自己保持一致:

//类的实例与构造函数实例的constructor属性都指向建立本身的类或构造函数
class Parent {
    constructor() {}
};
let son = new Parent();
console.log(son.constructor);//class Parent

let Parent = function (name) {
    this.name = name;
};
let son  = new Parent();
console.log(son.constructor);//Parent

类与构造函数prototype对象的constructor属性都会指向本身,这点也保持了一致。

//类与构造函数prototype的constructor属性都指向本身
class Parent {
    constructor() {}
};
console.log(Parent.prototype.constructor === Parent);//true

let Parent = function (name) {
    this.name = name;
};
console.log(Parent.prototype.constructor === Parent);//true

其实到这里,class类基本用法算说完了,下面主要是对于class概念其它补充。

3、class类的其它补充

1.class类中的存值取值函数

在类的内部也可使用get与set方法对某个属性的取值存值操做作拦截处理。

class Parent {
    get name() {
        return 'echo'
    };
    set name(val) {
        console.log(val);
    };
};
let son = new Parent();
son.name = '时间跳跃'; //时间跳跃
son.name; //echo

当咱们存值和取值时,其实是调用了class内部的get与set对应方法,这点与ES5保持一致。

2.class内中的属性名可使用变量

let name = 'sayName';
class Parent {
    [name]() {
        console.log('echo');
    }
};
let son = new Parent();
son[name]();//echo

3.class表达式写法

let Parent = class {
    sayName() {
        console.log('echo');
    }
};
let son = new Parent();
son.sayName(); //echo

与函数表达式相同,class类在表达式写法下也能添加一个class名:

let Parent = class Me{
    sayName() {
        console.log('echo');
    }
};
let son = new Parent();
son.sayName(); //echo
console.log(Parent.name);//Me

那么此时,Parent类的name属性为Me,但建立实例你得new Parent,而非new Me();

 4.类中this指向

类方法中自带严格模式,且类方法中的this默认指向类的实例

class Parent {
    sayName() {
        console.dir(this); //Patent {}
        this.sayAge(26);
    };
    sayAge(age) {
        console.log(age, this);
    }
};
let son = new Parent();
son.sayName(); //26 Patent {}
console.log(Parent); //class Parent

但若是你在内部方法单独抽出来在外部调用,this此时会指向undefined(严格模式);

class Parent {
    sayName() {
        console.log(this);//undefined
        this.sayAge(26);
    };
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName();//报错

解决方式是,能够经过在class类中的constructor构造方法中直接为你须要外部调用的方法绑定this。

class Parent {
    constructor(){
        this.sayName = this.sayName.bind(this);
    }
    sayName() {
        this.sayAge(26)
    };
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName();//26

或者在构造方法中利用箭头函数,由于箭头函数中的this指向自身定义时所在的对象,此时箭头函数this指向实例。

class Parent {
    constructor() {
        this.sayName = () => {
            this.sayAge(26);
        }
    }
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName(); //26

5.类的静态方法

我在JavaScript模式一书的读书笔记中也有提到ES5中的构造函数静态方法;若是某个方法只有类自身能够调用,实例并不会继承,那么咱们通常称此方法为静态方法。

//ES5
function Parent (){};
Parent.sayAge = function (){
    console.log(26);
};
Parent.sayAge()//26
let son = new Parent();
son.sayAge()//报错,找不到这个方法

//ES6
class Parent {
    static sayAge() {
        console.log(26);
    }
};
Parent.sayAge()//26
let son = new Parent();
son.sayAge()//报错,找不到这个方法

在上述代码中,我分别用ES5与ES6两种写法分别为类(构造函数)添加了静态方法sayAge。

很明显这个方法只能被类自身调用,实例没法继承,只是相比ES5直接添加在构造函数上,类使用了static字段,也就是说,若是你想让某个方法做为静态方法,请在前面添加static。

另外有一点,若是静态方法中使用了this,此时this指向了类,而不是实例,请注意。

//ES5
function Parent (){};
Parent.sayAge = function (){
    console.log(this);
};
Parent.sayAge()//构造函数自身

//ES6
class Parent {
    static sayAge() {
        console.log(this);
    }
};
Parent.sayAge()//class Patent 类自身

6.类的静态属性

同理,在ES5中,我在构造函数内部某个属性只想给构造函数自身使用,实例没法使用,此时就得使用静态属性

//ES5
function Patent(){};
Patent.age = 26;
Patent.sayAge = function (){
    console.log(this.age);
}
Patent.sayAge();//26

//ES6
class Parent {
    static sayAge() {
        console.log(this.age);
    };
};
Parent.age = 26;
Parent.sayAge();//26

上述代码中分别用ES5,ES6为类添加了静态属性,其实都是直接加在类上,作法相同。

固然在后面的提案中,也推荐使用static来建立类的静态属性,作法与静态方法相同,也就是说有两种方法能够作到这点。

class Parent {
    static age = 26;
    static sayAge() {
        console.log(this.age);
    };
};
Parent.sayAge();//26

7.实例属性的简写方法

我在前面说,当声明一个类,即便不写constructor方法,类也会自带,那我想省略掉constructor方法,同时还想给实例添加属性呢怎么办,其实也能够简写。

class Parent {
    constructor() {
        this.name = 'echo';
        this.age = 26;
    };
    sayName() {};
};
let son = new Parent('echo', 26);
son.name //echo
son.age //26

class Parent {
    name = 'echo';
    age = 'age';
    sayName() {};
};
let son = new Parent();
son.name //echo
son.age //26

以上两种写法等效,第二种实例赋值时直接写在了类的顶部,同时去掉了this,固然若是实例赋值带参数,那就无法简写了。

最后有一个类的私有属性和私有方法没说,由于ES6没提供,只能模拟,有兴趣能够自行阅读。

那么ES6中class类基本用法就说到这里了。

相关文章
相关标签/搜索