前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【译】浅谈 JavaScript 里的面向对象

【译】浅谈 JavaScript 里的面向对象

作者头像
腾讯IVWEB团队
发布2020-06-28 00:24:20
4110
发布2020-06-28 00:24:20
举报

本文翻译自 https://alligator.io/js/objects-prototypes-classes/ ,图片来自 www.digitalocean.com,内容以意译为主,欢迎交流。

JavaScript 里的对象,属性和类

JavaScript 里几乎所有的事物都是对象因此 JS 的面向对象跟其他的经典语言很不一样,比如 JS 的面向对象是基于原型的,而不是基于类的。

我本人是写 C++ 的,对面向对象也有一定的了解,但也因此对类和对象的工作原理的理解得非常传统。对比诸如像 Java 一样的语言之后,我感觉我对类和对象的理解更传统了。

当上面这类语言还在自己的类和对象的语义里倒腾的时候,对于初学者来说,JavaScript 里的对象系统确实非常先进。为什么这么说呢,首先第一点就是 JS 里对象创建的方式就很不一样,在 JS 里,可以直接使用 new 来创建对象:

代码语言:javascript
复制
let Reptile = new Object();

我们甚至不需要类,通过一类叫做构造函数的函数来创建对象:

代码语言:javascript
复制
function Reptile() {
  // ...
}

let reptile = new Reptile()

第二点,JavaScript 对象非常灵活,在经典的面向对象语言里只有能修改或添加对象属性,而 JavaScript 还可以修改添加对象的方法,也就是对象上可以同时修改添加属性或者方法。

一开始,我心里暗爽:“自由啊,爽啊”,但很快,我发现这会让我不得不去思考 JavaScript 对象的原型属性,因为想要在 JS 里写面向对象,原型的概念很重要。

所有的 JS 对象都会有 Object 构造函数创建:

代码语言:javascript
复制
var Reptile = function(name, canItSwim) {
  this.name = name;
  this.canItSwim = canItSwim;
}

而且所谓原型允许我们添加新的方法到对象构造器上,这意味着下面的方法 doesItDrown 存在于所有 Reptile 的实例上:

代码语言:javascript
复制
Reptile.prototype.doesItDrown = function() {
  if (this.canItSwim) {
    console.log(`${this.name} can swim`);
  } else {
    console.log(`${this.name} has drowned`);
  }
};

Reptile 的实例按照下面的方式创建:

代码语言:javascript
复制
// 在这个里例子我们假定 alligators 会游泳但 crocs 不会。 
let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); // alligator 会游泳

let croc = new Reptile("croc", false); 
croc.doesItDrown(); // croc 被淹死了

Reptile 实例的原型是其实现继承的继承,上述中的 doesItDrown 对于 alligator 和 croc 来说都可以访问到因为 Reptile 的原型上有可以找到它,而这个原型在 Reptile 的所有实例里都可以通过 __proto__ 访问到。

对象很自由,可以随意修改,而且所有实例其实都共享一个原型,这使得我们也可以随意修改原型上的东西,这会让写 C++ 的觉得非常古怪:

代码语言:javascript
复制
croc.__proto__.doesItDrown = function() {
  console.log(`the croc never drowns`);
};

croc.doesItDrown(); // the croc never drowns
alligator.doesItDrown(); // the croc never drowns

只要我们改变了一个实例的 prototype 上的属性或者方法,其他的实例都会被影响,即使是删除属性也是一样的,比如 croc 不想被淹死,它可以这样做:

代码语言:javascript
复制
delete croc.__proto__.doesItDrown
alligator.doesItDrown();
// TypeError: alligator.doesItDrown
// is not a function

上面写的例子说明了 JS 对象系统里 prototype 的重要性,而且说明了它跟其他传统对象语言的不同行为。

有了 ES6 语法,我们可以用 class 来创建对象了,但是,JavaScript 里并没有真正的类,所谓 es6 的 class 语法其实是 prototype 的语法糖,因此在编写代码的时候应该额外注意到 ES6 的方便以及它的局限性,例如上述的游泳例子用 ES6 写:

代码语言:javascript
复制
class Reptile {
  constructor (name, canItSwim) {
    this.name = name;
    this.canItSwim = name;
  }

  doesItDrown () {
   if(this.canItSwim) 
    console.log(`${this.name} can swim`);
   else
    console.log(`${this.name} has drowned`);
  }
}

let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); //alligator can swim

当然,这并不意味着 class 语法除了是语法糖以外一无是处,相反的,用 ES6 的类来写代码可以避免很多坑,比如可以强制开发者使用 new 来创建对象:

代码语言:javascript
复制
let croc = Reptile("croc", false);
// 会报错:
// TypeError: Class constructor Reptile
// cannot be invoked without 'new'

这显然是好事,因为它阻止了构造函数访问错误的上下文,也就是 this(一般这种不用 new 的情况下的 this 是全局作用域或者 window 对象)

结尾

尽管 JavaScript 目前还缺乏一些诸如私有属性的特性,它还是支持像 C++ 和 Java 里使用类而不是原型来创建对象的,当然,这里的类只是 prototype 的语法糖。

此外,TC39 目前计划做一个 JS 里的私有属性的新特性,你可以看 这里 并且可以提一下你的建议,如果这个特性通过了,我们可以做到如下事情:

代码语言:javascript
复制
class Foo {
    #a; #b; // 井号标明 a 和 b 是私有属性
    #sum = function() {
        return #a + #b;
    }
}

// 我感觉上面这种写法让我想起了 php 里访问变量的方式 $variable 
// 我还不确定这是不是好东西
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaScript 里的对象,属性和类
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档