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

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

那么自ES6起,JavaScript正式引入了class关键字,自此我们也可以通过class来定义类了。

但需要清楚的是ES6中class只是构造函数的一种语法糖,并非新鲜玩意,class能实现的,我们通过ES5构造函数同样可以实现。

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

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

1.写法变化

在ES6之前,我们模拟一个类的做法是通构造函数:

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实现更像类的写法,我们改写上面的方法:

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关键字创建实例。

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 () {},
};

通过对比可以发现,第二、三种写法先定义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']

二、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概念其它补充。

三、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(); //

5.类的静态方法

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

//ES5
function Parent (){};
Parent.sayAge = function (){
console.log(26);
};
Parent.sayAge()//
let son = new Parent();
son.sayAge()//报错,找不到这个方法 //ES6
class Parent {
static sayAge() {
console.log(26);
}
};
Parent.sayAge()//
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();// //ES6
class Parent {
static sayAge() {
console.log(this.age);
};
};
Parent.age = 26;
Parent.sayAge();//

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

当然在后面的提案中,也推荐使用static来创建类的静态属性,做法与静态方法相同,也就是说有两种方法可以做到这点。

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

7.实例属性的简写方法

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

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

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

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

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

es6入门5--class类的基本用法的更多相关文章

  1. C++程序设计入门(上) string类的基本用法

    string类中的函数 1. 构造 2. 追加 3. 赋值 4. 位置与清除 5. 长度与容量 6. 比较 7. 子串 8. 搜索 9. 运算符 追加字符串 string s1("Welc ...

  2. es6入门6--数组拓展运算符,Array.from()基本用法

    本文只是作为ES6入门第九章学习笔记,在整理知识点的同时,会加入部分个人思考与解答,若想知道更详细的介绍,还请阅读阮一峰大神的ES6入门 一.拓展运算符 ES6中新增了拓展运算(...)三个点,它的作 ...

  3. ES6入门——类的概念

    1.Class的基本用法 概述 JavaScript语言的传统方式是通过构造函数,定义并生成新对象.这种写法和传统的面向对象语言差异很大,下面是一个例子: function Point(x, y) { ...

  4. es6入门3--箭头函数与形参等属性的拓展

    对函数拓展兴趣更大一点,优先看,前面字符串后面再说,那些API居多,会使用能记住部分就好. 一.函数参数可以使用默认值 1.默认值生效条件 在变量的解构赋值就提到了,函数参数可以使用默认值了.正常我们 ...

  5. ES6入门之let和const命令

    前言 大家好,我是一只流浪的kk,当你看到这边博客的时候,说明你已经进入了ES6学习的领域了,从本篇博客开始,我将会将自己学习到ES6的相关知识进行整理,方便大家参考和学习,那么我将带你进入第一节的内 ...

  6. ES6入门十:iterator迭代器

    迭代模式 ES6迭代器标准化接口 迭代循环 自定义迭代器 迭代器消耗 一.迭代模式 迭代模式中,通常有一个包含某种数据集合的对象.该数据可能存在一个复杂数据结构内部,而要提供一种简单的方法能够访问数据 ...

  7. ES6入门笔记

    ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...

  8. es6入门4--promise详解

    可以说每个前端开发者都无法避免解决异步问题,尤其是当处理了某个异步调用A后,又要紧接着处理其它逻辑,而最直观的做法就是通过回调函数(当然事件派发也可以)处理,比如: 请求A(function (请求响 ...

  9. UWP入门(十二)--数据绑定用法

    原文:UWP入门(十二)--数据绑定用法 主要几个元素: Template DataTemplate ItemSource 数据绑定是一个数据提取的方法,能使数据和UI上的控件紧密相连,下面的Demo ...

随机推荐

  1. BZOJ 1492 货币兑换 Cash CDQ分治

    这题n2算法就是一个维护上凸包的过程. 也可以用CDQ分治做. 我的CDQ分治做法和网上的不太一样,用左边的点建立一个凸包,右边的点在上面二分. 好处是思路清晰,避免了凸包的插入删除,坏处是多了一个l ...

  2. myeclipse 安装flex插件后变为中文 修改配置文件切换到英文界面

    解决办法: 1. cmd 敲命令进入安装目录,运行myeclipse.exe -nl en后,启动为英文 在安装目录下新建txt,改名为myeclipse.bat,将上面那行命令写入保存,再发送快捷方 ...

  3. <再看TCP/IP第一卷>关于链路层的知识细节及相关协议

    在TCP/IP协议族中,链路层的主要有三个目的: (1)为IP模块发送和接受数据报 (2)为ARP模块发送ARP请求和接受ARP应答 (3)为RARP发送RARP请求和接受RARP应答 TCP/IP支 ...

  4. Spring Boot2.0之统一处理web请求日志

    试问,你的项目中,如果有几万个方法,你还这么写log.info("name"+name+",age"+age )日志么?low~ 所以用AOP呀 1.首先创建个 ...

  5. c 获取 域名ip

    #include <stdio.h> #include <netdb.h> int main(int argc, char *argv[]) { ) { printf(]); ...

  6. 大数据初级笔记二:Hadoop入门之Hadoop集群搭建

    Hadoop集群搭建 把环境全部准备好,包括编程环境. JDK安装 版本要求: 强烈建议使用64位的JDK版本,这样的优势在于JVM的能够访问到的最大内存就不受限制,基于后期可能会学习到Spark技术 ...

  7. 2017SN多校D1T2 note:dp

    题意: 给你一个长度为n的字符串s,并且告诉你有m对字母不能相邻,问你最少在s中取出多少个字符能够使这个字符串合法. 题解: 表示状态: dp[i] = max num of letters 考虑到第 ...

  8. 分享知识-快乐自己: Oracle数据库实例、用户、表、表空间之间关系

    数据库: Oracle数据库是数据的物理存储.这就包括(数据文件ORA或者DBF.控制文件.联机日志.参数文件). 其实Oracle数据库的概念和其它数据库不一样,这里的数据库是一个操作系统只有一个库 ...

  9. 分享知识-快乐自己:SpringMvc整合遇到-前台传JSON参数,后台实体类对象接收

    前台数据转JSON对象: /** * * @param $myFrom:from表单 * @returns {{}} */ function from($myFrom) { var ebookEntr ...

  10. CodeForces - 697F:Legen... (AC自动机+矩阵)

    Barney was hanging out with Nora for a while and now he thinks he may have feelings for her. Barney ...