JS6-Class

====Class(类)

Js中,生成实例对象的传统方法是通过构造函数

function Point(x, y) {

this.x = x;

this.y = y;

}

var p = new Point(1, 2);

上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

//定义类

class Point {

constructor(x, y) {

this.x = x;

this.y = y;

}

toString() {

return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)‘;

}

}

//创建对象

var p=new Point(1,2)

console.log(p.x)

上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。

Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

ES6 的类,完全可以看作构造函数的另一种写法。

class Point {

// ...

}

typeof Point // "function"

Point === Point.prototype.constructor // true

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。

使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

class Bar {

doStuff() {

console.log(‘stuff‘);

}

}

var b = new Bar();

b.doStuff() // "stuff"

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

class Point {

constructor() {

// ...

}

toString() {

// ...

}

toValue() {

// ...

}

}

// 等同于

Point.prototype = {

constructor() {},

toString() {},

toValue() {},

};

在类的实例上面调用方法,其实就是调用原型上的方法。

class B {}

let b = new B();

b.constructor === B.prototype.constructor // true

上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。

class Point {

constructor(){

// ...

}

}

Object.assign(Point.prototype, {

toString(){},

toValue(){}

});

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,ES5不用new也可以执行。

class Foo {

constructor() {

return 666;

}

}

Foo()// TypeError: Class constructor Foo cannot be invoked without ‘new‘

类的实例对象

生成类的实例对象的写法,与 ES5 完全一样,也是使用new命令。如果忘记加上new,像函数那样调用Class,将会报错。

class Point {

// ...

}

// 报错

var point = Point(2, 3);

// 正确

var point = new Point(2, 3);

与 ES5 一样,类的所有实例共享一个原型对象。

var p1 = new Point(2,3);

var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__//true

上面代码中,p1和p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的。

这也意味着,可以通过实例的__proto__属性为“类”添加方法。

__proto__ 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的JS引擎中都提供了这个属性,但不建议在生产中使用该属性:

var p1 = new Point(2,3);

var p2 = new Point(3,2);

p1.__proto__.printName = function () { return ‘Oops‘ };

p1.printName() // "Oops"

p2.printName() // "Oops"

var p3 = new Point(4,2);

p3.printName() // "Oops"

上面代码在p1的原型上添加了一个printName方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

Class 表达式

与函数一样,类也可以使用表达式的形式定义。

const MyClass = class Me {

getClassName() {

return Me.name;

}

};

上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类。

let inst = new MyClass();

Me.name // ReferenceError: Me is not defined

上面代码表示,Me只在 Class 内部有定义。

类也可以写成下面的表达式形式。

const MyClass = class {  };

采用 Class 表达式,可以写出立即执行的 Class。

let person = new class {

constructor(name) {

this.name = name;

}

sayName() {

console.log(this.name);

}

}(‘张三‘);

person.sayName(); // "张三"

上面代码中,person是一个立即执行的类的实例。

不存在变量提升

类不存在变量提升,与 ES5 完全不同。

new Foo(); // ReferenceError

class Foo {}

上面代码中,Foo类使用在前,定义在后,这样会报错,因为 ES6 不会把类的声明提升到代码头部。这种规定的原因与继承有关,必须保证子类在父类之后定义。

{

let Foo = class {};

class Bar extends Foo {

}

}

上面的代码不会报错,因为Bar继承Foo的时候,Foo已经有定义了。但是,如果存在class的提升,上面代码就会报错,因为class会被提升到代码头部,而let命令是不提升的,所以导致Bar继承Foo的时候,Foo还没有定义。

this 的指向

类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

class Logger {

printName(name = ‘there‘) {

this.print(`Hello ${name}`);

}

print(text) {

console.log(text);

}

}

const logger = new Logger();

const { printName } = logger;

printName(); // TypeError: Cannot read property ‘print‘ of undefined

上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而导致报错。

解决方法:写在构造函数constructor里。

class Logger {

constructor() {

this.printName = (name = ‘there‘) => {

this.print(`Hello ${name}`);

};

}

}

Class 的取值函数(getter)和存值函数(setter)

在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class MyClass {

constructor() {

// ...

}

get prop() {

return ‘getter‘;

}

set prop(value) {

console.log(‘setter: ‘+value);

}

}

let inst = new MyClass();

inst.prop = 123;// setter: 123

inst.prop// ‘getter‘

上面代码中,prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。

Class 的静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {

static classMethod() {

return ‘hello‘;

}

}

Foo.classMethod() // ‘hello‘

var foo = new Foo();

foo.classMethod()// TypeError: foo.classMethod is not a function

上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

父类的静态方法,可以被子类继承。

class Foo {

static classMethod() {

return ‘hello‘;

}

}

class Bar extends Foo {

}

Bar.classMethod() // ‘hello‘

上面代码中,父类Foo有一个静态方法,子类Bar可以调用这个方法。

Class 的静态属性和实例属性

静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

class Foo {

}

Foo.prop = 1;

Foo.prop // 1

上面的写法为Foo类定义了一个静态属性prop。

目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。

// 以下两种写法都无效

class Foo {

// 写法一

prop: 2

// 写法二

static prop: 2

}

Foo.prop // undefined

目前有一个静态属性的提案,对实例属性和静态属性都规定了新的写法:

类的实例属性

类的实例属性可以用等式,写入类的定义之中。

以前,我们定义实例属性,只能写在类的constructor方法里面。有了新的写法以后,可以不在constructor方法里面定义。

class MyClass {

myProp = 42;

constructor() {

console.log(this.myProp); // 42

}

}

上面代码中,myProp就是MyClass的实例属性。在MyClass的实例上,可以读取这个属性。

这种写法比以前更清晰:

class Person{

it= {

count: 3

};

}

为了可读性的目的,对于那些在constructor里面已经定义的实例属性,新写法允许直接列出:

class Person{

state;

constructor() {

this.state = {

count: 0

};

}

}

类的静态属性

类的静态属性只要在上面的实例属性写法前面,加上static关键字就可以了。这个新写法大大方便了静态属性的表达。

// 老写法

class Foo {

// ...

}

Foo.prop = 1;

// 新写法

class Foo {

static prop = 1;

}

上面代码中,老写法的静态属性定义在类的外部。整个类生成以后,再生成静态属性。这样让人很容易忽略这个静态属性,也不符合相关代码应该放在一起的代码组织原则。

new.target属性

new是从构造函数生成实例的命令。ES6 为new命令引入了一个new.target属性,该属性一般用在在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

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 生成实例‘);

}

}

var person = new Person(‘张三‘); // 正确

var notAPerson = Person(‘张三‘);  // 报错

上面代码确保构造函数只能通过new命令调用。

时间: 2024-08-08 22:07:03

JS6-Class的相关文章

[JS6] 通过用户事件事件执行脚本

1 <html> 2 <head> 3 <title>通过用户事件事件执行脚本</title> 4 <SCRIPT TYPE="text/JavaScript"> 5 <!-- 6 function shownews(){ 7 alert("单击按钮事件执行脚本") 8 } 9 //--> 10 </SCRIPT> 11 </head> 12 <body> 1

[ExtJS6学习笔记]Ext JS6主题系列 (Classic工具包)

本文作者:苏生米沿 本文地址:http://blog.csdn.net/sushengmiyan/article/details/50186709 翻译来源:http://docs.sencha.com/extjs/6.0/core_concepts/theming.html ExtJS提供的可以使用的主题包对于创建一个干净专业的程序来说已经很有创意了,然而,你可能还是会希望提供自己的一种设计方式或现在存在的企业设计方式. 从历史上来说,给程序美化就是指的给html标签提供渲染组件的规则,但是这

js6类和对象

[javascript] view plain copy // 第一种:对象 var person = {};// 或者var obj = new Object(); person.name = "king"; person.age = 20; person.Introduce = function () { alert("My name is " + this.name + ". My age is " + this.age); }; pers

JS6(内置对象)

JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的,如:字符串的长度.图像的长宽等: 对象的方法:能够在对象上执行的动作.例如,表单的"提交"(Submit),时间的"获取"(getYear)等: JavaScript 提供多个内建对象,比如 String.Date.Array 等等,使用对象前先定义,如下使用数组对象: var objectName =new Array();

js6:history和navigator对象的学习

原文发布时间为:2008-11-08 -- 来源于本人的百度文章 [由搬家工具导入] <html> <head> <title>js</title> </head> <body><input type="button" name="goto" Value="Back" onClick="history.back()"><input typ

js-6

面向对象 Js一开始就是写网页特效,面向过程的,作者发现这样写不好,代码重复利用率太高,计算机内存消耗太大,网页性能很差.所以作者就收到java和c的影响,往面向对象靠齐.Js天生有一个Object,但他没有面向对象的特征(封装,继承,多态).最后研究出了构造函数. 构造函数最终目的是生成对象. 我们把封装的那个函数叫 类 类里面的属性是私有的. 类原型上的属性是公有的,可继承的. 类身上天生自带一个prototype的属性,属性是对象 Prototype对象天生有一个constructor属性

JavaScript数组、字符串相关操作

任务描述 l  基于任务四进行升级 l  将新元素输入框从input改为textarea l  允许一次批量输入多个内容,格式可以为数字.中文.英文等,可以通过用回车,逗号(全角半角均可),顿号,空格(全角半角.Tab等均可)等符号作为不同内容的间隔 l  增加一个查询文本输入框,和一个查询按钮,当点击查询时,将查询词在各个元素内容中做模糊匹配,将匹配到的内容进行特殊标识,如文字颜色等.举例,内容中有abcd,查询词为ab或bc,则该内容需要标识 <textarea> 标签定义多行的文本输入控

使用FileReader接口读取文件内容

如果想要读取或浏览文件,则需要通过FileReader接口,该接口不仅可以读取图片文件,还可以读取文本或二进制文件,同时,根据该接口提供的事件与方法,可以动态侦察文件读取时的详细状态,接下来,我们详细介绍FileReader接口的使用方法. FileReader 接口 FileReader 接口提供了一个异步的API,通过这个API可以从浏览器主线程中异步访问文件系统中的数据,基于此原因,FileReader 接口可以读取文件中的数据,并将读取的数据放入内存中. 当访问不同文件时,必须重新调用F

js兵器谱之魔法召唤师:call / apply

ps:以下为笔者在爬莲花山时,大脑某些神经元突起闪现火花,最后转换为一堆文字片段 当卷福,or不,应该是当奇异博士(doctor strange)被 ancient one扔在珠穆朗玛山顶快要冻死的时候,情急之下,他终于召唤出了传送门. 魔法的世界,无处不在,作为麻瓜的我,学了js后,猛然间发现,js中居然也是魔法的一部分. call/apply,其实是代码重用在js中的一个重要体现. 这两货是函数自带的方法. 函数,本质上是一堆打包在一块的代码语句,能实现某种功用. js中的函数,又是对象.

history.back(-1)和history.go(-1)的区别

history.back(-1):直接返回当前页的上一页,数据全部消息,是个新页面 history.Go(-1):也是返回当前页的上一页,不过表单里的数据全部还在 history.back(0) 刷新 history.back(1) 前进 history.back(-1) 后退 <input type=button value=刷新 onclick="window.location.reload()"> <input type=button value=前进 oncl