ما الجديد في ES6؟ منظور تحويل CoffeeScript

نشرت: 2022-03-11

لقد كنت من محبي CoffeeScript لأكثر من عامين حتى الآن. أجد أنني أكثر إنتاجية في كتابة CoffeeScript ، وأرتكب أخطاء أقل سخافة ، ومع دعم خرائط المصدر ، فإن تصحيح أخطاء كود CoffeeScript أمر سهل تمامًا.

ما الجديد في ES6؟ منظور تحويل CoffeeScript

لقد لعبت مؤخرًا مع ES6 باستخدام Babel ، وأنا معجب بشكل عام. في هذه المقالة ، سوف أقوم بتجميع النتائج التي توصلت إليها حول ES6 من منظور تحويل CoffeeScript ، وإلقاء نظرة على الأشياء التي أحبها ومعرفة الأشياء التي ما زلت أفتقدها.

المسافة البادئة ذات الدلالة النحوية: نعمة أم نقمة؟

لطالما كان لدي القليل من علاقة الحب / الكراهية مع المسافة البادئة ذات الأهمية النحوية لـ CoffeeScript. عندما يسير كل شيء على ما يرام ، تحصل على عبارة "Look Ma، no hands!" المفاخرة باستخدام مثل هذا النحو في أضيق الحدود. ولكن عندما تسوء الأمور وتتسبب المسافة البادئة غير الصحيحة في أخطاء حقيقية (صدقني ، يحدث ذلك) ، لا يبدو أن الفوائد جديرة بالاهتمام.

 if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'

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

عندما بدأت في كتابة ES6 لم أكن متأكدًا تمامًا من رد فعلي. اتضح أنه من الجيد حقًا البدء في استخدام الأقواس المجعدة مرة أخرى. يساعد استخدام محرر حديث أيضًا ، لأنه بشكل عام ما عليك سوى فتح الدعامة ومحررك لطيف بما يكفي لإغلاقه من أجلك. لقد شعرت قليلاً بالعودة إلى عالم هادئ وواضح بعد سحر الشعوذة لـ CoffeeScript.

 if (iWriteES6) { if (iWriteNestedStatements) { let badThings = 'are less likely to happen' } }

بالطبع أنا أصر على رمي الفاصلة المنقوطة. إذا لم نكن بحاجة إليهم ، أقول طردهم. أجدهم قبيحين وهي كتابة إضافية.

دعم الفصل

يعد دعم الفصل في ES6 رائعًا ، وإذا كنت تنتقل من CoffeeScript ، فستشعر وكأنك في المنزل. دعنا نقارن الصيغة بين الاثنين بمثال بسيط:

فئة ES6

 class Animal { constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs } toString() { return `I am an animal with ${this.numberOfLegs} legs` } } class Monkey extends Animal { constructor(bananas) { super(2) this.bananas = bananas } toString() { let superString = super.toString() .replace(/an animal/, 'a monkey') return `${superString} and ${this.bananas} bananas` } }

فئة كوفي سكريبت

 class Animal constructor: (@numberOfLegs) -> toString: -> "I am an animal with #{@numberOfLegs} legs" class Monkey extends Animal constructor: (@numberOfBananas) -> super(2) toString: -> superString = super.toString() .replace(/an animal/, 'a monkey') "#{superString} and #{@numberOfLegs} bananas"

الإنطباعات الأولى

أول شيء قد تلاحظه هو أن ES6 لا يزال مطولًا أكثر من CoffeeScript. أحد الاختصارات المفيدة جدًا في CoffeeScript هو دعم التخصيص التلقائي لمتغيرات المثيل في المُنشئ:

 constructor: (@numberOfLegs) ->

هذا يعادل ما يلي في ES6:

 constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }

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

كيف سوبر!

هناك مسكتك صغير بالطريقة super في ES6. يتعامل CoffeeScript مع طريقة Ruby super ("الرجاء إرسال رسالة إلى طريقة superclass التي تحمل نفس الاسم") ، ولكن المرة الوحيدة التي يمكنك فيها استخدام super "naked" في ES6 هي في المُنشئ. يتبع ES6 نهجًا شبيهًا بجافا أكثر تقليدية حيث يشير super إلى مثيل الطبقة الفائقة.

سأعود

في برنامج CoffeeScript يمكننا استخدام العوائد الضمنية لبناء كود جميل مثل ما يلي:

 foo = -> if Math.random() > 0.5 if Math.random() > 0.5 if Math.random() > 0.5 "foo"

