مقارنة بين C # وجافا سكريبت. الأساسيات

C # وجافا سكريبت


بدأت مسيرتي الأكثر أو الأقل خطورة في البرمجة بكتابة برامج في C # ، وأحيانًا حاولت الكتابة في جافا سكريبت ، ثم وقعت الآن في ذهول في المواقف التي أدخلت فيها اسم المتغير بشكل غير صحيح واكتشفته لاحقًا سنوات عديدة ساعة من التصحيح ، لأنني لم أكن بالقرب من المترجم ، الأمر الذي سيساعدني في الخروج في الأوقات الصعبة. بعد مرور بعض الوقت ، بالإضافة إلى C # ، بدأت في كتابة الكثير من تعليمات JavaScript البرمجية ، ويمكنني الآن القيام بذلك دون أي صعوبات ، لم أعد أشعر بالارتباك بسبب نوع الكتابة الضمني والكتابة الديناميكية.


في هذه المقالة ، أرغب في تنظيم معرفتي الأساسية بهذه اللغات والنظر في أوجه التشابه والاختلاف بينها. يمكن أن تكون هذه المقالة بمثابة دليل لمطوري C # الذين يرغبون في تعلم JavaScript والعكس صحيح. أريد أيضًا أن أشير إلى أن هذه المقالة تصف إمكانات JS من جانب العميل ، حيث لا أملك خبرة تطوير على Node.js. لذا ، إذا كنت لا تزال لم تفقد الاهتمام ، فلنبدأ.


وحدات مساحة الاسم وشبيبة


في كل برنامج ، من أجل تجنب التضارب في أسماء المتغيرات أو الوظائف أو الفئات أو الكائنات الأخرى ، نقوم بدمجها في بعض المناطق. وبالتالي ، إذا احتوت منطقتان مختلفتان على عناصر تحمل نفس الاسم ، فلن يحدث تعارض.


في C # ، يتم استخدام مساحات الأسماء لتقسيم البرنامج إلى أجزاء. namespace استخدام 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' ، بعد الكلمة الأساسية ، نحتاج إلى تحديد المسار إلى المجلد الذي توجد فيه جميع المكونات الموصوفة.


طرق التصريح عن المتغيرات


الكلمة الأساسية var


كما تعلمون ، C # هي لغة برمجة مكتوبة بقوة ، لذلك ، عند تعريف متغير ، يجب أن يعرف المترجم نوعه ، لذلك يشار إليه عادة قبل اسمه.


 double pi = 3.14; User user = new User(); int[] a = new[] { 0, 1, 2 }; 

ولكن يمكننا أيضًا أن نخبر المترجم أنه يجب أن يستنتج النوع بمفرده من التعبير بعد علامة التعيين. أصبح هذا ممكنًا من خلال تقديم الكلمة الأساسية var في C # 3.0.


 // 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 // ... } 

على الرغم من أن لديك القدرة على إعلان المتغيرات باستخدام var في JavaScript ، لا يُنصح بذلك الآن ، بعد إصدار معيار ES6 ، تمت إضافة الكلمة الرئيسية let ، والتي تسمح لك أيضًا بالإعلان عن المتغيرات ، ولكن ميزتها هي أن نطاقها سيكون الكتلة التي يتم فيها الإعلان عنها ، وليس الوظيفة بأكملها.


الثوابت


يستخدم كل من C # و JavaScript الكلمة الأساسية const حقل ثابت. صحيح ، تجدر الإشارة إلى أن مفهوم الثابت في هذه الحالة يختلف عن هذه اللغات.


في C # ، الثابت هو تعبير يمكن تقييمه بالكامل في مرحلة التجميع ، أي يمكن أن تكون الثوابت أرقامًا أو منطقية أو سلاسل أو مراجع فارغة.


 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 . كما اتضح ، فإن void في JavaScript هو عامل أحادي يحسب قيمة المعامل ، ثم يتجاهله ويعود 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 للأغراض التالية:


  • لإنشاء كائنات ؛
  • لإخفاء العضو الموروث من الطبقة الأساسية ؛
  • لتحديد الأنواع التي يمكن استخدامها كوسيطة لمعلمة نوع في فئة عامة.

الحالة الأولى مشابهة لاستخدام new في JavaScript.


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

أنواع البيانات الأساسية


تحتوي كل لغة على أنواع بيانات - بدائية تستند إليها أنواع البيانات الأخرى ، فلنلق نظرة على أنواع البيانات المقدمة لنا في C # وجافا سكريبت.


أنواع بدائية C #:


  • عدد صحيح sbyte : sbyte ، short ، int ، long
  • عدد صحيح ushort : byte ، ushort ، uint ، ulong
  • أحرف Unicode: char
  • مجموعة أحرف Unicode: char
  • أرقام النقطة العائمة: float ، double
  • العشري decimal : decimal
  • القيمة المنطقية: منطقية

الفئة الأساسية هي Object .


بالنسبة لشبيبة:


أنواع البيانات البدائية:


  • رقم number
  • سلسلة string
  • منطقية boolean
  • null خاصة
  • معنى خاص undefined
  • symbol

النوع الأساسي هو Object .


بعد دراسة أساسيات كلتا اللغتين ، يمكن أن نصل إلى الاستنتاجات التالية:


  • بدلاً من مجموعة كبيرة بما فيه الكفاية من أنواع الأرقام ، تحتوي JavaScript على number نوع واحد ؛
  • لا تحتوي JavaScript على نوع char ؛ بدلاً من ذلك ، استخدم نوع string ؛
  • في اللغتين ، النوع الأساسي هو Object ؛
  • السمة المميزة لـ JS هي أنه يتم فصل القيم null وغير undefined إلى أنواع منفصلة ، بينما في C # null هي كلمة رئيسية تشير إلى عدم وجود قيمة.
  • يحتوي JS على نوع symbol ، والذي يتم استخدامه بشكل أساسي داخل معيار JavaScript نفسه ، من أجل أن تكون قادرًا على إضافة وظائف جديدة دون تعارض مع قاعدة التعليمات البرمجية الحالية.

