ES6 类(Class)基本用法和静态属性+方法详解
原文地址:http://blog.csdn.net/pcaxb/article/details/53759637
JavaScript语言的传统方法是通过构造函数,定义并生成新对象,prototype 属性使您有能力向对象添加属性和方法。下面是通过传统的方式创建和使用对象的案例:
<span style="font-size:18px;">//Person.js
function Person(x,y){
this.x = x;
this.y = y;
} Person.prototype.toString = function (){
return (this.x + "的年龄是" +this.y+"岁");
}
export {Person};
//index.js
import {Person} from './Person';
let person = new Person('张三',12);
console.log(person.toString());</span>
1.Class的基本用法
ES6引入了Class(类)这个概念,作为对象的模板,通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用ES6的“类”改写,就是下面这样。
<span style="font-size:18px;">//Person.js
class Person{
// 构造
constructor(x,y){
this.x = x;
this.y = y;
} toString(){
return (this.x + "的年龄是" +this.y+"岁");
}
}
export {Person};
//index.js
import {Person} from './Person';
let person = new Person('张三',12);
console.log(person.toString());</span>
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数Person,对应ES6的Person类的构造方法。Person类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
ES6的类,完全可以看作构造函数的另一种写法。
<span style="font-size:18px;">//Person.js
console.log(typeof Person);//function
console.log(Person === Person.prototype.constructor);//true</span>
上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
<span style="font-size:18px;">//Person.js
console.log(Person.prototype);//输出的是一个对象</span>
构造函数的prototype属性,在ES6的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面,通过以下方式可是覆盖类中的方法,当然定义类的时候也可以通过这个方式添加方法。
<span style="font-size:18px;">//index.js
Person.prototype = {
getName(){
return '张三';
},
getAge(){
return '12';
}
};</span>
在类的实例上面调用方法,其实就是调用原型上的方法
<span style="font-size:18px;">//index.js
console.log(person.constructor === Person.prototype.constructor);//true</span>
Object.assign方法可以给对象Person动态的增加方法,而Person.prototype = {}是覆盖对象的方法,或者在初始化的时候添加方法。
<span style="font-size:18px;">//index.js
Object.assign(Person.prototype,{
getWidth(){
console.log('12');
},
getHeight(){
console.log('24');
}
});
console.log(Person.prototype);</span>
toString方法是Person类内部定义的方法,ES6中它是不可枚举的,这一点与ES5的行为不一致,ES5是可以枚举的。
<span style="font-size:18px;">//index.js
//ES5
console.log(Object.keys(Person.prototype));//["toString", "getWidth", "getHeight"]
console.log(Object.getOwnPropertyNames(Person.prototype));//["constructor", "toString", "getWidth", "getHeight"] //ES6
console.log(Object.keys(Person.prototype));//["getWidth", "getHeight"]
console.log(Object.getOwnPropertyNames(Person.prototype));//["constructor", "toString", "getWidth", "getHeight"]</span>
Object.keys(obj),返回一个数组,数组里是该obj可被枚举的所有属性。Object.getOwnPropertyNames(obj),返回一个数组,数组里是该obj上所有的实例属性。
在ES6中,类的属性名可以使用表达式,具体实现方式如下
<span style="font-size:18px;">//Article.js
let methodName = "getTitle";
export default class Article{
[methodName](){
console.log('输出文章的标题1');
}
}
//index.js
import Article from './Article';
//console.log(Article.prototype);
let article = new Article();
article.getTitle()</span>
constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个默认的constructor方法会被添加。所以即使你没有添加构造函数,也是有默认的构造函数的。一般constructor方法默认返回实例对象this,但是也可以指定constructor方法返回一个全新的对象,让返回的实例对象不是该类的实例。
<span style="font-size:18px;">//ConstructorStu.js
import Article from './Article';
export default class ConstructorStu{
// 构造
constructor() {
console.log('constructor');
return new Article();
}
}
//index.js
import ConstructorStu from './ConstructorStu';
console.log('==111==');
console.log(new ConstructorStu() instanceof ConstructorStu);//false
console.log('==222==');
let cons = new ConstructorStu();
console.log('==333==');
cons.constructor();
console.log('==444==');
运行结果
==111==
constructor
false
==222==
constructor
==333==
==444==</span>
说明:类的构造函数,不使用new是没法调用的,即使你使用实例对象去调用也是不行的,这是它跟普通构造函数的一个主要区别。
实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。hasOwnProperty()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false。
<span style="font-size:18px;">//Person.js
class Person{
// 构造
constructor(x,y){
this.x = x;
this.y = y;
} toString(){
return (this.x + "的年龄是" +this.y+"岁");
}
} let person = new Person('lis',8);
console.log(person.toString());
console.log(person.hasOwnProperty('x'));//true
console.log(person.hasOwnProperty('y'));//true
console.log(person.hasOwnProperty('toString'));//false
console.log(person.__proto__.hasOwnProperty('toString'));//true</span>
说明:上面结果说明对象上有x,y属性,但是没有toString属性。也就是说x,y是定义在this对象上,toString定义在类上。
<span style="font-size:18px;">let person1 = new Person('张三',12);
let person2 = new Person('李四',13);
console.log(person1.__proto__ === person2.__proto__);//true</span>
类的所有实例共享一个原型对象,person1和person2都是Person的实例,它们的原型都是Person.prototype,所以__proto__属性是相等的。这也意味着,可以通过实例的__proto__属性为Class添加方法。
<span style="font-size:18px;">let person1 = new Person('张三',12);
let person2 = new Person('李四',13);
person1.__proto__.getH = function (){
return "Height";
};
console.log(person1.getH());
console.log(person2.getH());</span>
上面代码在person1的原型上添加了一个getH方法,由于person1的原型就是person2的原型,因此person2也可以调用这个方法。而且,此后新建的实例person3也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。
__proto__参考资料:点击打开链接
class不存在变量提升,需要先定义再使用,因为ES6不会把类的声明提升到代码头部,但是ES5就不一样,ES5存在变量提升,可以先使用,然后再定义。
<span style="font-size:18px;">//正确
new A();
function A(){ }//ES5可以先使用再定义,存在变量提升
//错误
new B();
class B{ }//B is not a constructor
//ES6不能先使用再定义,不存在变量提升</span>
这个类的名字是Expression而不是Expre,Expre只在Class的内部代码可用,指代当前类。
<span style="font-size:18px;">//Expression.js
const Expression = class Expre{ static getAge(){
return '12';
} getClassName(){
return " ClassName1= " +Expre.name + " ClassName2= " +Expression.name;
} }; let exp = new Expression();
//let exp = new Expre();错误
//bundle.js:7935 Uncaught ReferenceError: Expre is not defined
console.log(exp.getClassName());//ClassName1= Expre ClassName2= Expre
//console.log(Expre.getAge());错误
//bundle.js:7935 Uncaught ReferenceError: Expre is not defined
console.log(Expression.getAge());</span>
说明:Expre.name和Expression.name返回的都是Expre,返回的都是当前。
如果类的内部没用到的话,可以省略Expre,也就是可以写成下面的形式
<span style="font-size:18px;">//Expression.js
const MyExpre = class{ getClassName(){
return MyExpre.name;
} }; let myExpre = new MyExpre();
console.log(myExpre.getClassName());//MyExpre</span>
说明:如果省略了class后面的那个名字Expre,MyExpre.name返回的就是MyExpre,如果没有省略MyExpre.name返回就是class后面的那个名字Expre。
采用Class表达式,可以写出立即执行的Class
<span style="font-size:18px;">//Expression.js
let person = new class{ // 构造
constructor(props) {
this.props = props;
} getProps(){
return this.props;
} }('构造函数的参数'); console.log(person.getProps());//构造函数的参数</span>
私有方法是常见需求,但ES6不提供,只能通过变通方法模拟实现。一种做法是在命名上加以区别,在方法前面加上_(下划线),表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法。另一种方法就是索性将私有方法移出模块,因为模块内部的所有方法都是对外可见的。
<span style="font-size:18px;">//PrivateMethod.js
export default class PrivateMethod{ // 构造
constructor() {
} getName(){
priName();
}
} function priName(){
console.log('私有方法测试');
}
//index.js
import PriMe from './PrivateMethod';
let prime = new PriMe();
prime.getName();</span>
说明:通过这种方式还可以定义私有属性,同理。还有一种方法是利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值,不过这种方式稍微麻烦点。
类的方法内部如果含有this,它默认指向类的实例。getName方法中的this,默认指向ThisStu类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到name方法而导致报错。
<span style="font-size:18px;">//ThisStu.js
class ThisStu{ getName(){
return this.name();
} name(){
return '王五';
} }
export {ThisStu}; //index.js
import {ThisStu} from './ThisStu';
let thisStu = new ThisStu();
console.log(thisStu.getName());
const {getName} = thisStu;
getName();
//Cannot read property 'name' of undefined</span>
一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到name方法了。修改ThisStu.js文件如下:
<span style="font-size:18px;">//ThisStu.js
class ThisStu{
// 构造
constructor() {
this.getName = this.getName.bind(this);
} getName(){
return this.name();
} name(){
return '王五';
} }
export {ThisStu};</span>
使用构造函数,直接给当前实例的getName赋值,修改修改ThisStu.js文件的构造函数如下:
<span style="font-size:18px;">// 构造
constructor() {
//this.getName = this.getName.bind(this);
this.getName = ()=>{
return this.name();
}
}
</span>
2.Class的静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
<span style="font-size:18px;">//StaticMethod.js
//定义静态方法
static getAge(){
return '获取Age的静态方法';
}
//通过类名直接调用
console.log(StaticMethod.getAge());</span>
如果StaticMethod继承StaticMethodParent,StaticMethodParent的静态方法,可以被StaticMethod继承。
<span style="font-size:18px;">//StaticMethodParent.js
export default class StaticMethodParent{
static getCommon(){
return '父类的静态方法';
}
}
//通过子类直接调用
console.log(StaticMethod.getCommon());
如果StaticMethod继承StaticMethodParent,StaticMethodParent的静态方法,可以在StaticMethod中用super调用。
//定义静态方法
static getAge(){
//子类可以调用父类的静态方法
console.log(super.getCommon());
return '获取Age的静态方法';
}</span>
说明:静态方法只能在静态方法中调用,不能再实例方法中调用。
3.Class静态属性和实例属性
静态属性指的是Class本身的属性,即Class.propname,而不是定义在实例对象(this)上的属性。ES6使用静态属性和实例属性:
<span style="font-size:18px;">//StaticMethod.js
//定义静态属性
StaticMethod.firstName = 'pca';
console.log(StaticMethod.firstName);
//定义实例属性
//ES6实例属性只能在constructor构造函数中定义
constructor() {
super();
this.width = '40cm';
}
getWidth(){
return this.width;//使用的时候需要加上this
}
//为了可读性的目的,对于那些在constructor里面已经定义的实例属性,新写法允许直接列出。
width;</span>
说明:目前ES6,只有这种写法可行,因为ES6明确规定,Class内部只有静态方法,没有静态属性。
ES7有一个静态属性的提案,目前Babel转码器支持。安装babel-preset-stage-0 包含了0-3的stage,可根据需要添加,不同的stage封装了不同的插件,官方推荐是使用stage-1
安装命令(根据自己的需求调整):
<span style="font-size:18px;">npm install --save babel-preset-stage-0</span>
ES7使用静态属性和实例属性:
<span style="font-size:18px;">//StaticMethod.js
//ES7提案 定义静态属性
static lastName = 'pcaca';
//ES7定义实例属性
height = '150cm';</span>
说明:ES7和ES6的静态属性和实例属性只是定义不一样,调用的方式是一样的
Class的静态方法/Class静态属性和实例属性的整个案例:
<span style="font-size:18px;">//StaticMethodParent.js
export default class StaticMethodParent{
static getCommon(){
return '父类的静态方法';
}
} //StaticMethod.js
import StaticMethodParent from './StaticMethodParent' //定义静态属性和静态方法
class StaticMethod extends StaticMethodParent{
//因为ES6明确规定,Class内部只有静态方法,没有静态属性,所以ES6在类中定义静态属性都是错误的。
//static lastName = 'pcaca';ES6错误 //ES7提案 定义静态属性
//安装babel-preset-stage-0 包含了0-3的stage,可根据需要添加,
//不同的stage封装了不同的插件,官方推荐是使用stage-1
static lastName = 'pcaca'; //ES7定义实例属性
height = '150cm'; getHeight(){
return this.height;//ES7的使用也要加上this
} //ES6实例属性只能在constructor构造函数中定义
constructor() {
super();
this.width = '40cm';
} //为了可读性的目的,对于那些在constructor里面已经定义的实例属性,新写法允许直接列出。
width; getWidth(){
return this.width;//使用的时候需要加上this
} //定义静态方法
static getAge(){
//子类可以调用父类的静态方法
console.log(super.getCommon());
return '获取Age的静态方法';
}
}; //定义静态属性
StaticMethod.firstName = 'pca'; export {StaticMethod}; //index.js
import {StaticMethod} from './StaticMethod';
console.log(StaticMethod.getAge());
console.log(StaticMethod.getCommon());
console.log(StaticMethod.firstName);
console.log(StaticMethod.lastName);
let staticMethod = new StaticMethod();
console.log(staticMethod.height);
console.log(staticMethod.getHeight());
console.log(staticMethod.width);
console.log(staticMethod.getWidth());
//staticMethod.getAge();//bundle.js:7906 Uncaught TypeError: staticMethod.getAge is not a function</span>
参考资料:点击打开链接
ES6 类(Class)基本用法和静态属性+方法详解的更多相关文章
- Delphi中TStringList类常用属性方法详解
TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的. 常规的用法大家都知道,现在来讨论它的一些高级的用法. 先把要讨论的几个属性列出来: 1.CommaText 2.Delim ...
- 【delphi】TStringList类常用属性方法详解
TStringList 常用方法与属性 var List: TStringList; i: Integer; begin List := TStringList.Create; List.Add('S ...
- moviepy音视频剪辑:视频剪辑基类VideoClip的属性及方法详解
☞ ░ 前往老猿Python博文目录 ░ 一.概述 在<moviepy音视频剪辑:moviepy中的剪辑基类Clip详解>和<moviepy音视频剪辑:moviepy中的剪辑基类Cl ...
- Js apply方法与call方法详解 附ES6新写法
我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家 ...
- CSS定位属性Position详解
CSS中最常用的布局类属性,一个是Float(CSS浮动属性Float详解),另一个就是CSS定位属性Position. 1. position:static 所有元素的默认定位都是:position ...
- Java之Static静态修饰符详解
Java之Static静态修饰符详解 Java之Static静态修饰符详解 一.特点 1.随着类的加载而加载,随着类的消失而消失,生命周期最长 2.优先于对象存在 3.被所有类的对象共享 4.可以直接 ...
- $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )
$.ajax()方法详解 jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...
- Python的Django框架中forms表单类的使用方法详解
用户表单是Web端的一项基本功能,大而全的Django框架中自然带有现成的基础form对象,本文就Python的Django框架中forms表单类的使用方法详解. Form表单的功能 自动生成HTML ...
- equals()方法和hashCode()方法详解
equals()方法和hashCode()方法详解 1. Object类中equals()方法源代码如下所示: /** * Object类中的equals()方法 */ public boolean ...
随机推荐
- Python判断网络是否可以访问
import urllib url = "http://www.baidu.com" try: status = urllib.urlopen(url).code print st ...
- 016-Hadoop Hive sql语法详解6-job输入输出优化、数据剪裁、减少job数、动态分区
一.job输入输出优化 善用muti-insert.union all,不同表的union all相当于multiple inputs,同一个表的union all,相当map一次输出多条 示例 二. ...
- [转载]分布式session处理方案
伴随网站业务规模和访问量的逐步发展,原本由单台服务器.单个域名的迷你网站架构已经无法满足发展需要. 此时我们可能会购买更多服务器,并且启用多个二级子域名以频道化的方式,根据业务功能将网站分布部署在独立 ...
- http的keep-alive和tcp的keepalive区别
原文地址:http://blog.csdn.net/oceanperfect/article/details/51064574 1.HTTP Keep-Alive在http早期,每个http请求都要求 ...
- 每天一个Linux命令(52)telnet命令
执行telnet指令开启终端机阶段作业,并登入远端主机. (1)用法: 用法: telnet [参数] [主机] (2)功能: 功能: telnet命令通常 ...
- STM32 USB虚拟串口
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出.很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发.本章节来简单概述STM32低端芯片上的USB虚拟串口的移植.在官方 ...
- linux+java+webdriver chrome handless无界面启动
网上现有的解决方案要么是windows下的,要么是python的,搞了一天终于解决了,记录如下. 1 下载chrome linux版和对应版本的webdriver,我这里使用的是chrome66和ch ...
- JMeter学习(七)聚合报告之 90% Line 正确理解
90% Line 参数正确的含义: 虽然,我的上面理解有一定的道理,显然它是错误的.那看看JMeter 官网是怎么说的? 90% Line - 90% of the samples took no m ...
- centos iscsi 配置
首先是服务器的设置:[root@localhost 桌面]# yum install scsi-target-* -y 安装服务 配置yum的方法太简单了,我就不写了[root@local ...
- 使用shell统计字符串出现的次数,并从大到小进行排序显示