วิธีตรวจสอบว่าสคริปต์ทำงานภายใต้ Node.js หรือไม่


159

ฉันมีสคริปต์ที่ฉันต้องการจากสคริปต์ Node.js ซึ่งฉันต้องการให้เอ็นจิ้น JavaScript อิสระ

ตัวอย่างเช่นฉันต้องการทำexports.x = y;เฉพาะเมื่อมันทำงานภายใต้ Node.js ฉันจะทำการทดสอบนี้ได้อย่างไร


คุณลักษณะเมื่อโพสต์คำถามนี้ผมไม่ทราบ Node.js โมดูลจะขึ้นอยู่กับCommonJS

สำหรับตัวอย่างเฉพาะที่ฉันให้มาคำถามที่แม่นยำกว่านี้ก็คือ:

สคริปต์จะทราบได้อย่างไรว่าจำเป็นต้องใช้เป็นโมดูล CommonJS หรือไม่


3
ฉันไม่รู้ว่าทำไมคุณถึงพยายามทำเช่นนี้ แต่ตามกฎทั่วไปคุณควรใช้การตรวจจับคุณสมบัติแทนการตรวจจับเครื่องยนต์ quirksmode.org/js/support.html
Quentin

4
นี่เป็นคำขอเกี่ยวกับวิธีใช้การตรวจจับคุณสมบัติ แต่คำถามนั้นอธิบายตัวเองไม่ดี
ขาวดำ

เผยแพร่ห้องสมุดเพื่อการใช้งานของฉันเองช่วยได้สิ่งนี้จะช่วยnpmjs.com/package/detect-is-node
abhirathore2006


ปัญหาหนึ่งของคำถามและคำตอบส่วนใหญ่คือข้อสันนิษฐานว่ามีความเป็นไปได้เพียงสองอย่างเท่านั้น: Browser หรือ Node.js มีความเป็นไปได้ที่จะไม่มีทั้งเบราว์เซอร์และ Node.js เช่นกรณีของ Oracle Java Nashorn หากติดตั้ง JDK แล้วคำสั่ง jjs ให้คุณเรียกใช้สคริปต์ แต่มีความแตกต่างมากมายระหว่าง Nashorn และ Node.js ดังนั้นคุณจึงไม่สามารถสันนิษฐานได้ และใครจะรู้ว่าตัวเลือกในอนาคตอาจนำมาซึ่งใคร? จำเป็นต้องมีการตรวจสอบคุณสมบัติ

คำตอบ:


80

โดยมองหาการสนับสนุน 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._ = _;
    }
})();

ตัวอย่างที่นี่ยังคงรูปแบบโมดูล


45
สิ่งนี้ตรวจพบการสนับสนุน CommonJS ซึ่งเบราว์เซอร์อาจสนับสนุน
mikemaccana

7
มีปัญหานี่และ nailer "ตอกย้ำ" ฉันลองใช้ CommonJS ในเบราว์เซอร์และโมดูลโหลดเดอร์ที่ฉันใช้กำหนด module.exports ดังนั้นโซลูชันนี้จะบอกฉันไม่ถูกต้องว่าอยู่ในโหนด
Mark Melville

1
@MarkMelville เนื้อหาตรงนี้เป็นสิ่ง OP จะขอดังนั้นจึงไม่ได้เป็นปัญหา
Ross

13
ถ้อยคำไม่ดีในส่วนของฉัน ฉันหมายความว่ามีปัญหากับวิธีนี้ OP อาจยอมรับ แต่ฉันทำไม่ได้
Mark Melville

7
นี่ไม่ใช่คำตอบที่ดีที่สุดแน่นอน
user3751385

107

