考虑到JavaScript中的几乎所有东西都是对象,面向对象的JavaScript代码与其他面向对象的语言非常不同。
来自C++背景,我意识到面向对象的编程范式,并对Objects 和 Classes **应该如何工作的非常坚实的想法。
首先,JavaScript对象的创建方式非常不同. 对象实例可以使用 新操作员创建:
1let Reptile = new Object() {
2 // ...
3}
或具有函数构建器
1function Reptile() {
2 // ...
3}
其次,JavaScript对象是非常灵活的,而经典面向对象的语言只允许属性更改或属性插槽,JavaScript允许对象更改其属性和方法,即JavaScript对象有属性和方法插槽。
我的第一个想法是是的,自由!
但这带来了成本 - 需要了解JavaScript中的原型属性。
所有JavaScript对象都是从Object
构建器创建的:
1var Reptile = function(name, canItSwim) {
2 this.name = name;
3 this.canItSwim = canItSwim;
4}
而原型
使我们能够为物体构造者添加新的方法,这意味着以下方法现在在所有爬行动物
的例子中都存在。
1Reptile.prototype.doesItDrown = function() {
2 if (this.canItSwim) {
3 console.log(`${this.name} can swim`);
4 } else {
5 console.log(`${this.name} has drowned`);
6 }
7};
现在可以创建爬行动物
的对象实例:
1// for this example consider alligators can swim and crocs cannot
2let alligator = new Reptile("alligator", true);
3alligator.doesItDrown(); // alligator can swim
4
5let croc = new Reptile("croc", false);
6croc.doesItDrown(); // croc has drowned
爬行动物
对象的原型
现在是继承的基础,DoesItDrown
方法既可供alligator
和croc
访问,因为爬行动物
的原型
有这种方法。
现在,由于有方法插槽和一个共同的原型
实例属性在所有实例中共享,一些非常干净的技巧是可能的,这对C++的人来说是非常奇怪的:
1croc.__proto__.doesItDrown = function() {
2 console.log(`the croc never drowns`);
3};
4
5croc.doesItDrown(); // the croc never drowns
6alligator.doesItDrown(); // the croc never drowns
改变一个实例的原型
属性或方法,对象的所有实例都会受到影响,这意味着我们也可能正在删除物品。
1delete croc.__proto__.doesItDrown
2alligator.doesItDrown();
3
4//TypeError: alligator.doesItDrown
5// is not a function
现在没有人能游泳。
这只是一个愚蠢的例子来展示JavaScript中的原型
对Object系统的根本性,以及它对来自其他面向对象语言的人来说是多么令人不安。
在 ES6 语法中,JavaScript 提供了创建类的功能。
然而,真实类的概念在JavaScript中并不存在,而是通过原型
模拟而成,类语法只是围绕它的语法糖,因此,理解这种行为对于实现ES6类的便利性和局限性至关重要。
使用新的类
语法,爬行动物
将被定义为:
1class Reptile {
2 constructor (name, canItSwim) {
3 this.name = name;
4 this.canItSwim = canItSwim;
5 }
6
7 doesItDrown () {
8 if(this.canItSwim)
9 console.log(`${this.name} can swim`);
10 else
11 console.log(`${this.name} has drowned`);
12 }
13}
14
15let alligator = new Reptile("alligator", true);
16alligator.doesItDrown(); //alligator can swim
这并不意味着它不会为原型
用户带来任何新鲜事物,一些陷阱可以通过使用ES6类来避免,例如使创建实例的新
关键字是强制性的。
1let croc = Reptile("croc", false);
2//TypeError: Class constructor Reptile cannot be invoked without 'new'
这实际上是件好事,因为它防止在使用对象属性和方法时访问错误的背景,这通常是全球范围或窗口对象。
结论
虽然JavaScript目前确实缺乏像真正私人成员这样的功能,它已经通过类语法创建了对象,而不是原型类似于其他OO语言(如C++/Java)的类。
PS. 对于在 JavaScript 类中创建真正的私人会员,TC39 提出了一项建议,你可以跟随它(https://tc39.github.io/proposal-class-fields)并贡献你的意见。
1class Foo {
2 #a; #b; // # indicates private members here
3 #sum = function() { return #a + #b; };
4}
5
6// personally this format reminds me of $variable in PHP.
7// I'm not sure if that's a good thing