JavaScript类

定义类(class)

构造函数

通过构造函数模拟类,在内部使用this指向实例对象。使用new关键字来生成实例。

1
function Cat(name) {
2
  this.name = name;
3
}
4
5
var cat = new Cat('向日葵');
6
console.log(cat.name);       // 向日葵

Object.create()

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

1
var person = {
2
  name:'李磊',
3
  sayHi:function() {
4
    console.log(`你好!我叫${this.name}`);
5
  }
6
};
7
var user = Object.create(person);
8
user.sayHi(); // 你好!我叫李雷

极简主义法

封装

用一个对象模拟类,在这个类里面定义一个构造函数,来生成实例。

1
var Cat = {
2
  create:function() {
3
    var cat = {};
4
    cat.name = '向日葵';
5
    cat.eat = function() {
6
      console.log(`${this.name}吃东西!`);
7
    }
8
    return cat;
9
  }
10
};
11
12
var cat1 = Cat.create();
13
cat1.eat();

继承

继承的实现,只需要让需要继承的类在create方法中,调用被继承的create方法。

1
var Animal = {
2
  create:function(){
3
    var animal = {};
4
    animal.name = '喵喵';
5
    animal.eat = function(){
6
      console.log(`${this.name}吃东西!`);
7
    }; 
8
    return animal;
9
  }
10
}
11
12
var Cat = {
13
  create:function(){
14
    var cat = Animal.create();
15
    cat.name = '小太阳';
16
    cat.call = function(){
17
      console.log(`喵 ${this.name}`);
18
    };
19
    return cat;
20
  }
21
}
22
23
var cat = Cat.create();
24
cat.eat();
25
cat.call();

这样Cat实例就有了Cat类和Animal类的全部属性和方法。

私有属性和私有方法

create方法中,只要不是定义在cat对象上的方法和属性都是私有的。

1
var Cat = {
2
  create:function() {
3
    var cat = {};
4
    var sound = '喵喵喵';
5
    cat.name = '向日葵';
6
    cat.eat = function() {
7
      console.log(`${this.name}吃东西!`);
8
    }
9
    return cat;
10
  }
11
};
12
13
var cat = Cat.create();
14
console.log(cat.sound);     // undefined

数据共享

想让所有实例对象都能够读写同一个内部数据,只需要把这个内部视剧封装在类对象里面,create方法外面即可。

1
var Cat = {
2
  sound:'喵喵喵',
3
  create:function() {
4
    var cat = {};
5
    cat.name = '向日葵';
6
    cat.call = function() {
7
      console.log(`${this.name}${Cat.sound}!`);
8
    };
9
    cat.changeSound = function(text){
10
      Cat.sound = text;
11
    };
12
    return cat;
13
  }
14
};
15
16
var cat1 = Cat.create();
17
var cat2 = Cat.create();
18
cat1.call();  // 向日葵喵喵喵!
19
cat2.changeSound('嗷。。呜。。');
20
cat2.call();  // 向日葵嗷。。呜。。!
21
cat1.call();  // 向日葵嗷。。呜。。!

Class 关键字

ES6 引入了Class(类)的概念。通过class关键字来定义类。实际上ES6class只是一个语法糖。

1
class Cat {
2
  constructor(name) {
3
    this.name = name;
4
  }
5
  eat(){
6
    console.log(`${this.name}吃东西!`);
7
  }
8
}
9
var cat = new Cat('向日葵');
10
car.eat();

constructor 方法

constructor 方法是类的默认方法,通过new来生成实例时,自动调用改方法。
如果没有定义constructor方法,就会有一个默认空的constructor方法存在。
constructor是一个构造方法。相当于ES5的构造函数Cat()。在类里面定义方法直接把函数定义放进去即可,方法之间不需要逗号分隔。
也是通过new来实例化类,跟构造函数一致。

1
class Cat{}
2
3
// 等同于
4
class Cat{
5
  constructor() {}
6
}

constructor方法默认返回实例对象(this),也可以指定返回另外一个对象。

1
class Cat {
2
  constructor() {
3
    return Object.create(null);
4
  }
5
}
6
var cat = new Cat(); 
7
console.log(cat);       // {}

getter 和 setter

在类内部可以试一关键字getset,对某个属性存值和取值,拦截改属性的存取行为。

ES5:

1
function Cat(name) {
2
  this._name = name;
3
}
4
5
Cat.prototype = {
6
  get name(){
7
    return this._name;
8
  },
9
  set name(name){
10
    this._name = name;
11
  }
12
}
13
14
var cat = new Cat('向日葵');
15
console.log(cat.name);       // 向日葵
16
cat.name = '小太阳';
17
console.log(cat.name);

ES6:

1
class Cat {
2
  constructor(name) {
3
    this._name = name;
4
  }
5
  get name(){
6
    return this._name;   
7
  }
8
  set name(name){
9
    this._name = name;
10
  }
11
}
12
let cat = new Cat('向日葵');
13
console.log(cat.name);       // 向日葵
14
cat.name = '小太阳';
15
console.log(cat.name);       // 小太阳