ไม่มีวิธีที่เชื่อถือได้ในการตรวจจับการทำงานใน Node.js เนื่องจากทุกเว็บไซต์สามารถประกาศตัวแปรเดียวกันได้อย่างง่ายดาย แต่เนื่องจากไม่มีwindowวัตถุใน Node.js โดยค่าเริ่มต้นคุณสามารถไปทางอื่น ๆ และตรวจสอบว่าคุณกำลังทำงานอยู่ภายใน เบราว์เซอร์

นี่คือสิ่งที่ฉันใช้สำหรับ libs ที่ควรทำงานได้ทั้งในเบราว์เซอร์และภายใต้ Node.js:

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

} else {
    window.foo = {};
}

มันอาจยังคงระเบิดในกรณีที่windowกำหนดไว้ใน Node.js แต่ไม่มีเหตุผลที่ดีสำหรับใครบางคนทำเช่นนี้เนื่องจากคุณจะต้องออกไปvarหรือตั้งค่าคุณสมบัติบนglobalวัตถุอย่างชัดเจน

แก้ไข

สำหรับการตรวจสอบว่าสคริปต์ของคุณจำเป็นต้องใช้เป็นโมดูล CommonJS หรือไม่นั่นไม่ใช่เรื่องง่ายอีกต่อไป สิ่งที่ commonJS ระบุคือ A: โมดูลจะถูกรวมผ่านการเรียกใช้ฟังก์ชัน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จะประกาศที่นั่น


2
เกี่ยวกับ "Node.js เป็นสภาพแวดล้อมที่สะอาดกว่าดังนั้นจึงไม่น่าที่หน้าต่างจะถูกประกาศที่นั่น": ฉันเพิ่งมาที่นี่เพื่อค้นหาวิธีที่จะทราบว่าสคริปต์ของฉันทำงานในเบราว์เซอร์ที่จำลองโดย node.js + JSDOM หรือไม่ หรือในเบราว์เซอร์ธรรมดา ... เหตุผลก็คือฉันมีลูปไม่สิ้นสุดโดยใช้ setTimeout เพื่อตรวจสอบตำแหน่ง URL ซึ่งใช้ได้ในเบราว์เซอร์ แต่ช่วยให้สคริปต์ node.js ทำงานตลอดไป ... ดังนั้นอาจมีหน้าต่าง ในสคริปต์ node.js after all :)
Eric Bréchemier

1
@Eric ฉันสงสัยอย่างมากว่ามันจะอยู่ในขอบเขตทั่วโลกดังนั้นถ้าคุณนำเข้าบางสิ่งบางอย่างเช่นwindowในบรรทัดแรกของโมดูลของคุณคุณไม่ควรมีปัญหาใด ๆ นอกจากนี้คุณยังสามารถเรียกใช้ฟังก์ชั่นที่ไม่ระบุชื่อและตรวจสอบ[[Class]]ของthisภายใน (ทำงานเฉพาะในโหมดที่ไม่ใช่เข้มงวด) โปรดดูที่ "คลาส" ภายใต้: bonsaiden.github.com/JavaScript-Garden/#typeof
Ivo Wetzel

1
ปัญหาของฉันแตกต่างจาก OP เล็กน้อย: ฉันไม่ต้องการสคริปต์มันโหลดโดย JSDOM พร้อมหน้าต่างจำลองเป็นบริบทโลก ... มันยังคงทำงานโดย node.js + V8 ในบริบทที่แตกต่างจากโมดูลปกติ
Eric Bréchemier

1
อาจเป็น ... ฉันไปอีกทิศทางหนึ่ง: 1) ตรวจหาการสนับสนุนสำหรับ onhashchange ("onhashchange" ในหน้าต่าง) เพื่อหลีกเลี่ยงการสร้าง infinite loop 2) จำลองการสนับสนุนโดยการตั้งค่าคุณสมบัติ onhashchange บนหน้าต่างจำลองในสคริปต์ node.js หลัก
Eric Bréchemier

1
typeof self === 'object'อาจปลอดภัยกว่าเนื่องจากtypeof window === 'undefined'ล้มเหลวในขอบเขตผู้ปฏิบัติงานเว็บ
Lewis

