ES6 (二十一)Class 的基本语法、constructor 方法、static关键字(静态方法)、私有方法和私有属性#

本文详细介绍了ES6中的Class特性,包括Class的基本语法、constructor方法、static关键字(用于定义静态方法)、私有方法和私有属性的概念。Class实质上是构造函数的语法糖,其方法默认不可枚举。通过实例属性的新写法,可以为类添加方法。静态方法不会被实例继承,而是通过类直接调用。私有方法和私有属性是当前讨论的热点,通过Symbol或特定提案实现,确保了方法和属性的封装性。文章还探讨了new.target属性的应用,用于判断构造函数的调用方式。
摘要由CSDN通过智能技术生成

ES6 (二十一)Class 的基本语法、constructor 方法、static关键字(静态方法)、私有方法和私有属性#

1. 简介

总结:

  • ES6 的类,完全可以看作构造函数的另一种写法。类的数据类型就是函数,类本身就指向构造函数。
  • 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。
  • 在类的实例上面调用方法,其实就是调用原型上的方法。
  • (类的实例小节):实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上),可以通过实例的__proto__属性为“类”添加方法(__proto__ 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,因此必须十分谨慎,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,然后再来为原型添加方法/属性。)。
  • 类的内部所有定义的方法,都是不可枚举的(non-enumerable)。与 ES5 的行为不一致。

类的由来

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。

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);

上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。


传统ES5:

面向对象编程的第一步,就是要生成对象。前面说过,对象是单个实物的抽象。通常需要一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。

典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓==“类”就是对象的模板,对象就是“类”的实例==。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)

JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。

构造函数就是一个普通的函数,但具有自己的特征和用法。

var Vehicle = function () {
    
  this.price = 1000;
};

上面代码中,Vehicle就是构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写。

构造函数的特点有两个。(函数内部使用this关键字就可以理解为构造函数

  • 函数体内部使用了this关键字,代表了所要生成的对象实例
  • 生成对象的时候,必须使用new命令。

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。

语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

实际上从面向过程到面向对象也是一种语法糖。

需要声明的是“语法糖”这个词绝非贬义词,它可以给我们带来方便,是一种便捷的写法,编译器会帮我们做转换;而且可以提高开发编码的效率,在性能上也不会带来损失。

class Point {
   
  constructor(x, y) {
   
    this.x = x;
    this.y = y;
  }

  toString() {
   
    return '(' + this.x + ', ' + this.y + ')';
  }
}

上面代码定义了一个“类”,可以看到里面有一个constructor()方法,这就是构造方法,而==this关键字则代表实例对象==(与前面的引用中说的一样)。这种新的 Class 写法,本质上与本章开头的 ES5 的构造函数Point是一致的。

Point类除了构造方法,还定义了一个toString()方法。注意,定义toString()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法与方法之间不需要逗号分隔,加了会报错。

ES6 的类,完全可以看作构造函数的另一种写法。

class Point {
   
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。

使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

class Bar {
   
  doStuff() {
   
    console.log('stuff');
  }
}

const b = new Bar();
b.doStuff() // "stuff"

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

class Point {
   
  constructor() {
   
    // ...
  }

  toString() {
   
    // ...
  }

  toValue() {
   
    // ...
  }
}

// 等同于

Point.prototype = {
   
  constructor() {
   },
  toString() {
   },
  toValue() {
   },
};

上面代码中,constructor()toString()toValue()这三个方法,其实都是定义在Point.prototype上面。

因此,在类的实例上面调用方法,其实就是调用原型上的方法。

class B {
   }
const b = new B();

b.constructor === B.prototype.constructor // true

上面代码中,bB类的实例,它的constructor()方法就是B类原型的constructor()方法。

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign()方法可以很方便地一次向类添加多个方法。

class Point {
   
  constructor(){
   
    // ...
  }
}

Object.assign(Point.prototype, {
   
  toString(){
   },
  toValue(){
   }
});

prototype对象的constructor()属性,直接指向“类”的本身,这与 ES5 的行为是一致的。

Point.prototype.constructor === Point // true

另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。

class Point {
   
  constructor(x, y) {
   
    // ...
  }

  toString() {
   
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

上面代码中,toString()方法是Point类内部定义的方法,它是不可枚举的。这一点与 ES5 的行为不一致。

var Point = function (x, y) {
   
  // ...
};

Point.prototype.toString = function () {
   
  // ...
};

Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

上面代码采用 ES5 的写法,toString()方法就是可枚举的。

constructor 方法

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

class Point {
   
}

// 等同于
class Point {
   
  constructor() {
   }
}

上面代码中,定义了一个空的类Point,JavaScript 引擎会自动为它添加一个空的constructor()方法。

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

class Foo {
   
  constructor() {
   
    return Object.create(null); //默认返回实例对象,但此处指定了一个新的对象
  }
}

new Foo() instanceof Foo
// false

上面代码中,constructor()函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

class Foo {
   
  constructor() {
   
    return Object.create(null);
  }
}

Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'

类的实例

生成类的实例的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错。

class Point {
   
  // ...
}

// 报错
var point = Point(2, 3);

// 正确
var point = new Point(2, 3);

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

//定义类
class Point {
   

  constructor(x,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChrisP3616

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值