كقاعدة ، هناك الآن المزيد والمزيد من التطبيقات التي من الضروري فيها معالجة البيانات على العميل ، الأمر الذي يتطلب دقة أكبر في الحسابات. تفتقر جافا سكريبت حاليًا إلى القدرة المدمجة على العمل مع أعداد كبيرة ، ولكن في المستقبل القريب من المقرر إضافة نوع جديد من BigInt . لحل مشاكل مماثلة في C # هناك فئة System.Numerics.BigInteger .


فحص نوع الكائن


تدقيق النوع هو عملية نموذجية إلى حد ما لمعظم لغات البرمجة. بناءً على النوع ، يمكننا تنفيذ إجراءات مختلفة. على سبيل المثال ، ضع في اعتبارك مثالًا على الحياة: تسمع جرس الباب ، إذا جاء إليك جار مخمور (كائن من نوع الجار في حالة سكر ) لاقتراض المال ، فمن غير المحتمل أن تفتح له الباب ، ولكن إذا كان أفضل صديق لك خلف الباب (كائن من أفضل النوع صديق ) ، فأنت لا تتردد في السماح له بدخول الشقة. يوفر C # وجافا سكريبت أيضًا تسهيلات للتحقق من نوع الكائنات.


عامل 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() سيعيد سلسلة 'Animal' ، ونوع typeof [1,2,3] - سلسلة Array ، مهما كانت متناقضة ، فإن النتيجة في كلتا الحالتين هي 'object' . أيضًا ، نظرًا لحقيقة أن الفئات في JS هي التفاف فوق الدالات ، فإن تعبير typeof class C {} سيُرجع 'function' بدلاً من 'class' . حقيقة أخرى مثيرة للاهتمام هي أن نوع التعبير typeof null سيُرجع 'object' . في JavaScript ، هذا العامل له عيب كبير: جميع الكائنات غير البدائية تبدو متشابهة ، وكلها لها نفس نوع object .


من الجدير بالذكر أنه في JavaScript typeof يمكن تطبيقه على أي شيء: الكائنات والوظائف والفئات وما إلى ذلك ... في C # ، ينطبق هذا العامل فقط على الأنواع.


هو ومثال


بالإضافة إلى الحصول على معلومات حول نوع ما ، قد يكون من المفيد في بعض الأحيان التحقق من أن الكائن ينتمي إلى نوع معين.


في C # يوجد عامل تشغيل لهذه الأغراض.


 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 ، من أجل معرفة نوع الكائن الذي تنتمي إليه ، تحتاج إلى استخدام عامل التشغيل - 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 ، وفي حالة جافا سكريبت ، أيضًا لغير 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 موجودة في كل مكان ، تمت إضافة عامل التشغيل Null Propagation في 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(""); 

تحديد القيم الافتراضية


هناك عملية شائعة أخرى هي تعيين القيم الافتراضية ، من الإصدار 2.0 في C # Null Coalescing Operator ظهر - ?? .


السطرين التاليين من رمز C # متساويان:


 var name = user != null && user.name != null ? user.name : ""; var name = user?.name ?? ""; 

في JavaScript ، يتم إجراء عملية مماثلة على النحو التالي.


 var name = user && user.name || ""; 

ومع ذلك ، يمكننا استخدام عوامل التشغيل && و || فقط إذا كانت 0 و false والسلسلة الفارغة ليست قيمًا صالحة.


في المستقبل المنظور ، مشغلي ?. ، ?? يجب أن تظهر في جافا سكريبت (لقد اجتازوا الآن المرحلة 0) ، يمكن العثور على مزيد من التفاصيل حول هذه العوامل في جافا سكريبت في المقالة .


هذه الكلمة


يحتوي كل من C # و JavaScript على this . عادة في C # فهم الغرض من this هو مباشر ، ولكن في 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 ، يسمى هذا سياق المكالمة وسيتم تحديده عندما يتم استدعاء الوظيفة. إذا قمت بتشغيل نفس الوظيفة في سياق كائنات مختلفة ، فستتلقى وظيفة مختلفة:


 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 الوظائف بشكل صريح: 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; } //   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; 

ظهر دعم التدمير أو (مهمة التدمير) في JavaScript في معيار EcmaScript السادس. بمساعدتها. يمكنك تعيين مصفوفة أو كائن لعدة متغيرات في وقت واحد ، وتقسيمه إلى أجزاء.


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

تجدر الإشارة إلى أن إعادة الهيكلة في JavaScript لها ميزات أكثر من C #:


  • تغيير ترتيب المتغيرات ؛
  • لا حاجة إلى التصريح الصريح بفواصل التفكيك ؛
  • صفيف دعم التدمير ؛
  • تحديد القيم الافتراضية ؛
  • تعيين خصائص كائن لمتغير باسم مختلف ؛
  • دعم التدمير المتداخل.

الخلاصة


في هذه المقالة ، ناقشنا فقط المفاهيم الأساسية للغات C # وجافا سكريبت. لكن العديد من الجوانب ظلت غير متأثرة:


  • مجموعات
  • الوظائف
  • الطبقات
  • تعدد المواضيع

كل واحد من هذه المواضيع واسع النطاق وسيتم الكشف عنه لاحقًا في مقالة منفصلة.

Source: https://habr.com/ru/post/ar414593/


All Articles