45

ขณะนี้ฉันสะดุดการตรวจจับ Node ที่ไม่ถูกต้องซึ่งไม่ได้ตระหนักถึง Node-environment ในElectronเนื่องจากการตรวจจับคุณสมบัติที่ทำให้เข้าใจผิด โซลูชันต่อไปนี้ระบุสภาพแวดล้อมของกระบวนการอย่างชัดเจน


ระบุ Node.js เท่านั้น

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

สิ่งนี้จะค้นพบว่าคุณกำลังใช้งานโหนดอยู่หรือไม่เนื่องจากprocess.releaseประกอบด้วย "ข้อมูลเมตาที่เกี่ยวข้องกับ [Node-] รีลีส"

หลังจากการวางไข่ของio.jsค่าของprocess.release.nameก็อาจกลายเป็นio.js(ดูกระบวนการ doc ) หากต้องการตรวจสอบสภาพแวดล้อมที่พร้อมใช้งานของ Node อย่างเหมาะสมฉันเดาว่าคุณควรตรวจสอบดังนี้:

ระบุโหนด (> = 3.0.0) หรือ io.js

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

คำสั่งนี้ทดสอบกับ Node 5.5.0, Electron 0.36.9 (พร้อม Node 5.1.1) และ Chrome 48.0.2564.116

ระบุโหนด (> = 0.10.0) หรือ io.js

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

ความคิดเห็นของ @ daluege เป็นแรงบันดาลใจให้ฉันคิดเกี่ยวกับข้อพิสูจน์ทั่วไปเพิ่มเติม นี้ควรทำงานจาก Node.js> = 0.10 ฉันไม่พบตัวระบุที่ไม่ซ้ำสำหรับรุ่นก่อนหน้า


Ps: ฉันกำลังโพสต์คำตอบนั้นไว้ที่นี่เนื่องจากคำถามทำให้ฉันอยู่ที่นี่แม้ว่า OP จะค้นหาคำตอบสำหรับคำถามอื่น


2
ดูเหมือนจะเป็นวิธีที่เชื่อถือได้มากที่สุดขอบคุณ แม้ว่าจะทำงานกับรุ่น> = 3.0.0 เท่านั้น
ฆา

@daluege - ขอบคุณสำหรับแรงบันดาลใจ ฉันโชคไม่ดีที่ไม่พบหลักฐานสำหรับต่ำกว่า 0.10
Florian Breisch

3
ผมพบว่าการใช้ตอบสนอง webpack, processและprocess.versionมีอยู่ในมัดผมจึงเพิ่มการตรวจสอบพิเศษสำหรับการprocess.versionที่process.release.nodeจะไม่ได้กำหนดในฝั่งไคลเอ็นต์ แต่มีรุ่นโหนดเป็นค่าในฝั่งเซิร์ฟเวอร์
แอรอน

@Aaron: ขอบคุณสำหรับคำใบ้นั้น ฉันไม่สามารถค้นหาคำจำกัดความของprocess.versionตัวแปรใด ๆ(ในการตอบสนอง, webpack หรือ react-webpack) ฉันขอขอบคุณคำใบ้ใด ๆ ที่กำหนดตัวแปรเวอร์ชันเพื่อเพิ่มลงในคำตอบ ทั้งนี้ขึ้นอยู่กับข้อ จำกัด ของ release.node สำหรับ node> = 3.xx
Florian Breisch

2
สายการบินเดียวและปลอดภัยยิ่งขึ้น:function isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }
brillout

25

ปัญหาที่เกิดขึ้นจากการพยายามคิดว่าสภาพแวดล้อมที่รหัสของคุณกำลังทำงานอยู่คือวัตถุใด ๆ ที่สามารถแก้ไขได้และประกาศว่ามันใกล้จะเป็นไปไม่ได้ที่จะคิดออกว่าวัตถุใดที่มีถิ่นกำเนิดในสภาพแวดล้อมและโปรแกรมนั้นได้ทำการแก้ไข

