
我或多或少认真的编程道路始于用C#编写程序,有时我尝试使用JavaScript编写,然后在我错误地输入变量名并在后来发现它的情况下陷入混乱。 很多很多年 一个小时的调试时间,因为我不在编译器附近,这将在困难时期帮助我。 一段时间之后,除了C#外,我还开始编写许多JavaScript代码,现在我可以毫无困难地做到这一点,我不再对隐式类型转换和动态类型感到困惑。
在本文中,我想将我对这些语言的基本知识进行系统化,并考虑它们的异同。 本文可以作为希望学习JavaScript的C#开发人员的指南,反之亦然。 我还想指出,由于我没有Node.js的开发经验,因此本文介绍了客户端JS的功能。 因此,如果您仍然没有失去兴趣,那就开始吧。
命名空间和js模块
在每个程序中,为了避免变量,函数,类或其他对象的名称冲突,我们在某些区域将它们组合在一起。 因此,如果两个不同区域包含具有相同名称的元素,则不会发生冲突。
在C#中,名称空间用于将程序拆分为多个部分。 关键字namespace
用于声明它们。 例如,如果我们要创建一组用户界面组件,将它们全部放在一个名称空间(例如Components
是合乎逻辑的。 通常,命名空间具有以下命名: [AssemblyName].[DirectoryName].[DirectoryName].[...]
。 在每个文件中,用户界面组件的类必须放置在名称空间内:
ComboBox.cs
文件的内容:
namespace AssemblyName.Components { public class ComboBox {
为了开始使用组件,您需要using AssemblyName.Components
如下从名称空间导入它们。 使用这种单行连接方法,我们将所有对象导入到当前文件中。
JS将ES模块用于相同的目的。 使用它们时,我们在某种程度上通过编写其他代码来模拟名称空间的行为。 考虑带有组件库的相同示例。 假设我们有一个Components
文件夹,其中包含用户界面ComboBox.js
, Button.js
, Button.js
等的组件。 为了获得与Components
文件夹中的名称空间相比类似的行为,您需要创建一个index.js
文件,其中将包含以下代码:
export { default as Dialog } from './ComboBox'; export { default as Button } from './Button'; export { default as Checkbox } from './Checkbox';
为了使用这些组件,您需要将它们导入到当前文件中。 这可以通过以下方式完成: import * as Components from './../Components'
,在from关键字之后,我们需要指定所有描述的组件所在的文件夹的路径。
声明变量的方法
var
关键字
如您所知,C#是一种强类型化的编程语言,因此,在声明变量时,编译器必须知道其类型,为此,通常在其名称之前进行指示。
double pi = 3.14; User user = new User(); int[] a = new[] { 0, 1, 2 };
但是我们也可以告诉编译器,它必须根据分配符号后的表达式自行推断类型。 通过在C#3.0中引入var
关键字,可以做到这一点。
使用var
我们可以创建匿名类型的对象:
在JavaScript中,还可以使用var
关键字声明变量,但是,与C#不同,如果在函数外部声明变量,则这些变量的范围将是整个函数或window
对象。
var a = 5 // - window function go() { var a = 6 // - go // ... }
尽管您可以在JavaScript中使用var
声明变量,但现在不建议这样做,在ES6标准发布后,添加了let
关键字,它还允许您声明变量,但其优点是它们的作用域为在其中声明它们的块,而不是整个函数。
常数
C#和JavaScript都使用const
关键字来声明一个常量字段。 的确,值得注意的是,在这种情况下,常量的概念对于这些语言而言是不同的。
在C#中,常数是一个可以在编译阶段完全评估的表达式,即 常量可以是数字,布尔值,字符串或null引用。
const int c1 = 5; const int c2 = c1 + 100; const string c3 = ""; const bool c4 = true; const User human = null; const User human = new User(firstName);
在JavaScript中,常量的值也不能更改,但是,与C#中一样,该值没有限制,您可以为其分配任何值/对象/数组。 但是,如果将一个对象分配给一个常量,则可以保护常量本身不受更改,但不受其内部属性的更改:
const c1 = 5; const c2 = c1 + 100; const c3 = ""; const c4 = true; const user = { name: "" }; user.name = "";
void
关键字
在撰写本文时,我在控制台中尝试了一些函数,并且出于习惯开始将其描述为C# void SomeFunction...
,当我发现JavaScript关键字为void
时,这让我感到非常惊讶。 事实证明,JavaScript中的void
是一元运算符,用于计算操作数的值,然后将其丢弃并返回undefined
。
alert("!"); // "!" alert(void "!"); // undefined
因此,我们可以说void
的使用清楚地表明没有返回值;有关其使用示例的更多详细信息,请参见下一篇文章 。
在C#中, void
不是运算符,但实质上具有相似的含义。 在这里,它指示不存在函数返回值:
public void SampleMethod() {
但是,如您在上面的示例中所看到的,通常在通常指示返回值的类型的位置使用void
,这绝非偶然,因为在C#中void
也是一种类型。
var t = typeof(void); t.Name
作为指针,只能在不安全的上下文中使用void
作为类型。
unsafe { void* identifier;
new
关键字
在JavaScript中, new
关键字是一个运算符,通常用于许多类似C的语言来创建对象。
function Animal() { //... } const animal = new Animal();
在C#中, new
可用于以下目的:
- 创建对象;
- 隐藏基类的继承成员;
- 将可用作参数的类型限制为泛型类中的类型参数。
第一种情况类似于在JavaScript中使用new
。
class Animal {
基本数据类型
每种语言都有数据类型-基于构建其他数据类型的基元,让我们看一下C#和JavaScript提供给我们的数据类型。
原始C#类型:
sbyte
整数: sbyte
, short
, int
, long
- 无符号整数:
byte
, ushort
, uint
, ulong
- Unicode字符:
char
- Unicode字符集:
char
- 浮点数:
float
, double
- 十进制小数:
decimal
- 布尔值:
bool
基类是Object
。
对于JS:
基本数据类型:
- 号码
number
string
- 布尔布尔
- 特殊
null
- 特殊含义
undefined
symbol
基本类型是Object
。
在研究了两种语言的原始语之后,我们可以得出以下结论:
- JavaScript没有一个足够大的数字类型集,而是具有一个单一的
number
; - JavaScript没有
char
类型;而是使用string
类型; - 在两种语言中,基本类型都是
Object
; - JS的一个独特功能是将
null
和undefined
分为不同的类型,而在C#中, null
是指示缺少值的关键字。 - JS具有
symbol
类型,该symbol
类型主要在JavaScript标准本身内部使用,以便能够添加新功能而不与现有代码库冲突。
通常,现在有越来越多的应用程序需要在客户端上处理数据,这需要更高的计算精度。 JavaScript当前缺乏内置的处理大量数据的能力,但是在不久的将来,它计划添加一种新型的BigInt
。 为了解决C#中的类似问题,有一个类System.Numerics.BigInteger
。
对象类型检查
对于大多数编程语言,类型检查是相当典型的操作。 根据类型,我们可以执行各种操作。 例如,考虑一个生活的例子:您听到门铃,如果一个醉酒的邻居来找你借钱( 邻居为醉汉 ),那么您不太可能为他打开门,但是如果您最好的朋友在门后(那个对象为最好的对象) 朋友 ),那么您会毫不犹豫地让他进入公寓。 C#和JavaScript还提供了用于检查对象类型的工具。
typeof
运算符
对于类型信息,C#和JavaScript都具有typeof
运算符。 让我们看看它在两种语言中如何工作:
在C#中,将typeof
运算符应用于类型,并返回Type
类的对象,该对象包含所有类型信息。
namespace Zoo { public class Animal {} } Type t = typeof(Animal); t.Name
在JS中, typeof
返回一个指示操作数类型的字符串。
typeof 30 // 'number' typeof Symbol() // 'symbol' typeof undefined // 'undefined' // typeof new Animal() // object typeof null // 'object' typeof [1,2,3] // 'object' // typeof function() {} // 'function'; typeof class C {} // 'function';
在上面的示例中,您可以注意到此运算符的某些功能。 如果typeof new Animal()
的表达式typeof new Animal()
将返回字符串'Animal'
和typeof [1,2,3]
-字符串Array
,这似乎是合乎逻辑的,但是矛盾的是,在两种情况下,结果都是'object'
。 同样,由于JS中的类是函数的包装器,因此typeof class C {}
的表达式typeof class C {}
将返回'function'
而不是'class'
。 另一个有趣的事实是typeof null
表达式将返回'object'
。 在JavaScript中,此运算符有一个很大的缺点:所有非原始对象看起来都一样,它们都具有相同的object
类型。
值得注意的是,在JavaScript中typeof
可以应用于任何对象:对象,函数,类等。在C#中,此运算符仅适用于类型。
除了获取有关类型的信息之外,有时验证对象是否属于特定类型也很有用。
在C#中,出于这些目的有一个is
运算符。
class Person { }
在JavaScript中,为了找出对象属于什么类型,您需要使用operator- instanceof
。
function Person() {} function Programmer() {} // Programmer Person Programmer.prototype = Object.create(Person.prototype); var person = new Person(); var programmer = new Programmer(); console.log(person instanceof Person); // true console.log(person instanceof Programmer); // false console.log(programmer instanceof Person); // true console.log(programmer instanceof Programmer); // true
布尔和空值检查
为了不获取Null reference exception
,几乎在所有地方,在使用变量之前,都要检查它是否为null
,对于JavaScript,还要检查其undefined
。
在C#中,我们不断看到类似的代码:
if(user != null && String.IsNullOrEmpty(user.name)) { user.SetName(""); }
在JavaScript中,此结构可以写得短一些。 这是由于这样的事实,与C#不同,在JavaScript中,铸造时除false
以外的许多值也被视为false
:
null
undefined
- “”(空行)
0
NaN
(不是数字)
因此,上面的C#代码可以编写如下:
if (user && !user.name) { user.setName(""); }
或
user && !user.name && user.setName("");
由于null
检查无处不在,因此在C#6.0中添加了空传播运算符 .?
。
C#代码:
if (user != null && user.parent != null && user.parent.parent != null) { user.parent.parent.SetName(""); }
在其帮助下,该代码节可以按如下方式重写:
user?.parent?.parent?.SetName("");
在JavaScript中,通常按以下步骤进行:
user && user.parent && user.parent.parent && user.parent.parent.setName("");
设定默认值
另一个常见的操作是设置默认值,从C#中出现的 2.0版Null合并运算符- ??
。
以下两行C#代码是等效的:
var name = user != null && user.name != null ? user.name : ""; var name = user?.name ?? "";
在JavaScript中,通常按以下步骤进行类似的操作。
var name = user && user.name || "";
但是,我们可以使用运算符&&
和||
仅当0
, false
和空字符串不是有效值时。
在可预见的将来,运营商?.
, ??
应该出现在JavaScript中(它们现在已经通过Stage0阶段),有关这些JavaScript中的运算符的更多详细信息,请参见本文 。
这个关键词
C#和JavaScript都具有this
。 通常,在C#中,了解此操作的目的很简单,但是在JavaScript中,这是最复杂的语言概念之一。 此外,我们将考虑this
示例应用于示例。
在C#中, this
指向该类的当前实例。
class User { public string Name { get; set; } public void PrintEmployee() { Console.WriteLine(this.name); } } var employee = new Employee(); E1.PrintEmployee();
在此示例中,在Console.WriteLine(this.name)
表达式中, this
指向employee
变量。
由于this
是该类的当前实例,因此不能在未绑定到特定类型的方法中使用它,例如,在静态方法中。
在JavaScript中, this
称为调用上下文,将在调用函数时确定。 如果在不同对象的上下文中运行相同的函数,它将收到不同的this
:
var user = { firstName: "" }; var admin = { firstName: "" }; function func() { alert( this.firstName ); } user.f = func; admin.g = func; // this : user.f();
另外,在JavaScript中,可以使用以下函数显式指定this
的值: call
, bind
和apply
。 例如,上面的示例可以重写如下:
var user = { firstName: "" }; var admin = { firstName: "" }; function func() { alert( this.firstName ); } // this : func.call(user); // func.call(admin); // func.bind(user)();// func.bind(admin)();//
改制
通常需要将对象的多个字段分配给局部变量。 例如,您多久观察一次类似的代码?
void Method(User user) { var firstName = user.FirstName; var lastName = user.LastName;
为此,可以使用解构。 两种语言都不同程度地支持此功能。
C#7.0引入了一种称为解构函数的新功能来支持解构。 为了声明一个解构函数,我们需要定义一个称为Deconstruct
的方法,该方法的所有参数都必须使用out
修饰符声明:
class Person { public string FirstName { get; set; } public string LastName { get; set; }
第六个EcmaScript标准中出现了对JavaScript进行解构或(解构分配)的支持。 在她的帮助下 您可以一次将数组或对象分配给多个变量,将其分成多个部分。
let [firstName, lastName] = ["", ""]; let [firstName, _ ] = ["", ""]; let { firstName, lastName } = { firstName: "", lastName: "" }; let { firstName } = { firstName: "", lastName: "" };
值得注意的是,JavaScript中的重组比C#中具有更多功能:
- 更改变量的顺序;
- 无需显式声明解构函数;
- 阵列解构支持;
- 设置默认值;
- 将对象的属性分配给具有不同名称的变量;
- 支持嵌套解构。
结论
在本文中,我们仅讨论了C#和JavaScript语言的最基本概念。 但是许多方面仍然不受影响:
这些主题中的每个主题都相当广泛,稍后将在另一篇文章中进行介绍。