JavaScript 程序员能够从C++中学到什么

作者:Bret Cameron

翻译:猖獗的手艺宅

原文:
https://medium.com/@bretcamer…

本文首发微信民众号:前端前锋
迎接关注,天天都给你推送新颖的前端手艺文章

如何经由过程相识范例、内存以及低级言语使你成为更好的顺序员

《JavaScript 程序员能够从C++中学到什么》

时刻的完毕?图片来自 Jens Kreuter,由Bret Cameron修正。

像许多开辟新手一样,JavaScript 是我学的第一门言语。它是一种 Web 前端编程言语 —— 谢谢Node.js —— 它同时也是一种盛行的后端东西。

我也置信,作为一种“更高等”的言语,JavaScript 是初学者的绝佳挑选。你可以在任何 Web 浏览器上运转它,而且由于具有原型继承和动态范例等功用,学习者在编写和实行第一段代码之前战胜的停滞更少。

然则 JavaScript 让初学者更轻易上手的要素也让它难以被控制。它能以看上去不直观的体式格局运转,而且当涉及到更多不透明的功用时,许多顺序员更依赖于试错法,比方隐式范例强迫转换或 this 关键字。 晓得这些功用比明白它们要轻易许多。

《JavaScript 程序员能够从C++中学到什么》

“Any fool can know. The point is to understand.” —— Albert Einstein

因而要成为更高等的 JavaScript 开辟人员,试着更深切地相识幕后发作的事变是有很大协助的。归根结柢,最出色的处所是 V8 JavaScript 引擎:它是运用最普遍的 JavaScript 编译器(Google Chrome、Node.js等的基础之一),它是开源的,因而你可以正确地看到 JavaScript 是如何在 C ++ 中实行的。

然则本文不是 V8 的指南。相反,它是有关像 C++ 如许的低级言语如何协助我们进步对 JavaScript 等高等言语的明白的一篇文章。 C++ 不仅可以协助我们明白底层的编译器代码,而且经由过程研讨 C++ 顺序员必需要做而 JavaScript 顺序员没必要做的事,可以更好地相识在 JavaScript 中提拔效力的处所,以及为何偶然会激发题目。

特别是我们将会研讨 C++ 中的数据范例和内存治理,以及这些学问如何协助我们防备范例毛病,并防备 JavaScript 中的内存走漏。还会研讨内存治理与时刻溢出之间的关联。

JavaScript 中强迫范例

在进入 C++ 之前,先让我们看看 JavaScript 是如何处置惩罚数据范例以及“范例强迫”系统的一些圈套的。

JavaScript 运用范例强迫转化自动将一种数据范例转换为别的一种:字符串转为数字、数字转为字符串、数字或字符串转为布尔值等等。换句话说,假如你没有明白指定所需的范例,JavaScript 将依据一组划定规矩举行猜想。偶然这很管用,它可以协助我们疾速简约地编写代码。但偶然候多是激发杂沓的缘由。

现实上纵然这类行动从根本上来讲是可展望的,但某些自动推想也不那末直观,而且在许多大型项目的代码库中,很轻易看到范例强迫转换致使了不测毛病的发作。比方以下是运用组合字符串和数字的举行运算的一些演示:

"10" - 4
// 6

"10" + 4
// "104"

"20" - "5"
// 15

"20" + "5"
// 205

"20" + + "5"
// 205

"foo" + "bar"
// "foobar"

"foo" + + "bar"
// "fooNaN"

"6" - 3 + 3
// 6

"6" + 3 - 3
// 60

在这些例子中, + 运算符造成了大批的杂沓,它可以强迫把字符串转为数字,也可以作为衔接运算符组合两个或多个字符串。

末了一个例子多是最令人困惑的。在 "6" + 3 — 3 中,假如起首处置惩罚 3 — 3 ,然后再举行字符串衔接,"6" + 0 会返回一个字符串,然则在这里返回的结果居然是一个数字!

虽然范例强迫转换可以协助开辟人员更疾速、简约地编写代码,然则它使初学者思索得更少,从而也就不清楚为何如许的转换系统能够会致使毛病,特别是在更大、更庞杂的代码库中。上面的结果关于经验丰富的 JavaScript 顺序来讲多是完整合理的,但它们并不直观!