อย่างไรก็ตามมีเทคนิคเล็กน้อยที่เราสามารถใช้เพื่อพิจารณาว่าคุณอยู่ในสภาพแวดล้อมแบบใด

เริ่มต้นด้วยโซลูชันที่ได้รับการยอมรับโดยทั่วไปที่ใช้ในไลบรารีขีดล่าง:

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

เทคนิคนี้ใช้ได้ผลดีกับฝั่งเซิร์ฟเวอร์อย่างแท้จริงเมื่อrequireมีการเรียกใช้ฟังก์ชั่นมันจะรีเซ็ตthisวัตถุเป็นวัตถุว่างเปล่าและกำหนดนิยามใหม่moduleให้คุณอีกครั้งซึ่งหมายความว่าคุณไม่ต้องกังวลเกี่ยวกับการดัดแปลงภายนอก ตราบใดที่โค้ดของคุณโหลดrequireคุณก็ปลอดภัย

อย่างไรก็ตามสิ่งนี้แยกออกจากกันบนเบราว์เซอร์เนื่องจากทุกคนสามารถกำหนดได้อย่างง่ายดายmoduleเพื่อให้ดูเหมือนเป็นวัตถุที่คุณกำลังมองหา ในมือข้างหนึ่งนี่อาจเป็นพฤติกรรมที่คุณต้องการ แต่มันก็เป็นตัวกำหนดว่าตัวแปรใดที่ผู้ใช้ไลบรารีสามารถใช้ในขอบเขตส่วนกลาง อาจมีบางคนต้องการใช้ตัวแปรที่มีชื่อmoduleที่exportsอยู่ข้างในเพื่อการใช้งานอื่น ไม่น่าเป็นไปได้ แต่เราคือใครที่จะตัดสินว่าคนอื่นสามารถใช้ตัวแปรอะไรได้บ้างเนื่องจากสภาพแวดล้อมอื่นใช้ชื่อตัวแปรนั้น

อย่างไรก็ตามเคล็ดลับคือถ้าเราสมมติว่าสคริปต์ของคุณกำลังโหลดอยู่ในขอบเขตทั่วโลก (ซึ่งจะเป็นถ้ามันถูกโหลดผ่านแท็กสคริปต์) ตัวแปรไม่สามารถสงวนไว้ในการปิดด้านนอกเพราะเบราว์เซอร์ไม่อนุญาต . ตอนนี้จำได้ในโหนดthisวัตถุเป็นวัตถุว่างเปล่า แต่moduleตัวแปรยังคงมีอยู่ นั่นเป็นเพราะมันถูกประกาศในการปิดด้านนอก ดังนั้นเราสามารถแก้ไขเครื่องหมายขีดล่างโดยเพิ่มการตรวจสอบพิเศษ:

this.module !== module

ด้วยสิ่งนี้หากมีคนประกาศmoduleขอบเขตทั่วโลกในเบราว์เซอร์มันจะถูกวางไว้ในthisวัตถุซึ่งจะทำให้การทดสอบล้มเหลวเพราะthis.moduleจะเป็นวัตถุเดียวกับโมดูล บนโหนดthis.moduleไม่มีอยู่และmoduleมีอยู่ภายในการปิดด้านนอกดังนั้นการทดสอบจะสำเร็จเนื่องจากไม่เท่ากัน

ดังนั้นการทดสอบขั้นสุดท้ายคือ:

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