هنا لديك فرصة ضئيلة جدًا لاستعادة "foo" عند استدعاء foo() . لا يحتوي ES6 على أي من هذا ويجبرنا على العودة باستخدام الكلمة الرئيسية return :

 const foo = () => { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { return "foo" } } } }

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

 array.map( x => x * 10 )

يعد هذا مفيدًا نوعًا ما ولكنه قد يكون مربكًا بعض الشيء لأنك تحتاج إلى return إذا قمت بإضافة الأقواس المتعرجة:

 array.map( x => { return x * 10 })

ومع ذلك ، لا يزال من المنطقي. السبب في ذلك هو أنك إذا أضفت الأقواس المتعرجة ، فذلك لأنك تريد استخدام خطوط متعددة ، وإذا كان لديك عدة سطور ، فمن المنطقي أن تكون واضحًا ما ستعود إليه.

مكافآت بناء الجملة ES6 Class

لقد رأينا أن لدى CoffeeScript بعض الحيل النحوية في جعبتها عندما يتعلق الأمر بتعريف الفئات ، ولكن لدى ES6 أيضًا بعض الحيل الخاصة به.

حاصلون وخطابات

يتمتع ES6 بدعم قوي للتغليف عبر أدوات القياس والمُحددات ، كما هو موضح في المثال التالي:

 class BananaStore { constructor() { this._bananas = [] } populate() { // fetch our bananas from the backend here } get bananas() { return this._bananas.filter( banana => banana.isRipe ) } set bananas(bananas) { if (bananas.length > 100) { throw `Wow ${bananas.length} is a lot of bananas!` } this._bananas = bananas } }

بفضل برنامج getter ، إذا قمنا بالوصول إلى bananaStore.bananas ، فسوف يعيد الموز الناضج فقط. هذا أمر رائع حقًا لأنه في CoffeeScript سنحتاج إلى تنفيذه عبر طريقة getter مثل bananaStore.getBananas() . لا يبدو هذا طبيعيًا على الإطلاق عندما نعتاد في JavaScript على الوصول إلى الخصائص مباشرة. كما أنه يجعل التطوير محيرًا لأننا نحتاج إلى التفكير في كل خاصية كيف يجب أن نصل إليها - هل هي .bananas أم getBananas() ؟

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

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

طرق ثابتة

يدعم ES6 الطرق الثابتة ، والتي تتيح لنا القيام بهذه الحيلة الشقية:

 class Foo { static new() { return new this } }

الآن إذا كنت تكره كتابة new Foo() يمكنك الآن كتابة Foo.new() . سوف يتذمر الأصوليون لأن الكلمة new هي كلمة محجوزة ، ولكن بعد اختبار سريع للغاية يبدو أنها تعمل في Node.js ومع المتصفحات الحديثة على ما يرام. لكن ربما لا ترغب في استخدام هذا في الإنتاج!

لسوء الحظ ، نظرًا لعدم وجود دعم للخصائص الثابتة في ES6 ، إذا كنت تريد تحديد ثوابت الفئة والوصول إليها في طريقتك الثابتة ، فسيتعين عليك القيام بشيء مثل هذا ، وهو أمر مخادع بعض الشيء:

 class Foo { static imgPath() { return `${this.ROOT_PATH}/img` } } Foo.ROOT_PATH = '/foo'

يوجد اقتراح ES7 لتنفيذ مثيل تعريفي وخصائص الفئة ، حتى نتمكن من القيام بشيء مثل هذا ، والذي سيكون أمرًا رائعًا:

 class Foo { static ROOT_PATH = '/foo' static imgPath() { return `${this.ROOT_PATH}/img` } }

يمكن القيام بذلك بأناقة في CoffeeScript:

 class Foo @ROOT_PATH: '/foo' @imgPath: -> @ROOT_PATH

سلسلة الاستيفاء

لقد حان الوقت الذي يمكننا فيه أخيرًا إجراء استيفاء السلسلة في جافا سكريبت!

 `I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`

قادم من كوفي سكريبت / روبي ، يبدو هذا التركيب قوياً قليلاً. ربما بسبب خلفية Ruby الخاصة بي حيث يتم استخدام backtick لتنفيذ أوامر النظام ، يبدو الأمر خاطئًا في البداية. كما أن استخدام علامة الدولار يبدو وكأنه نوع من الثمانينيات - ولكن ربما هذا أنا فقط.

أفترض أنه بسبب مخاوف التوافق مع الإصدارات السابقة ، لم يكن من الممكن تنفيذ الاستيفاء لسلسلة نمط CoffeeScript. "What a #{expletive} shame" .

وظائف السهم

تعتبر وظائف السهم من المسلمات في CoffeeScripter. نفذت ES6 إلى حد كبير بناء جملة السهم السميك لـ CoffeeScript. لذلك نحصل على بناء جملة قصير لطيف ، ونحصل على النطاق المعجمي this ، لذلك لا يتعين علينا القفز عبر الأطواق مثل هذه عند استخدام ES5:

 var that = this doSomethingAsync().then( function(res) { that.foo(res) })

المكافئ في ES6 هو:

 doSomethingAsync().then( res => { this.foo(res) })

لاحظ كيف تركت القوس حول رد الاتصال الأحادي ، لأنني أستطيع ذلك!

الكائن الحرفي على المنشطات

تحتوي الكائنات الحرفية في ES6 على بعض التحسينات الجادة في الأداء. يمكنهم فعل كل أنواع الأشياء هذه الأيام!

أسماء الخصائص الديناميكية

لم أكن أدرك حقًا حتى كتابة هذا المقال أنه منذ برنامج CoffeeScript 1.9.1 يمكننا الآن القيام بذلك:

 dynamicProperty = 'foo' obj = {"#{dynamicProperty}": 'bar'}

وهو ألم أقل بكثير من شيء مثل هذا الذي كان ضروريًا من قبل:

 dynamicProperty = 'foo' obj = {} obj[dynamicProperty] = 'bar'

يحتوي ES6 على بناء جملة بديل أعتقد أنه جميل جدًا ، لكنه ليس فوزًا كبيرًا:

 let dynamicProperty = 'foo' let obj = { [dynamicProperty]: 'bar' }

اختصارات غير تقليدية

هذا مأخوذ في الواقع من CoffeeScript ، لكنه كان شيئًا كنت أجهله حتى الآن. إنه مفيد جدًا على أي حال:

 let foo = 'foo' let bar = 'bar' let obj = { foo, bar }

الآن محتويات obj هي { foo: 'foo', bar: 'bar' } . هذا مفيد للغاية ويستحق التذكر.

كل ما يمكن أن يفعله الفصل يمكنني أن أفعله أيضًا!

يمكن أن تفعل الكائنات الحرفية الآن كل ما يمكن للفصل القيام به ، حتى شيء مثل:

 let obj = { _foo: 'foo', get foo() { return this._foo }, set foo(str) { this._foo = str }, isFoo() { return this.foo === 'foo' } }

لست متأكدًا تمامًا من سبب رغبتك في البدء في القيام بذلك ولكن مهلا ، يمكنك الآن! لا يمكنك تحديد الأساليب الثابتة بالطبع ... لأن ذلك لن يكون له أي معنى على الإطلاق.

للحلقات لإرباكك

سيقدم بناء جملة ES6 للحلقة بعض أخطاء ذاكرة العضلات اللطيفة لـ CoffeeScripters ذوي الخبرة ، حيث يترجم for-of ES6 إلى in-in لـ CoffeeSCript ، والعكس بالعكس.

ES6

 for (let i of [1, 2, 3]) { console.log(i) } // 1 // 2 // 3

كوفي سكريبت

 for i of [1, 2, 3] console.log(i) # 0 # 1 # 2

هل يجب أن أتحول إلى ES6؟

أعمل حاليًا على مشروع Node.js كبير إلى حد ما باستخدام 100٪ كوفي سكريبت ، وما زلت سعيدًا جدًا ومنتجًا للغاية معه. أود أن أقول إن الشيء الوحيد الذي أشعر بالغيرة منه حقًا في ES6 هو الحاصلون والمحددون.

كما أن استخدام ES6 في الممارسة العملية لا يزال مؤلمًا بعض الشيء اليوم. إذا كنت قادرًا على استخدام أحدث إصدار من Node.js ، فستحصل على جميع ميزات ES6 تقريبًا ، ولكن بالنسبة للإصدارات الأقدم وفي المتصفح ، لا تزال الأمور أقل وردية. نعم ، يمكنك استخدام Babel ، لكن هذا يعني بالطبع دمج Babel في مجموعتك.

بعد قولي هذا ، يمكنني رؤية ES6 خلال العام أو العامين المقبلين تكتسب الكثير من الأرض ، وآمل أن أرى أشياء أكبر في ES7.

ما يسعدني حقًا مع ES6 هو أن "جافا سكريبت القديم البسيط" هي تقريبًا ودية وقوية خارج الصندوق مثل CoffeeScript. هذا أمر رائع بالنسبة لي بصفتي مستقلاً - في الماضي كنت أكره قليلاً للعمل في مشاريع JavaScript - ولكن مع ES6 يبدو كل شيء أكثر لمعانًا قليلاً.