C#和JavaScript的比较。 基础知识

C#和JavaScript


我或多或少认真的编程道路始于用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.jsButton.jsButton.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关键字,可以做到这一点。


 // i - int var i = 5; // a - int[] var a = new[] { 0, 1, 2 }; 

使用var我们可以创建匿名类型的对象:


 // anon -      var anon = new { Name = "Terry", Age = 34 }; var type = anon.GetType();//"<>f__AnonymousType0`2" 

在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 = ""; //  user = 5; // ,   

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 // System.Void 

作为指针,只能在不安全的上下文中使用void作为类型。


  unsafe { void* identifier; //,    } 

new关键字


在JavaScript中, new关键字是一个运算符,通常用于许多类似C的语言来创建对象。


 function Animal() { //... } const animal = new Animal(); 

在C#中, new可用于以下目的:


  • 创建对象;
  • 隐藏基类的继承成员;
  • 将可用作参数的类型限制为泛型类中的类型参数。

第一种情况类似于在JavaScript中使用new


 class Animal { //... } var animal = new Animal(); 

基本数据类型


每种语言都有数据类型-基于构建其他数据类型的基元,让我们看一下C#和JavaScript提供给我们的数据类型。


原始C#类型:


  • sbyte整数: sbyteshortintlong
  • 无符号整数: byteushortuintulong
  • Unicode字符: char
  • Unicode字符集: char
  • 浮点数: floatdouble
  • 十进制小数: decimal
  • 布尔值: bool

基类是Object


对于JS:


基本数据类型:


  • 号码number
  • string
  • 布尔布尔
  • 特殊null
  • 特殊含义undefined
  • symbol

基本类型是Object


在研究了两种语言的原始语之后,我们可以得出以下结论:


  • JavaScript没有一个足够大的数字类型集,而是具有一个单一的number
  • JavaScript没有char类型;而是使用string类型;
  • 在两种语言中,基本类型都是Object
  • JS的一个独特功能是将nullundefined分为不同的类型,而在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 // 'Animal' t.FullName // 'Zoo.Animall' t.GetMethods //    t.GetFields //     // ... 

在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#中,此运算符仅适用于类型。


instanceof


除了获取有关类型的信息之外,有时验证对象是否属于特定类型也很有用。


在C#中,出于这些目的有一个is运算符。


 class Person { } // Programmer  Person class Programmer : Person { } var person = new Person(); var programmer = new Programmer(); person is Person //true person is Programmer //false programmer is Person //true programmer is Programmer //true 

在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


  1. null
  2. undefined
  3. “”(空行)
  4. 0
  5. 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 || ""; 

但是,我们可以使用运算符&&|| 仅当0false空字符串不是有效值时。


在可预见的将来,运营商?.?? 应该出现在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(); //  admin.g(); //  func();// undefined -    this -   window 

另外,在JavaScript中,可以使用以下函数显式指定this的值: callbindapply 。 例如,上面的示例可以重写如下:


 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; } //   public void Deconstruct(out string firstName, out string lastName) { firstName = this.FirstName; lastName = this.LastName; } } ... Person person = new Person { FirstName = "", LastName = "" }; (string firstName, string lastName) = person; (string firstName, _ ) = person; 

第六个EcmaScript标准中出现了对JavaScript进行解构(解构分配)的支持。 在她的帮助下 您可以一次将数组或对象分配给多个变量,将其分成多个部分。


 let [firstName, lastName] = ["", ""]; let [firstName, _ ] = ["", ""]; let { firstName, lastName } = { firstName: "", lastName: "" }; let { firstName } = { firstName: "", lastName: "" }; 

值得注意的是,JavaScript中的重组比C#中具有更多功能:


  • 更改变量的顺序;
  • 无需显式声明解构函数;
  • 阵列解构支持;
  • 设置默认值;
  • 将对象的属性分配给具有不同名称的变量;
  • 支持嵌套解构。

结论


在本文中,我们仅讨论了C#和JavaScript语言的最基本概念。 但是许多方面仍然不受影响:


  • 馆藏
  • 功能
  • 多线程

这些主题中的每个主题都相当广泛,稍后将在另一篇文章中进行介绍。

Source: https://habr.com/ru/post/zh-CN414593/


All Articles