หมายเหตุ: แม้ว่าสิ่งนี้จะอนุญาตให้ใช้moduleตัวแปรได้อย่างอิสระในขอบเขตส่วนกลาง แต่ก็ยังเป็นไปได้ที่จะเลี่ยงผ่านสิ่งนี้บนเบราว์เซอร์โดยสร้างการปิดใหม่และการประกาศmoduleภายในจากนั้นโหลดสคริปต์ภายในการปิดนั้น ณ จุดนี้ผู้ใช้ทำการจำลองสภาพแวดล้อมของโหนดอย่างสมบูรณ์และหวังว่าจะรู้ว่าพวกเขากำลังทำอะไรอยู่และกำลังพยายามทำรูปแบบโหนดที่ต้องการ หากรหัสถูกเรียกในแท็กสคริปต์รหัสนั้นจะยังคงปลอดภัยจากการปิดภายนอกใหม่


2
ว้าวขอบคุณที่อธิบายเหตุผลอย่างชัดเจนเบื้องหลังแต่ละชิ้นในหนึ่งซับของคุณ
Jon Coombs

ได้Cannot read property 'module' of undefinedเพราะนี่คือไม่ได้กำหนดในการทดสอบมอคค่าเช่น
srghma

20

งานต่อไปนี้ในเบราว์เซอร์เว้นแต่จะก่อวินาศกรรมโดยเจตนา:

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

ปัง


4
var process = {toString: function () {return '[กระบวนการวัตถุ]'; }};
Nick Desaulniers

1
มีเหตุผลบางอย่างที่คุณใช้process+''แทนprocess.toString()หรือไม่?
อันตราย

3
เกือบจะ ใช้สิ่งนี้แทน:Object.prototype.toString.call(process)
sospedra

2
นี่คือคำตอบที่ดีที่สุดสำหรับคำถามนี้
loretoparisi

3
@harmic: var process = null;จะทำให้กรณีที่สองล้มเหลว ในทั้ง Javascript และ Java นิพจน์'' + xจะสร้างเช่นเดียวกับx.toString()เมื่อxเป็นที่น่ารังเกียจอดีตที่สร้าง"null"หรือ"undefined"ที่หลังจะโยนข้อผิดพลาด
joeytwiddle

17

นี่เป็นวิธีที่ยอดเยี่ยมในการทำเช่นกัน:

const isBrowser = this.window === this;

สิ่งนี้ทำงานได้เพราะในเบราว์เซอร์ตัวแปร 'นี้' ทั่วโลกมีการอ้างอิงตนเองเรียกว่า 'หน้าต่าง' การอ้างอิงตนเองนี้ไม่มีอยู่ในโหนด

  • ในเบราว์เซอร์ 'นี้' เป็นการอ้างอิงถึงวัตถุทั่วโลกที่เรียกว่า 'หน้าต่าง'
  • ในโหนด 'this' เป็นการอ้างอิงไปยังวัตถุ module.exports
    • 'this' ไม่ใช่การอ้างอิงถึง Node global object เรียกว่า 'global'
    • 'this' ไม่ใช่การอ้างอิงไปยังพื้นที่การประกาศตัวแปรโมดูล

หากต้องการทำลายการตรวจสอบเบราว์เซอร์ที่แนะนำข้างต้นคุณต้องทำสิ่งต่อไปนี้

this.window = this;

ก่อนดำเนินการตรวจสอบ


ทำไมไม่ง่าย ๆconst isBrowser = this.window !== undefined? และในทางทฤษฎีในโหนดฉันสามารถทำได้this.window = thisเพื่อหลอกการแก้ปัญหา
ไทเลอร์ลอง

11

ยังตรวจจับสภาพแวดล้อมอื่น:

(ความหมาย: คำตอบส่วนใหญ่ที่นี่ไม่เป็นไร)

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

หวาดระแวงบิตใช่ไหม? คุณสามารถทำให้มากขึ้นโดยการตรวจสอบอย่างละเอียดมากGlobals

แต่อย่า!

สิ่งเหล่านี้สามารถปลอมแปลง / จำลองได้

ตัวอย่างเช่นการปลอม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 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]';

