كيفية التحقق مما إذا كان البرنامج النصي يعمل تحت Node.js؟

StackOverflow https://stackoverflow.com/questions/4224606

  •  26-09-2019
  •  | 
  •  

سؤال

لديّ برنامج نصي أتطلبه من برنامج نصي Node.js ، والذي أريد أن أبقيه على محرك JavaScript مستقلًا.

على سبيل المثال ، أريد أن أفعل exports.x = y; فقط إذا كان يعمل تحت Node.js. كيف يمكنني إجراء هذا الاختبار؟


عند نشر هذا السؤال ، لم أكن أعرف ميزة وحدات Node.js تعتمد على commonjs.

بالنسبة للمثال المحدد الذي قدمته ، كان سؤال أكثر دقة هو:

كيف يمكن للنص معرفة ما إذا كان مطلوبًا كوحدة شائعة؟

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

المحلول

من خلال البحث عن دعم commonjs, ، هكذا Underscore.JS المكتبة تفعل ذلك:

تحرير: إلى سؤالك المحدث:

(function () {

    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this; 

    // Create a reference to this
    var _ = new Object();

    var isNode = false;

    // Export the Underscore object for **CommonJS**, with backwards-compatibility
    // for the old `require()` API. If we're not in CommonJS, add `_` to the
    // global object.
    if (typeof module !== 'undefined' && module.exports) {
            module.exports = _;
            root._ = _;
            isNode = true;
    } else {
            root._ = _;
    }
})();

مثال هنا يحتفظ بنمط الوحدة النمطية.

نصائح أخرى

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

هذا ما أستخدمه في libs التي يجب أن تعمل في المتصفح وتحت node.js:

if (typeof window === 'undefined') {
    exports.foo = {};

} else {
    window.foo = {};
}

قد لا يزال ينفجر في حال window تم تعريفه في node.js ولكن لا يوجد جيد سبب قيام شخص ما بذلك ، لأنك ستحتاج صراحة إلى الخروج var أو اضبط العقار على global هدف.

تعديل

لاكتشاف ما إذا كان البرنامج النصي الخاص بك مطلوبًا كوحدة شائعة ، فهذا ليس بالأمر السهل مرة أخرى. الشيء الوحيد الذي يحدده Commonjs هو أن: سيتم تضمين الوحدات النمطية عبر مكالمة إلى الوظيفة require و B: الوحدات تصدر الأشياء عبر الخصائص على exports هدف. الآن كيف يتم تنفيذ ذلك يتم تركه للنظام الأساسي. يلتف Node.js محتوى الوحدة في وظيفة مجهولة:

function (exports, require, module, __filename, __dirname) { 

يرى: https://github.com/ry/node/blob/master/src/node.js#l325

لكن لا حاول اكتشاف ذلك عبر بعض الجنون arguments.callee.toString() الأشياء ، بدلاً من ذلك ، استخدم رمز المثال الخاص بي أعلاه والذي يتحقق من المتصفح. Node.js هي بيئة أنظف وسيلة لذلك من غير المحتمل ذلك window سيتم الإعلان هناك.

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


تحديد node.js فقط

(typeof process !== 'undefined') && (process.release.name === 'node')

سيكتشف هذا ما إذا كنت تعمل في عملية عقدة ، منذ ذلك الحين process.release يحتوي على "البيانات الوصفية المتعلقة بالإصدار الحالي [العقدة].

بعد تفرخ io.js قيمة ال process.release.name قد تصبح أيضا io.js (انظر العملية). لاكتشاف بيئة جاهزة للعقدة ، أعتقد أنه يجب عليك التحقق على النحو التالي:

حدد العقدة (> = 3.0.0) أو io.js

(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)

تم اختبار هذا البيان مع العقدة 5.5.0 ، الإلكترون 0.36.9 (مع العقدة 5.1.1) والكروم 48.0.2564.116.

حدد العقدة (> = 0.10.0) أو io.js

(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')

ألهمني تعليق Daluege للتفكير في دليل أكثر عمومية. هذا يجب أن يعمل من node.js> = 0.10. لم أجد معرفًا فريدًا للإصدارات السابقة.


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

المشكلة في محاولة معرفة البيئة التي يتم تشغيل الكود الخاص بك هي أنه يمكن تعديل أي كائن وإعلانه مما يجعل من المستحيل معرفة الكائنات الأصلية للبيئة ، والتي تم تعديلها بواسطة البرنامج.

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

لنبدأ بالحل المقبول عمومًا المستخدم في مكتبة السطح السفلي:

typeof module !== 'undefined' && module.exports

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

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

ومع ذلك ، فإن الحيلة هي أننا إذا افترضنا أن البرنامج النصي الخاص بك يتم تحميله في النطاق العالمي (والذي سيكون إذا تم تحميله عبر علامة نصية) ، فلا يمكن حجز متغير في إغلاق خارجي ، لأن المتصفح لا يسمح بذلك . تذكر الآن في العقدة ، this الكائن هو كائن فارغ ، ومع ذلك ، module المتغير لا يزال متاحا. هذا لأنه أعلن في إغلاق خارجي. لذلك يمكننا بعد ذلك إصلاح فحص INFRESSCORE عن طريق إضافة شيك إضافي:

this.module !== module

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

وبالتالي ، فإن الاختبار النهائي هو:

typeof module !== 'undefined' && this.module !== module

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

ما يلي يعمل في المتصفح ما لم يتم تخريبه عن قصد ، بشكل صريح:

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}

بام.

إليك طريقة رائعة للقيام بذلك أيضًا:

const isBrowser = this.window === this;

هذا يعمل لأنه في المتصفحات ، "هذا المتغير" لديه مرجع ذاتي يسمى "نافذة". هذه المرجع الذاتي غير موجود في العقدة.

  • في المتصفح "هذا" هو إشارة إلى الكائن العالمي ، تسمى "النافذة".
  • في العقدة "هذا" هو إشارة إلى كائن Module.exports.
    • 'هذا هو ليس إشارة إلى الكائن العالمي للعقدة ، تسمى "Global".
    • 'هذا هو ليس إشارة إلى مساحة الإعلان المتغيرة للوحدة.

لكسر المتصفح المقترح أعلاه ، يجب عليك القيام بشيء مثل ما يلي

this.window = this;

قبل تنفيذ الشيك.

بعد آخر الكشف عن البيئة:

(المعنى: معظم الإجابات هنا على ما يرام.)

function isNode() {
    return typeof global === 'object'
        && String(global) === '[object global]'
        && typeof process === 'object'
        && String(process) === '[object process]'
        && global === global.GLOBAL // circular ref
        // process.release.name cannot be altered, unlike process.title
        && /node|io\.js/.test(process.release.name)
        && typeof setImmediate === 'function'
        && setImmediate.length === 4
        && typeof __dirname === 'string'
        && Should I go on ?..
}

قليلا بجنون العظمة أليس كذلك؟ يمكنك جعل هذا أكثر مطوّلة من خلال التحقق من المزيد الكرات.

لكن لا!.

كل هذه أعلاه يمكن أن تكون مزيفة/محاكاة على أي حال.

على سبيل المثال لتزوير global هدف:

global = {
    toString: function () {
        return '[object global]';
    },
    GLOBAL: global,
    setImmediate: function (a, b, c, d) {}
 };
 setImmediate = function (a, b, c, d) {};
 ...

لن يتم إرفاق هذا بالكائن العالمي الأصلي للعقدة ولكنه سيتم إرفاقه بـ window اعتراض في متصفح. لذلك سوف يعني أنك في Node Env داخل المتصفح.

الحياة قصيرة!

هل نهتم إذا كانت بيئتنا مزيفة؟ سيحدث ذلك عندما يعلن بعض المطورين الغبي عن متغير عالمي يسمى global في النطاق العالمي. أو بعض الشرير Dev حقن رمز في ENV لدينا بطريقة أو بأخرى.

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

وماذا في ذلك؟

إذا استهدف بيئات 2: المتصفح والعقدة ؛
"use strict"; ؛ وإما ببساطة تحقق من window أو global; ؛ وتشير بوضوح إلى أنه في المستندات أن الكود الخاص بك يدعم هذه البيئات فقط. هذا هو!

var isBrowser = typeof window !== 'undefined'
    && ({}).toString.call(window) === '[object Window]';

var isNode = typeof global !== "undefined" 
    && ({}).toString.call(global) === '[object global]';

إذا كان ذلك ممكنا لحالة الاستخدام الخاصة بك ؛ بدلا من اكتشاف البيئة ؛ قم بالكشف عن الميزات المتزامنة داخل كتلة المحاولة/الصيد. (هذه سوف تستغرق بضعة ميلي ثانية لتنفيذها).

على سبيل المثال

function isPromiseSupported() {
    var supported = false;
    try {
        var p = new Promise(function (res, rej) {});
        supported = true;
    } catch (e) {}
    return supported;
}

معظم الحلول المقترحة يمكن أن تكون مزيفة بالفعل. طريقة قوية هي التحقق من الداخلية Class خاصية الكائن العالمي باستخدام Object.prototype.toString. لا يمكن تزيين الفصل الداخلي في جافا سكريبت:

var isNode = 
    typeof global !== "undefined" && 
    {}.toString.call(global) == '[object global]';

ماذا عن استخدام كائن العملية والتحقق execpath ل node?

Process.execpath

هذا هو المسار المطلق للمسار القابل للتنفيذ الذي بدأ العملية.

مثال:

/usr/local/bin/node

كيف يمكن للنص معرفة ما إذا كان مطلوبًا كوحدة شائعة؟

ذات الصلة: للتحقق مما إذا كان مطلوبًا كوحدة مقابل تشغيل مباشرة في العقدة ، يمكنك التحقق require.main !== module. http://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module

هذا هو اختلالي في ما هو أعلاه:

(function(publish) {
    "use strict";

    function House(no) {
        this.no = no;
    };

    House.prototype.toString = function() {
        return "House #"+this.no;
    };

    publish(House);

})((typeof module == 'undefined' || (typeof window != 'undefined' && this == window))
    ? function(a) {this["House"] = a;}
    : function(a) {module.exports = a;});

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

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

أنا استخدم process للتحقق من الحصول على node.js مثل ذلك

if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

أو

if (typeof(process) !== 'undefined' && process.title === 'node') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

موثق هنا

node.js لديه process كائن ، طالما لم يكن لديك أي نص آخر ينشئه process يمكنك استخدامه لتحديد ما إذا كان الرمز يعمل على العقدة.

var isOnNodeJs = false;
if(typeof process != "undefined") {
  isOnNodeJs = true;
}

if(isOnNodeJs){
  console.log("you are running under node.js");
}
else {
  console.log("you are NOT running under node.js");
}

هذه طريقة آمنة ومباشرة لضمان التوافق بين JavaScript من جانب الخادم وجاف العميل ، والتي ستعمل أيضًا مع المتصفح ، المتطلبات أو الشائعة من جانب العميل:

(function(){

  // `this` now refers to `global` if we're in NodeJS
  // or `window` if we're in the browser.

}).call(function(){
  return (typeof module !== "undefined" &&
    module.exports &&
    typeof window === 'undefined') ?
    global : window;
}())

تعديل: بخصوص سؤالك المحدث: "كيف يمكن للنص معرفة ما إذا كان مطلوبًا كوحدة شائعة؟" لا أعتقد أنه يمكن. يمكنك التحقق مما إذا كان exports هو كائن (if (typeof exports === "object"))، حيث المواصفات يتطلب تقديمه للوحدات النمطية ، ولكن كل ما يخبرك هو ... exports هو كائن. :-)


