سؤال

لقد كنت أعمل مع node.js لفترة من الوقت على تطبيق الدردشة (أعرف ، أصلي للغاية ، لكنني اعتقدت أنه سيكون مشروع تعليمي جيد). Underscore.JS يوفر الكثير من مفاهيم البرمجة الوظيفية التي تبدو مثيرة للاهتمام ، لذلك أود أن أفهم كيف سيتم إعداد برنامج وظيفي في JavaScript.

من فهمي للبرمجة الوظيفية (التي قد تكون خاطئة) ، فإن الفكرة بأكملها هي تجنب الآثار الجانبية ، والتي لها في الأساس وظيفة تقوم بتحديث متغير آخر خارج الوظيفة حتى لا يكون هناك شيء مثل

var external;
function foo() {
   external = 'bar';
}
foo();

من شأنه أن يخلق تأثيرًا جانبيًا ، صحيح؟ كقاعدة عامة ، فأنت تريد تجنب إزعاج المتغيرات في النطاق العالمي.

حسنًا ، فكيف يعمل هذا عندما تتعامل مع الأشياء وما لا؟ على سبيل المثال ، في كثير من الأحيان ، سيكون لدي مُنشئ وطريقة init التي تهيئة الكائن ، مثل ذلك:

var Foo = function(initVars) {
   this.init(initVars);
}

Foo.prototype.init = function(initVars) {
   this.bar1 = initVars['bar1'];
   this.bar2 = initVars['bar2'];
   //....
}

var myFoo = new Foo({'bar1': '1', 'bar2': '2'});

لذا فإن طريقة init الخاصة بي تسبب عن قصد آثارًا جانبية ، ولكن ما هي طريقة وظيفية للتعامل مع نفس المواقف؟

أيضًا ، إذا كان بإمكان أي شخص أن يشير إلي إما إلى رمز مصدر Python أو JavaScript لبرنامج يحاول أن يكون وظيفيًا قدر الإمكان ، فسيكون ذلك موضع تقدير كبير أيضًا. أشعر أنني على وشك "الحصول عليها" ، لكنني لست هناك تمامًا. أنا مهتم بشكل أساسي بكيفية عمل البرمجة الوظيفية مع مفهوم فئات OOP التقليدية (أو التخلص منه لشيء مختلف إذا كان هذا هو الحال).

هل كانت مفيدة؟

المحلول

يجب أن تقرأ هذا السؤال:

JavaScript كلغة وظيفية

هناك الكثير من الروابط المفيدة ، بما في ذلك:

الآن ، لرأي. الكثير من الناس يساء فهم جافا سكريبت, ، ربما لأن بناء الجملة الخاص به يشبه معظم لغات البرمجة الأخرى (حيث تبدو Lisp/Haskell/Ocaml مختلفة تمامًا). JavaScript هو ليس موجه نحو الكائن ، هو في الواقع ملف لغة قائمة على النموذج الأولي. لا يحتوي على فصول أو ميراث كلاسيكي ، لذا لا ينبغي مقارنة Java أو C ++.

يمكن أن تكون JavaScript أفضل مقارنة بـ Lisp ؛ لديها إغلاق ووظائف من الدرجة الأولى. باستخدامها ، يمكنك إنشاء تقنيات برمجة وظيفية أخرى ، مثل التطبيق الجزئي (الكاري).

لنأخذ مثالًا (باستخدام sys.puts من Node.js):

var external;
function foo() {
    external = Math.random() * 1000;
}
foo();

sys.puts(external);

للتخلص من الآثار الجانبية العالمية ، يمكننا لفه في إغلاق:

(function() {
    var external;
    function foo() {
        external = Math.random() * 1000;
    }
    foo();

    sys.puts(external);
})();

لاحظ أنه لا يمكننا فعل أي شيء في الواقع external أو foo خارج النطاق. لقد اختتموا تمامًا في إغلاقهم ، لا يمكن المساس بهم.

الآن ، للتخلص من external اعراض جانبية:

(function() {
    function foo() {
        return Math.random() * 1000;
    }

    sys.puts(foo());
})();

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

