定义类(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);
|
数据共享
想让所有实例对象都能够读写同一个内部数据,只需要把这个内部视剧封装在类对象里面,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
关键字来定义类。实际上ES6
的class
只是一个语法糖。
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
在类内部可以试一关键字get
和set
,对某个属性存值和取值,拦截改属性的存取行为。
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();
|
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();
|
8 | var user = new Person();
|
9 | user.className();
|
上面示例代码中,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();
|
静态方法调用了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();
|
父类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();
|
实例属性
实例属性处理可以定义在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);
|
16 | console.log(user._name);
|
静态属性
静态属性指的是 Class
本身的属性, 即Class.propname
, 而不是定义在实例对象(this
) 上的属性。
1 | class Foo {
|
2 | }
|
3 | Foo.prop = 'foo';
|
4 | console.log(Foo.prop);
|
在属性前面加上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;
|