ES6完全学习笔记 之 Class(上)基本语法介绍

  • 原创
  • 作者:程序员三丰
  • 发布时间:2022-06-09 18:54
  • 浏览量:552
传统的 ES5 函数式的方式生成实例对象的编程方式与真正的面向对象语言如 C++、Java、PHP 等差异很大,很容易让人感到困惑。所以 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,更像面向对象编程的语法,也让对象原型的写法更加清晰。其实 ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到。

类的由来

  • 先看一个通过传统方法通过构造函数生成实例对象的例子。

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    
    Point.prototype.toString = function () {
        return '(' + this.x + ', ' + this.y + ')';
    }
    
    var p = new Point(1, 2);
    console.log(p.toString());
    
  • ES6 引入了 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.toString());
      
    • 通过 class 关键字,可以定义类。
    • constructor 方法为构造方法。
    • this 关键字代表实例对象。
    • 可以把 ES6 的 class 看做是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法了。
    • 类的所有方法都定义在类的 prototype 属性上面。

      class Point {
          constructor() {
              // ...
          }
      
          toString() {
              // ...
          }
      
          toValue() {
              // ...
          }
      }
      
      // 等同于
      
      Point.prototype = {
          constructor() {},
          toString() {},
          toValue() {}
      }
      
    • 通过 Object.assign() 方法可以方便的一次向类添加多个方法。

      class Point {
          constructor(x, y) {
              this.x = x;
              this.y = y;
          }
      
          toString() {
              return '(' + this.x + ', ' + this.y + ')';
          }
      }
      
      Object.assign(Point.prototype, {
          toValue() {},
          debuger() {}
      });
      
    • 类的内部所有定义的方法,都是不可枚举的。

      console.log(Object.keys(Point.prototype));
      // (2) ['toValue', 'debuger']
      
      console.log(Object.getOwnPropertyNames(Point.prototype));
      // (4) ['constructor', 'toString', 'toValue', 'debuger']
      

constructor 方法

  • constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法。
  • 一个类必须有一个 constructor 方法,如果没有显式定义,会默认添加一个空的 constructor 方法。

    class Point {
        // ...
    }
    
    // 等同于
    
    class Point {
        constructor() {}
    }
    
  • constructor 方法默认返回实例对象(即 this),也可以指定返回另外一个对象。

    class Foo {
                constructor() {
            return Object.create(null);
        }
    }
    
    console.log(new Foo() instanceof Foo);  // false
    
  • 类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。

    class Foo {
        constructor() {
            // return Object.create(null);
        }
    }
    
    Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'
    
    // 下面是普通构造函数
    
    function Animal() {
        return 'Animal';
    }
    
    console.log(Animal()); // 输出 Animal
    

类的实例

  • 使用 new 命令生成类的实例。

  • 与 ES5 一样,实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即定义在 class 上)。

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    
        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    Object.assign(Point.prototype, {
        toValue() {},
        debuger() {}
    });
    
    var point = new Point(2, 3); // 结果输出:(2, 3)
    console.log(point.toString());  // 结果输出:true
    console.log(point.hasOwnProperty('x')); // 结果输出:true
    console.log(point.hasOwnProperty('y')); // 结果输出:true
    console.log(point.hasOwnProperty('toString')); // 结果输出:false
    console.log(point.hasOwnProperty('toValue')); // 结果输出:false
    console.log(point.__proto__.hasOwnProperty('toString')); // 结果输出:true
    console.log(point.__proto__.hasOwnProperty('toValue')); // 结果输出:true
    
  • 与 ES5 一样,类的所有实例共享一个原型对象。

    var point = new Point(2, 3);
    var point2 = new Point(100, 500);
    console.log(point.__proto__ === point2.__proto__); // 结果输出:true
    
  • 可以通过实例的 __proto__ 属性为“类”添加方法,但这属于修改原型,会改变“类”的原始定义,并影响所有实例,必须相当谨慎,不推荐使用,仅做理解即可。

    var point = new Point(2, 3);
    var point2 = new Point(100, 500);
    
    point.__proto__.printName = function () {
        console.log('Test...')
    }
    
    point.printName(); // 结果输出:Test...
    point2.printName(); // 结果输出:Test...
    
    var point3 = new Point(2000, 7000);
    point3.printName(); // 结果输出:Test...
    

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

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

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    
        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }
    
        get z() {
            return 'getter';
        }
    
        set z(value) {
            console.log('setter: ' + value);
        }
    }
    
    let pointZ = new Point(0, 0);
    pointZ.z = 1000; // 控制台会输出:setter: 1000
    console.log(pointZ.z); // 控制台会输出:getter
    
  • 存值函数和取值函数是设置在属性的 Descriptor 对象上。

    var descriptor = Object.getOwnPropertyDescriptor(Point.prototype, "z");
    console.log("get" in descriptor); // true
    console.log("set" in descriptor); // true
    

属性表达式

  • 类的属性名可以使用表达式。

    let methodName = 'getMsg';
    
    class Messager {
        constructor() {
    
        }
    
        [methodName]() {
            console.log('您调用了 getMsg 方法....');
        }
    }
    
    (new Messager()).getMsg();
    (new Messager())[methodName]();
    

Class 表达式

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

    const MyClass = class Mc {
        getClassName() {
            return Mc.name;
        }
    
        getClassName2() {
            return this.constructor.name;
        }
    }
    
    new Mc(); // Uncaught ReferenceError: Mc is not defined
    let mc = new MyClass();
    console.log(mc.getClassName()); // 结果输出:Mc
    console.log(mc.getClassName2()); // 结果输出:Mc
    

    需要注意的是,上面的代码中这个类的名字是 Mc,但是 Mc 只能在 Class 内部可用,只带当前类。
    在 Class 外部,这个类只能使用 MyClass 引用。
    如果内部没有用到的话,可以省略 Mc,可以简写为:const MyClass = class { /_ ... _/}

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

    let person = new class {
        constructor(name) {
            this.name = name;
        }
    
        sayHello() {
            console.log('hello, ', this.name);
        }
    }('张三疯');
    
    person.sayHello();
    

注意点

  • 严格模式
    • 类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。
    • 只要你的代码写在类或模块中,就只有严格模式可用。

      考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

  • 不存在提升

    • 类不存在变量提升,这一点与 ES5 完全不同。

      new Foo(); // Uncaught ReferenceError: Cannot access 'Point' before initialization
      
      class Foo {}
      
  • name 属性
    • name 属性总是返回紧跟在 class 关键字后面的类名。
  • Generator 方法
    • 如果在某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
  • this 的指向
    • 类的方法内部如果还有 this,它默认指向类的实例。
声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/29

文章归档

强烈推荐的PHP全栈开发后台管理系统
buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

推荐文章

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas vue3 three.js 微信支付 PHP全栈开发 Python AI 人工智能 AI辅助 工作经验 实战笔记