أريد أيضًا أن أشير إلى أن خلط البرمجة الوظيفية مع الكائنات أمر جيد تمامًا. خذ هذا على سبيل المثال:

var Square = function(x, y, w, h) {
   this.x = x;
   this.y = y;
   this.w = w;
   this.h = h;
};

function getArea(square) {
    return square.w * square.h;
}

function sum(values) {
    var total = 0;

    values.forEach(function(value) {
        total += value;
    });

    return total;
}

sys.puts(sum([new Square(0, 0, 10, 10), new Square(5, 2, 30, 50), new Square(100, 40, 20, 19)].map(function(square) {
    return getArea(square);
})));

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

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

أعتقد اعتقادا راسخا أن الجوانب الوظيفية يمكن أن تكون مفيدة للغاية في جافا سكريبت ولكن في النهاية ، يجب أن تستخدم كل ما يجعل الكود أكثر قابلية للقراءة وما يناسبك.

نصائح أخرى

عليك أن تفهم أن البرمجة الوظيفية والبرمجة الموجهة للكائنات تتناقض إلى حد ما مع بعضها البعض. لا يمكن أن يكون كلاهما وظيفية بحتة وموجهة نحو الكائن البحت.

البرمجة الوظيفية هو كل شيء عن الحسابات عديمة الجنسية. البرمجة الشيئية هو كل شيء عن انتقالات الدولة. (الصبر هذه. نأمل ألا يكون الأمر سيئًا للغاية)

JavaScript أكثر توجهاً نحو الكائن مما هو وظيفي. مما يعني أنه إذا كنت ترغب في البرمجة بأسلوب وظيفي بحت ، فيجب عليك التخلي عن أجزاء كبيرة من اللغة. على وجه التحديد جميع أجزاء الكائن Orient.

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

أحاول الالتزام بالقواعد التالية:

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

هناك عدد من الفوائد التي يجب الحصول عليها من اتباع هذه القواعد:

  1. سهولة إعادة الاستخدام. كلما كانت الوظيفة أطول وأكثر تعقيدًا ، كلما زادت تخصصها أيضًا ، وبالتالي فكلما كان من المحتمل أن يتم إعادة استخدامه. الآثار العكسية هي أن الوظائف الأقصر تميل إلى إعادة استخدامها بشكل أكثر العامة وبالتالي أسهل في إعادة استخدامها.

  2. موثوقية الكود. من الأسهل التفكير في صحة الكود إذا كان أقل تعقيدًا.

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

تحديث:

اقتراح مدمج من التعليق.

تحديث 2:

وأضاف بعض الروابط المفيدة.

أظن، http://documentcloud.github.com/underscore/ يجب أن تكون مناسبة لما تحتاجه-فهو يوفر أهم وظائف الترتيب العالي للبرمجة الوظيفية وليس لديه وظائف من جانب العميل لمعالجة DOM التي لا تحتاج إلى جانب الخادم. على الرغم من أنه ليس لدي خبرة في ذلك.

كملاحظة جانبية: ميزة IMHO الأساسية للبرمجة الوظيفية الشفافية المرجعية من وظيفة - تعتمد نتيجة الوظيفة فقط على معلماتها - لا تعتمد الوظيفة على التغييرات على الكائنات الأخرى ولا تقدم أي تغيير باستثناء قيمة النتيجة. إنه يجعل من السهل التفكير في صحة البرنامج وقيمة للغاية لتنفيذ الخيوط المتعددة التي يمكن التنبؤ بها (إذا كانت ذات صلة). على الرغم من أن JavaScript ليس لغة الرهان لـ FP - أتوقع أن تكون هياكل البيانات غير القابلة للتغيير مكلفة للغاية لاستخدامها.

لذا هناك شيئان للإشارة ،

  1. في المثال الأول ، لن يتسرب متغيرك إلى المنطقة العالمية وهو بالطريقة التي يجب القيام بها ، حاول عدم استخدام المتغيرات أبدًا دون إعلانها ، أي أن "البيانات" ستؤدي إلى تسرب البيانات إلى المنطقة العالمية.

  2. مثالك الثاني صحيح أيضًا ، سيتم الإعلان عن BAR1 و BAR2 فقط على كائن FOO.

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top