ECMAScript6之Class
1、Class的基本语法
1、1简介
基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
1、2静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo { static classMethod() { return 'hello'; } }
Foo.classMethod() // 'hello'
var foo = new Foo(); foo.classMethod() // TypeError: foo.classMethod is not a function
注意,如果静态方法包含this关键字,这个this指的是类,而不是实例
class Foo { static bar() { this.baz(); } static baz() { console.log('hello'); } baz() { console.log('world'); } } Foo.bar() // hello
虽然既然类的静态方法不可被实例所继承,但是却可以被子类继承,不赘述。
1、2实例属性
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。写法对比:
//实例属性this._count定义在constructor()方法里面 class IncreasingCounter { constructor() { this._count = 0; } get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; } }
//属性定义在类的最顶层,其它不变 class IncreasingCounter { _count = 0; get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; } }
这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。
1、3静态属性
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
class Foo { } Foo.prop = 1; Foo.prop // 1
目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性法的前面,加上static
关键字。
// 老写法 class Foo { // ... } Foo.prop = 1; // 新写法 class Foo { static prop = 1; }
1、4构造方法
一个类必须要有 constructor 方法,如果没有显示定义,一个空的 constructor 方法会被默认添加
class Animal { } // 等同于 class Animal { constructor() {} }
上面代码中,定义了一个空的类Animal,JavaScript 引擎会自动为它添加一个空的constructor()
方法。
constructor()
方法默认返回实例对象(即this
),完全可以指定返回另外一个对象。
class Foo { constructor() { return Object.create(null); } } new Foo() instanceof Foo // false
上面代码中,ES6constructor
()方法返回一个全新的对象,结果导致实例对象不是Foo
类的实例。
类必须使用new
调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new
也可以执行。
class Foo { constructor() { return Object.create(null); } } Foo() // TypeError: Class constructor Foo cannot be invoked without 'new'
2、Class的继承
2、1简介
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
- 子类必须在constructor方法中调用super方法,否则新建实例时会报错
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() } }
这是因为子类自己的this对象,必须通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后对其加工,加上子类自己的实例属性和方法,如果不调用super方法,子类就得不到this对象。
- 父类的静态方法,也会被子类继承
class A { static hello() { console.log('hello world'); } } class B extends A { } B.hello() // hello world
hello()是A类的静态方法,B继承A,业绩承了A的静态方法。
2、2super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同.
- 当做函数使用
class parent { constructor() { console.log(11) } } class child extends parent{ constructor() { super(); } } let c = new child();//打印11
当做函数使用时,super()调用会生成一个空对象,作为context来调用父类的constructor,返回this对象,作为子类constructor的context继续调用构造函数。
2. 当做对象使用
const proto = { foo: 'hello' }; const obj = { foo: 'world', find() { return super.foo; } }; Object.setPrototypeOf(obj, proto); obj.find() // "hello"
上面代码中,对象obj.find()方法之中,通过super.foo引用了原型对象proto的foo属性。
2、3this关键字
JavaScripte中的this可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式
- this关键字最终指向的是调用它的对象
function GetThis(){ console.log(this); }; GetThis();//打印出window对象
最后的调用我们也可以写成window.GetThis();调用他的就是window对象。其实window.可以省略不写。
var getThis={ user:'me', fn:function(){ console.log(this); } } getThis.fn();//打印的就时getThis对象
this含义如此之多的原因
JavaScripte中的函数既可以被当作普通函数执行,也可以作为对象的方法执行。一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建 改执行环境时,JavaScript首先会创建argument变量,其中包含调用函数时传入的参数。接下来创建作用域链。然后初始化变量,首先初始化 函数的形参表,值为argument变量中对应的值,如果argument变量中没有对应值,则该形参初始化为undefined。如果该函数中有内部函数,则初始化这些内部函数。如果没有,继续初始化改函数内定义的局部变量,需要注意的是此时这些变量初始化为undefined,其复制操作在执行环境创建成功后,函数执行时才会执行。最后为this变量赋值,如前所述,会根据函数调用方式的不同,付给this全局对象,当前对象,至此,函数的执行环境创建成功,函数开始执行,所需变量均从之前构建好的执行环境中读取。