考虑到 JavaScript 范例强迫系统的长处和瑕玷,如今让我们看看 C++ 是如何处置惩罚数据范例的。

C++ 中的范例和内存治理

C++ 之类的低级言语没有这类潜伏缺点,由于必需在定义时声明数据范例。虽然 JavaScript 也有三个关键字  varletconst  用于声明新变量,但在C ++中每一个数据范例都有本身的关键字。

比方 C++ 中的 7 种基础数据范例是整型、浮点型、双精度浮点型,字符型,宽字符型,布尔型和无范例。用于定义它们的关键字分别是 intfloatdoubleboolcharwchar_tvoid

下面的代码段包括了每种范例的示例声明,并增加了解释:

#include <iostream>
#include <string>
using namespace std;

int main()
{
 
  // BOOLEANS
  bool isChecked = true;
  
  // INTEGERS
  int age = 24;

  // FLOATS
  // In general, a float has 7 decimal digits of precision, while a double has 15
  float pi7 = 3.1415926;
  double pi15 = 3.141592653589793;
  
  // CHARACTERS
  // Regular characters can contain only values stored in the ISO Latin tables
  // Wide characters, however, can contain unicode values
  char englishGreeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
  wchar_t mandarinGreeting[7] = { 'n', 'ǐ', ' ', 'h', 'ǎ', 'o', '\0' };
  
  // STRINGS
  // In C++, string is not a data type (as it is in JavaScript and many other languages) 
  // It is a class, and so we must write #include <string> at the top of the document
  string greeting = "Hello";
  
  // VOID
  // A common use of void is to define functions which don't return anything
  void printMessage() {
    cout << "Hello, world!";
  };
  
  return 0;
}

与 JavaScript 差别,C++ 为开辟人员供应了大批内存治理的要领。在 C++ 中,每声明一个变量时,我们也会决定要保存若干内存。比方,一般的 char 一般只包括8位(1字节),这就将其用途限制为 ISO Latin 表的255个字符。相比之下,wchar_t 包括16或32位,虽然占用了更多内存,但许可我们可以接见更多品种的 Unicode 字符。

在整型中可以找到最多的品种,个中基础的 int 关键字可以与关键字 shortlonglong long 以及 “signedness” 关键字 signedunsigned 连系运用。 。

基础的 int 范例的取值局限是系统系统发起的天然局限。在 64 位操作系统上一般是 32 位。这就意味着如许的一个有标记的变量的取值局限在 -2,147,483,648 和 2,147,483,647 之间,而无标记变量的取值局限是 0 到 4,294,967,295 之间。

假如你可以确认本身的变量取值局限比较小,可以运用 short int 来节约内存。或许假如你正在处置惩罚异常大的整数,你可以运用 unsigned long long int 来处置惩罚 64 位的数字,其取值上限为 2^64 - 1

为何内存至关重要:一个关于时刻溢出的用例

运用 64 位变量(比方 long long int)可以让盘算机示意将来约 2.92 亿年的日期。这似乎是没什么必要的,但它现实上处理了一个异常现实的题目。

根据通例,盘算中的大多数日期都是用 Unix 时刻来示意的,该时刻的肇端日期是 1970 年 1 月 1 日半夜,准确到秒。假如将 Unix 时刻存储在有标记的 32 位变量中,可纪录的最大值为 2,147,483,647。虽然看起来很大,但考虑到它每一秒都在增进,现实上 20 亿并不能让我们用得太久。

现实上 32 位系统上纪录的日期将在 2038 年 1 月 19 日 UTC(恰好是 03:14:07 )到达最大值。当这类状况发作时,日期将会变成负的 2,147,483,647,这个时刻是 1901 年 12 月 13 日。它被称为 2038 题目,而且它致使了许多题目的涌现,比方“一切盘算机将在 2038 年垮台” —— 由英国小报 Metro 供应。

