JavaScript中,生成实例的对象,通常通过构造函数,如下:
function Fn(x,y){
this.x = x
this.y = y
}
Fn.prototype.toString = function(){
renturn '('+this.x+','+this.y+')'
}
var p = new Fn(1, 2)
Es6中引入class概念,可以看做是一个语法糖
上面的代码用Es6来写
class Fn{
constructor(x,y){
this.x = x
this.y = y
}
toString(){
return '('+this.x+''+this.y+')'
}
}
这里有一个constructor方法
这个Es6的Fn类的构造方法 就相当于 Es5中的构造函数 Fn
原型链中的扩展发方法,也可以直接在 class里面写出
calss Fn(){ }
typeof Fn // function
Fn === Fn.prototype.constructor // true
上述代码说明类的数据类型就是函数
类本身就是构造函数 也是对类使用new命令
class Animal(){
bark(){}
}
let dog = new Animal()
dog.bark()
构造函数的prototype属性在Es6的类上面继续存在
事实上 类的所有方法都定义在类的prototype属性上面
class Point{
constuctor(){}
toString(){}
toValue(){}
}
等同于:
Point.prototype = {
constructor(){},
toString(){},
toValue(){}
}
此类实例的调用方法,实际就是在调用原型上的方法
class B{}
let b = new B();
b.constructor === B.prototype.constructor //true
b就是B的实例,它的consturctor方法就是B类constructor方法
由于类的方法都定义在prototype对象上面,所以类的新方法,可以添加在prototype上面
Object.assign方法可以很方便的一次向类添加多个方法
class Point {
constructor(){}
Object.assign(Point.prototype,{
toString(),
toValue()
})
}
另外类的所有定义发方法都是不可枚举的
但是Es5中方法可枚举
var Point = function (x, y) {
// ...
};
Point.prototype.toString = function() {
// ...
};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
至于constructor方法是类的默认方法,通过new命令生成实例对象时,自动调用该方法
一个类必须要有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加
sonstructor方法默认返回实例对象,完全可以指定返回另外一个对象,
class Foo{
constructor(){
return Object.Create(null)
}
}
new.Foo() instanceof Foo //false
生成类的实例的写法
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
与Es5完全一样,也是使用new命令,如果忘记加new 像函数那样调用class会报错,实例的属性除非显示定义在其本身,否则都是定义在原型上
class Point{
constructor(x,y){
this.x = x
this.y = y
}
toString(){
return '(' + this.x + ', ' + this.y + ')';
}
let point = new Point(2,3)
point.toString()
point.hasOwnProperty('x')
point.hasOwnProperty('toString')
point.__proto__.hasOwnProperty('toString')
}
与Es5一样,类的所有实例共享一个原型对象。
var p1 = new Point(2,3)
var p2 = new Point(3,2)
p1._proto__ === p2.__proto__
我们可以用object.prototypeof方法来获取实例对象的原型然后来为原型添加方法
取值和存值
取值函数(getter)和存值函数(setter)
class MyClass {
constructor(){
}
get prop(){
return 'getter'
}
set pro(value){
console.log(value)
}
}
let inst = new MyClass()
inst.props = 123
console.log(inst.prop)
上面代码中,prop属性有对应的存值和取值函数
属性表达式 类的属性名,可以采用表达式
let methodName = 'getArea'
class square {
constructor(length){
}
[methodName](){
}
}
class表达式
类名,也可以用表达式
const myClass = class Me{
getClassName(){
return Me.name
}
}
采用表达式,可以写出立即执行的class
重点关注
类和模块内部默认是严格末世,不需要使用 use strict指定运行模式
不存在提升
name 属性
由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。
class Point {}
Point.name // "Point"
name属性总是返回紧跟在class关键字后面的类名。
(4)Generator 方法
如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x);
}
// hello
// world
上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for…of循环会自动调用这个遍历器。
this 的指向
类的方法内部如果含有this,它默认指向类的实例。
解决办法
构造函数中绑定this
构造方法使用箭头函数
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
另一种解决方法是使用箭头函数。
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
静态方法 § ⇧ 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function