الإجابة الأصلية:

أنا متأكد من أن هناك بعض الرمز الخاص بـ NodeJS (EventEmitter, ، ربما لا ، عليك استخدام require للحصول على وحدة الأحداث ؛ انظر أدناه) التي يمكنك التحقق منها ، ولكن كما قال ديفيد ، من الأفضل أن تكتشف الميزة (بدلاً من البيئة) إذا كان من المنطقي القيام بذلك.

تحديث: ربما شيء مثل:

if (typeof require === "function"
    && typeof Buffer === "function"
    && typeof Buffer.byteLength === "function"
    && typeof Buffer.prototype !== "undefined"
    && typeof Buffer.prototype.write === "function") {

لكن هذا يخبرك فقط أنك في بيئة مع require وشيء يشبه إلى حد كبير Nodejs Buffer. :-)

const isNode =
  typeof process !== 'undefined' &&
  process.versions != null &&
  process.versions.node != null;

خذ مصدر node.js وقم بتغييره لتحديد متغير مثل runningOnNodeJS. تحقق من هذا المتغير في الكود الخاص بك.

إذا لم تتمكن من الحصول على إصدار خاص بك من Node.js ، فتأمل طلب ميزة في المشروع. اطلب منهم تحديد متغير يمنحك إصدار Node.js الذي تقوم بتشغيله. ثم تحقق من هذا المتغير.

منشور قديم جدًا ، لكنني قمت بحلها فقط عن طريق لف عبارات المتطلبات في محاولة - catch

    try {

           var fs = require('fs')

} catch(e) {
       alert('you are not in node !!!')
}

أعتقد أنه بسيط للغاية.

     if (typeof process !== 'undefined' process && process.title === 'node') {
        // Your code here
     }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top