这个令人震惊的题目能够并不是现实,然则当 2038 年到来时,这个题目能够会致使 32 位操作系统以至是全部旧版本的编程言语涌现题目。我第一次碰到这个题目时正在用 PHP,在 5.2 版本之前没有内置的体式格局可以纪录凌驾 2038 年的日期。(JavaScript 运用了 64 位系统来处置惩罚日期,所以我们 JavaScript 顺序员不必忧郁这个)

2038 题目证明了我们本身治理内存的潜伏用途。在需要较小取值局限的处所可以节约内存。在需要更大取值局限的场所,可以确保我们的系统可以具有充足的内存。

JavaScript 中的内存治理

“JavaScript 在建立对象时自动分派内存,并在不再运用时开释它(
渣滓接纳)。这类自动化处置惩罚能够会引起杂沓:它能够会给顺序员带来毛病的暗示,即他们不需要忧郁内存治理题目。“ —— MDN

JavaScript被称为“自动渣滓接纳”言语。它用 mark-and-sweep 算法来搜检哪些内存是运动的,哪些是“渣滓”。然后收集器可以开释“渣滓”,将未运用的内存还给操作系统。

自动渣滓接纳是高等言语的一个特性,它有助于开释内存——不需要经由过程顺序员的明白指导就可以通知它不再需要。有关 JavaScript 中渣滓接纳机制的信息,请检察这篇文章MDN’s page on Memory Management

渣滓接纳是一个壮大的自动内存治理系统,但它并不是十拿九稳。特别是所谓的“不需要的援用”能够会致使内存走漏,这意味着顺序占用的内存比现实需要的多,从而降低了内存的效力。然则假如我们可以意想到内存走漏的风险,就可以采用步伐将其删除。

不测的运用全局变量是致使内存走漏的一个罕见缘由。当我们在 JavaScript 代码中没有用关键字 varletconst 定义变量时,那末它会自动被认为是一个全局变量。除非已定义了 foo,不然表达式 foo =“bar” 相当于 window.foo = "bar"

像 ESLint 如许的 linting 东西可以协助你找出如许的毛病,然则 JavaScript 内置的严厉情势也可以将它们标记为毛病,从而防备不测运用全局变量。要激活严厉情势,只需在剧本或函数的开首到场"use strict";。有关从代码中去除内存走漏风险的更多要领,请参阅这篇文章

JavaScript 中的范例

另有一些要领可以指定变量范例并在 JavaScript 中建立本身的范例,这类体式格局让人想到低级言语。最盛行和最全面的处理计划是 TypeScript,它是 JavaScript 的语法超集,为言语增加了静态范例选项。

在 TypeScript 上有许多不错的资本,足以申明它是能确保你代码可扩大性而且没有毛病的好要领,它可以协助我们防备本文在前面关于“强迫范例”那一节中看到的那种不直观的结果。 TypeScript 的文件扩大名是 .ts,另有一个等效的 .jsx.tsx。对初学者来讲最好的一篇文章是5分钟入门 TypeScript

值得注意的是,另有一些针对差别 JavaScript 手艺的范例解释处理计划。比方你可以将官方的 PropTypes node module 增加到你的 React 项目中。这使你可以纪录传递给组件的 props 的预期数据范例以及设置默认值。特别是当与像 ESLint 如许的 linter 连系运用时,PropTypes 是基于 React 的设置的壮大补充。

结论

总的来讲,我愿望本文有助于说明 C++ 如许的低级言语和 JavaScript 这类高等言语之间的一些差别。

我也愿望它可以为你供应一种东西,以 TypeScript 或 PropTypes 的情势将 C++ 中的一些优点带入 JavaScript,并可以影响和革新 JavaScript 中的内存治理。

假如你对 C++ 有深切的明白,而且想要相识更多关于 JavaScript 的完成体式格局,最好的行止多是官方 V8 网站或许官方 Git repo。Happy coding!

本文首发微信民众号:前端前锋

迎接扫描二维码关注民众号,天天都给你推送新颖的前端手艺文章

《JavaScript 程序员能够从C++中学到什么》

迎接继承阅读本专栏别的高赞文章:

    原文作者:疯狂的技术宅
    原文地址: https://segmentfault.com/a/1190000018977861
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