Class 表达式

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

1
let CatClass = class Cat {
2
  constructor(name) {
3
    this.name = name;  
4
  } 
5
  sayName(){
6
    console.log(this.name);
7
  } 
8
}
9
let cat = new CatClass('向日葵');
10
cat.sayName();    // 向日葵

采用Class表达式,可以写出类似函数那样的立即执行的Class

1
let cat = new class {
2
  constructor(name) {
3
    this.name = name;  
4
  } 
5
  sayName(){
6
    console.log(this.name);
7
  } 
8
}('向日葵');
9
cat.sayName();  // 向日葵

this 指向

类内部的this默认指向类的实例,不能单独使用。

1
class Cat {
2
  constructor(name) {
3
    this.name = name;
4
  }
5
  call(){
6
    console.log(`喵 ${this.name}`);
7
  }
8
}
9
let cat = new Cat('向日葵');
10
const {call} = cat;
11
call();  // Cannot read property 'name' of undefined

this指向的不再是Cat,而是运行时所在的环境,这时就会找不到name而报错。

构造函数内部绑定

1
class Cat {
2
  constructor(name) {
3
    this.name = name;
4
    this.call = this.call.bind(this); 
5
  }
6
  call(){
7
    console.log(`喵 ${this.name}`);
8
  }
9
}
10
let cat = new Cat('向日葵');
11
const {call} = cat;
12
call();

箭头函数

箭头函数内部的this指向定义时所在的对象。

1
class Cat {
2
  constructor(name) {
3
    this.name = name;
4
    this.call = () => {
5
      console.log(`喵 ${this.name}`);
6
    }
7
  }
8
}
9
let cat = new Cat('向日葵');
10
const {call} = cat;
11
call();

静态方法

在类在定义的方法都会被继承,如果在方法前加上static关键字,就表示改方法不会被实例继承,只能通过类调用,这就叫静态方法。

1
class Person {
2
  static className(){
3
    return Person.name;
4
  }
5
}
6
7
Person.className();  // Person
8
var user = new Person();
9
user.className();    // user.className is not a function

上面示例代码中,Person类的className()方法前有关键字static,表明这是一个静态方法需要在Person类上调用.如果通过实例来调用,会抛出一个错误:这不是一个方法。

如果静态方法内有this关键字,那么这个this指向的是类,而不是实例对象。

1
class Foo {
2
  static a(){
3
    this.b();
4
  }
5
  static b(){
6
    console.log('static b');
7
  }
8
  b(){
9
    console.log('b');
10
  }
11
}
12
Foo.a();    // static b

静态方法调用了this.b().这里最终输出了 static b,说明它调用的是静态方法b(),this指向的是Foo类。静态方法可以与非静态方法重名。

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

1
class Foo {
2
  static className(){
3
    return this.name;
4
  }
5
}
6
class Bar extends Foo{
7
  
8
}
9
Bar.className();    // Bar

父类Foo有一个静态方法,子类Bar可以调用这个方法。

静态方法也是可以从super对象上调用的。

1
class Foo {
2
  static className(){
3
    return this.name;
4
  }
5
}
6
class Bar extends Foo{
7
  static className(){
8
    return super.className();
9
  }
10
}
11
Bar.className();    // Bar

实例属性

实例属性处理可以定义在constructor()方法里面的this上,也可以定义在类的最顶层。

1
class Person {
2
  _age = 0;
3
  constructor(name) {
4
    this._name = name;
5
  }
6
  get value(){
7
    return this._age;
8
  }
9
  set value(age){
10
    this._age = age;
11
  }
12
}
13
14
let user = new Person('李雷');
15
console.log(user._age);       //  0
16
console.log(user._name);      //  李雷

静态属性

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

1
class Foo {
2
}
3
Foo.prop = 'foo';
4
console.log(Foo.prop);    // foo

在属性前面加上static关键字,也可以定义静态属性。

1
class Foo {
2
  static prop = 1;
3
}

私有方法和私有属性

私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。

私有属性:在属性名前,使用#表示。

1
class Person {
2
  #age = 0;
3
  get value(){
4
    return this.#age;
5
  }
6
  set value(age){
7
    this.#age = age;
8
  }
9
}
10
let user = new Person();
11
console.log(user.#age);           // Private field '#age' must be declared in an enclosing class
12
user.#age = 20;       // Private field '#age' must be declared in an enclosing class

#age 就是私有属性,如果在外部使用就会报错。

私有方法:在方法前加#

私有属性也可以设置 getter 和 setter 方法。

1
class Person {
2
  #age = 0;
3
  #print(){
4
    console.log(#age);
5
  }
6
  printAge(){
7
    this.#print();
8
  }
9
  get #value(){
10
    return #age;
11
  }
12
  set #value(age){
13
    this.#age = age;
14
  }
15
}
16
17
let person = new Person();
18
person.printAge();
19
person.#value = 20;

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!