ถ้าเป็นไปได้สำหรับกรณีการใช้งานของคุณ; แทนที่จะตรวจจับสภาพแวดล้อม ทำการตรวจจับคุณสมบัติแบบซิงโครนัสภายในบล็อกลอง / catch (สิ่งเหล่านี้จะใช้เวลาไม่กี่มิลลิวินาทีในการดำเนินการ)

เช่น

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

9

โซลูชั่นที่นำเสนอส่วนใหญ่สามารถปลอมแปลงได้จริง เป็นวิธีที่มีประสิทธิภาพคือการตรวจสอบภายในคุณสมบัติของวัตถุทั่วโลกโดยใช้Class Object.prototype.toStringคลาสภายในไม่สามารถปลอมใน JavaScript:

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

2
สิ่งนี้จะกลับมาจริงภายใต้เบราว์เซอร์
alt

1
คุณทดสอบหรือไม่ ฉันไม่เห็นว่า browserify สามารถเปลี่ยนคลาสภายในของวัตถุได้อย่างไร นี้จะต้องมีการเปลี่ยนรหัสใน JavaScript VM หรือเขียนทับObject.prototype.toStringซึ่งเป็นวิธีปฏิบัติที่ไม่ดีจริงๆ
Fabian Jakobs

ฉันทดสอบมัน นี่คือสิ่งที่ browserify ทำ: var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};
Vanuan

คุณเห็นใน Chrome จะมีค่าเท่ากับ({}.toString.call(window)) "[object global]"
Vanuan

2
มันแปลกเพราะwindow.toString()ผลิต"[object Window]"
Vanuan

5

สิ่งที่เกี่ยวกับการใช้วัตถุกระบวนการและการตรวจสอบexecPathสำหรับnode?

process.execPath

นี่คือชื่อพา ธ สัมบูรณ์ของไฟล์เรียกทำงานที่เริ่มต้นกระบวนการ

ตัวอย่าง:

/ usr / local / bin / โหนด


2
เกี่ยวกับwindow.process = {execPath: "/usr/local/bin/node"};อะไร
КонстантинВан

4

สคริปต์จะบอกได้อย่างไรว่าจำเป็นต้องใช้เป็นโมดูล commonjs หรือไม่?

ที่เกี่ยวข้อง: require.main !== moduleการตรวจสอบว่าจะได้รับต้องเป็นโมดูลเทียบกับการทำงานโดยตรงในโหนดคุณสามารถตรวจสอบ http://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module


4

นี่คือรูปแบบของฉันในสิ่งที่ด้านบน:

(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.window ซึ่งเป็น == หน้าต่าง) สำหรับฉันแล้วดูเหมือนว่าสิ่งนี้จะไม่เกิดขึ้นเว้นแต่คุณจะอยู่ในเบราว์เซอร์หรือในสภาพแวดล้อมที่ต้องการให้คุณเชื่อว่าคุณอยู่ในเบราว์เซอร์ ในกรณีอื่น ๆ ทั้งหมดหากมีการประกาศตัวแปร 'โมดูล' ส่วนกลางจะใช้สิ่งนั้นมิฉะนั้นจะใช้วัตถุทั่วโลก


4

ฉันใช้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
}

เอกสารที่นี่


2
process.titleสามารถเปลี่ยนแปลงได้
Ben Barkay

จากนั้นตรวจสอบชื่อที่คุณเปลี่ยนเป็น หรือใช้ process.version
Chris

หากคุณเขียนหนังสือให้กับห้องสมุด (อย่างที่ควรจะเป็น) คุณจะไม่สามารถคาดหวังได้ว่าชื่อหนังสือควรเป็นอย่างไร
Ben Barkay

3

ในขณะที่เขียนนี้คำตอบนี้เป็นตัวเลือก "เร็ว ๆ นี้" มากกว่าเนื่องจากใช้ประโยชน์จากคุณสมบัติใหม่ ๆ ของ JavaScript

const runtime = globalThis.process?.release?.name || 'not node'
console.log(runtime)

runtimeค่าจะเป็นอย่างใดอย่างหนึ่งหรือnodenot node

ดังที่กล่าวไว้แล้วสิ่งนี้ขึ้นอยู่กับคุณลักษณะใหม่ ๆ ของ JavaScript globalThisเป็นคุณสมบัติที่สรุปได้ในข้อมูลจำเพาะ ECMAScript 2020 การรวมสายเชื่อมต่อ / nullish แบบไม่บังคับ ( ?ส่วนหนึ่งglobalThis.process?.release?.name) ได้รับการสนับสนุนในเอนจิ้นV8 ซึ่งมาพร้อมกับ Chrome 80 ตั้งแต่วันที่ 4/8/2020 รหัสนี้จะทำงานในเบราว์เซอร์ แต่จะไม่ทำงานในโหนดตั้งแต่โหนด 13 ใช้สาขา V8 7.9.xxx ฉันเชื่อว่าโหนด 14 (ครบกำหนดที่จะวางจำหน่ายในวันที่ 4/21/2020) ควรใช้ V8 8.x +

วิธีนี้มาพร้อมกับข้อ จำกัด ในปัจจุบันที่ดีต่อสุขภาพ อย่างไรก็ตาม; ก้าวที่เบราว์เซอร์ / โหนดได้รับการปล่อยตัวในที่สุดมันจะเป็นหนึ่งซับที่เชื่อถือได้


1
นี่ควรเป็นคำตอบที่ยอมรับได้! และทุกคนควรใช้โหนด 14 btw
Sceat

2

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");
}

2

นี่เป็นวิธีที่ปลอดภัยและตรงไปตรงมาในการรับรองความเข้ากันได้ระหว่างฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์จาวาสคริปต์ซึ่งจะทำงานกับเบราว์เซอร์, RequireJS หรือ CommonJS รวมถึงฝั่งไคลเอ็นต์:

(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;
}())

1

แก้ไข : เกี่ยวกับคำถามที่อัปเดตของคุณ: "สคริปต์จะทราบได้อย่างไรว่าจำเป็นต้องใช้เป็นโมดูล commonjs หรือไม่" ฉันไม่คิดว่าจะทำได้ คุณสามารถตรวจสอบว่าexportsเป็นวัตถุ ( if (typeof exports === "object")) เนื่องจากข้อมูลจำเพาะต้องการให้มีการให้บริการกับโมดูลหรือไม่ แต่สิ่งที่บอกคุณว่า ... exportsคือวัตถุ :-)


คำตอบเดิม:

ฉันแน่ใจว่ามีสัญลักษณ์เฉพาะ NodeJS ( EventEmitterบางที คุณไม่ต้องใช้requireเพื่อรับโมดูลเหตุการณ์ดูด้านล่าง ) ที่คุณสามารถตรวจสอบได้ แต่อย่างที่ David พูดไว้ กว่าสภาพแวดล้อม) ถ้ามันเหมาะสมที่จะทำเช่นนั้น

อัปเดต : บางทีสิ่งที่ชอบ:

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

แต่นั่นแค่บอกคุณว่าคุณอยู่ในสภาพแวดล้อมrequireและมีอะไรบางอย่างที่เหมือนกับ NodeJS Bufferมาก :-)


ฉันยังคงสามารถทำลายมันได้ด้วยการตั้งค่าทุกสิ่งในเว็บไซต์ ... ที่เกินความจริง;) การตรวจสอบว่าอยู่ในเบราว์เซอร์นั้นง่ายกว่าเนื่องจากสภาพแวดล้อมของโหนดนั้นสะอาดกว่า
Ivo Wetzel

1
@Ivo: ใช่เห็นประโยคสุดท้ายของฉัน ฉันสามารถทำลายการตรวจสอบของคุณได้อย่างง่ายดายโดยการกำหนดwindowตัวแปรภายในแอปพลิเคชัน NodeJS :-)
TJ Crowder

1
@Ivo: ฉันจะไม่เป็นที่ทุกคนประหลาดใจถ้ามีคนที่กำหนดไว้windowใน NodeJS โมดูลเพื่อให้พวกเขาอาจรวมถึงรหัสที่เป็นที่พึ่งwindowเป็นวัตถุทั่วโลกและไม่ต้องการที่จะปรับเปลี่ยนรหัสว่า ฉันจะไม่ทำคุณก็ไม่ได้ แต่ฉันพนันได้เลยว่ามีใครบางคน :-) หรือพวกเขาเคยใช้ความwindowหมายอย่างอื่น
TJ Crowder

1
@Ivo: yuiblog.com/blog/2010/04/09/...เป็นหนึ่งในเหตุผลว่าทำไมวัตถุหน้าต่างอาจจะกำหนดไว้ใน Node.js
slebetman

1
@TJCrowdertypeof process !== "undefined" && process.title === "node"
Raynos



-1

นำซอร์สของ node.js และเปลี่ยนเพื่อกำหนดตัวแปรเช่น runningOnNodeJSและเปลี่ยนเป็นกำหนดตัวแปรเช่น ตรวจสอบตัวแปรนั้นในรหัสของคุณ

หากคุณไม่สามารถมี node.js เวอร์ชันส่วนตัวให้เปิดการร้องขอคุณสมบัติในโครงการ ขอให้พวกเขากำหนดตัวแปรที่ให้เวอร์ชันของ node.js ที่คุณใช้อยู่จากนั้นตรวจสอบตัวแปร


1
นั่นอีกครั้งไม่ได้แก้ปัญหาของเขา (โดยทั่วไปแก้ไม่ได้) ฉันสามารถสร้างตัวแปรในเบราว์เซอร์ได้อีกครั้ง วิธีที่ดีกว่าคือการป้องกันไม่ให้สคริปต์สร้างwindowโกลบอลขึ้นมาเดาว่าฉันจะส่งคำขอคุณลักษณะในไฟล์นั้น
Ivo Wetzel

@Ivo: มันเป็นความคิดที่ไม่ดีที่จะทำลายรหัสที่ใช้ jsdom ( github.com/tmpvar/jsdom ) เพื่อทำการจัดการกับฝั่งเซิร์ฟเวอร์โดยใช้ไลบรารีที่คุ้นเคยเช่น YUI และ jQuery และมีรหัสในการผลิตที่ทำเช่นนี้
slebetman

@slebetman ไม่มันจะไม่ทำลาย jsdom ฉันกำลังพูดถึงทั่วโลกเหมือนไม่มีคำสั่ง var ทั่วโลกตัวอย่างรหัสที่มีการใช้varคำสั่งคนที่เพิ่งรั่วไหลลงใน namespace ทั่วโลกดีที่พวกเขาไม่ได้รับแนวคิดของโมดูลในตัวเองแล้ว
Ivo Wetzel

@Ivo มีความรุนแรงเหมือนว่าเราควรใช้ความสามารถในการกินเค้กเพราะคนอ้วนมากเกินไป คุณต้องถ่วงพื้นที่ทั่วโลกเพื่อให้ได้ไลบรารีที่ใช้งานได้ระหว่างโมดูล หรือคุณสามารถรวมมันทั้งหมดในโมดูลเดียว แต่แล้วประเด็นคืออะไร
Ben Barkay

-1

โพสต์เก่ามาก แต่ฉันเพิ่งแก้ไขได้โดยการตัดคำสั่งที่จำเป็นในลองจับ

try {
     var fs = require('fs')
} catch(e) {
     alert('you are not in node !!!')
}

2
ไม่เป็นความจริงคุณสามารถใช้เบราว์เซอร์เพื่อใช้การโทรแบบ "nodeish" require ()
fat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.