Javascript typeof выдает ошибку referenceerror

давайте перейдем к делу.
Я использую typeof, чтобы проверить, существует ли API переменных. Я узнал, что typeof возвращает "undefined", если переменная не существует.

function exitApplication() {
    let typeOfApi = typeof API;
    if (typeOfApi !== "undefined") {
        API.close(() => {
            strip.shutdown();
            ...
            ...
            ...
        });
    }
    else {
        console.log("Bye!");
        process.exit(0);
    }
}

Если я сейчас запускаю свою программу с тестовыми данными, которые приводят к вызову exitApplication, когда API еще не определен, я получаю ReferenceError:

    let typeOfApi = typeof API;
                    ^

ReferenceError: API is not defined

Поскольку я использую Webpack, я изменил выходной файл и заменил API чем-нибудь еще, что не было определено, и вуаля, оно работает, а typeOfApi - «undefined» (вставленный мной код - это выход Webpack).

API - это константное значение, и я использую в своем коде только let и const. Я что-то читал о временных мертвых зонах, но typeof все равно должен возвращать undefined, если переменная let не определена?

Я также читал этот Почему typeof только иногда выдает ReferenceError? но я я не использую выражения.

О, и мой код написан на машинописном тексте. Но я не настолько "хорош" в этом и действительно не знаю, как получить типы для restify, поэтому API имеет тип any. (Я знаю, что typeof и typecript - совершенно разные вещи: D). Но в любом случае код, похоже, переведен 1 к 1.


Изменить: Итак, я сделал этот небольшой пример. Это вывод Webpack

#!/usr/local/bin/node
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
/******/
/******/    // define __esModule on exports
/******/    __webpack_require__.r = function(exports) {
/******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/        }
/******/        Object.defineProperty(exports, '__esModule', { value: true });
/******/    };
/******/
/******/    // create a fake namespace object
/******/    // mode & 1: value is a module id, require it
/******/    // mode & 2: merge all properties of value into the ns
/******/    // mode & 4: return value when already ns object
/******/    // mode & 8|1: behave like require
/******/    __webpack_require__.t = function(value, mode) {
/******/        if(mode & 1) value = __webpack_require__(value);
/******/        if(mode & 8) return value;
/******/        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/        var ns = Object.create(null);
/******/        __webpack_require__.r(ns);
/******/        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/        return ns;
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = "./src/test.ts");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./src/test.ts":
/*!*********************!*\
  !*** ./src/test.ts ***!
  \*********************/
/*! no static exports found */
/***/ (function(module, exports) {

exitApplication();
const API = {};
function exitApplication() {
    let typeOfApi = typeof API;
    console.log(typeOfApi);
    if (typeOfApi !== "undefined") {
        console.log("Bye!");
        process.exit(0);
    }
    else {
        console.log("Bye!");
        process.exit(0);
    }
}


/***/ })

/******/ });
//# sourceMappingURL=LED-Controller.js.map

И это также вызовет ошибку ссылки

Изменить: вот мои конфигурации TS и Webpack. https://gist.github.com/Lucarus/ebbfab5cc6560094a292ba86557ffd1d
Для примера I заменил Applications.ts на test.ts, но использовал ту же конфигурацию.


person Lukas    schedule 20.02.2020    source источник
comment
Вы пробовали только if(API){...}?   -  person Scott Marcus    schedule 20.02.2020
comment
можете показать, где вы определили API? Также почему вы проверяете тип API, если он есть?   -  person Sunil Lama    schedule 20.02.2020
comment
@ScottMarcus API не определен, поэтому возникает ошибка ссылки   -  person Lukas    schedule 20.02.2020
comment
Я понятия не имею, почему это происходит. Я пытался воспроизвести проблему, но не смог. Может быть, попробуйте удалить кусочки кода, пока не найдете минимально воспроизводимый фрагмент? Но раньше я проверял наличие глобальных переменных с помощью функции try / catch: const apiExists = () => { try { return !!API } catch(e) { return false } }   -  person suddjian    schedule 20.02.2020
comment
Если API не определен, то почему вы думаете, что можете получить его тип?   -  person Scott Marcus    schedule 20.02.2020
comment
Или вы можете использовать if (window.API), если это должно работать только в браузере.   -  person suddjian    schedule 20.02.2020
comment
@ScottMarcus прочитал мой вопрос. typeof вернет undefined, если вы передадите ему переменную, которая не была создана. Посмотрите документацию по типу;)   -  person Lukas    schedule 20.02.2020
comment
@suddjian похоже, что мне придется это использовать. Я убрал ВСЕ и не работает ^^.   -  person Lukas    schedule 20.02.2020
comment
Можете ли вы поделиться своим машинописным текстом и конфигурацией веб-пакета?   -  person suddjian    schedule 20.02.2020
comment
@suddjian Я добавил это в свой вопрос. Но согласны ли вы, что typeof не должен возвращать ReferenceError? и должен скорее возвращать undefined. Я не уверен, имеет ли это какое-то отношение к es6, и способ let более специфичен, чем var.   -  person Lukas    schedule 20.02.2020
comment
Я понимаю, как typeof работает. Я спрашиваю вас, почему, по вашему мнению, вы получите действительный тип от API, если вы его никогда не объявляли?   -  person Scott Marcus    schedule 20.02.2020
comment
@ScottMarcus, дело в том, что я не знаю, объявлено ли это еще. Я все равно реструктурирую свой код. Но в зависимости от того, как работает typeof, он должен возвращать undefined, попробуйте его в интерпретаторе узла, просто введите typeof thisIsNotDeclared, и он вернет строку undefined. Но, поскольку кажется, что работают es6 и let / const, интерпретатор знает, что API должен быть, но его нет, и поэтому выдает ошибку, но если интерпретатор знает, что переменная не существует, он вернет undefined. (Пока не уверен на 100%. Но я думаю, что это как-то связано с временной мертвой зоной)   -  person Lukas    schedule 20.02.2020
comment
@ScottMarcus ага, я обнаружил, что es-discourse.com / t / why-typeof-is-not-more-safe / 15 и что medium.com/javascript-scene/ typeof работает только с var   -  person Lukas    schedule 20.02.2020
comment
Это абсолютно временная мертвая зона.   -  person Scott Marcus    schedule 20.02.2020


Ответы (1)


Вы вызываете функцию, которая ссылается на переменную const API = {} ДО того, как эта переменная была инициализирована, но внутри области, в которой она будет объявлена. С const и let это недопустимо. У вас есть это:

exitApplication();
const API = {};
function exitApplication() {
    let typeOfApi = typeof API;
    console.log(typeOfApi);
    if (typeOfApi !== "undefined") {
        console.log("Bye!");
        process.exit(0);
    }
    else {
        console.log("Bye!");
        process.exit(0);
    }
}

Функция поднимается в верхнюю часть этой области видимости, поэтому вы можете вызвать exitApplication(), но вы еще не выполнили строку кода, которая инициализирует API. Но интпредставитель знает, что он там и еще не инициализирован, и это ReferenceError в Javascript, чтобы попытаться получить доступ к определенной const или let переменной в области, где она определена, до того, как будет запущена строка, содержащая ее объявление.

Когда я запускаю это в Chrome, я получаю точную ошибку:

Uncaught ReferenceError: Cannot access 'API' before initialization

который точно сообщает вам, в чем проблема. На первом проходе интерпретатора он проанализировал код и знает, что const API = {} существует, поэтому доступ к нему незаконен, пока он не достигнет той строки кода, которая его инициализирует. Если вы действительно хотите обойти это, измените const на var, но, вероятно, есть лучший способ написать код, в котором не нужно использовать var.


Конечно, если вы просто переместите объявление API на одну строку вверх, никаких проблем:

const API = {};
exitApplication();
function exitApplication() {
    let typeOfApi = typeof API;
    console.log(typeOfApi);
    if (typeOfApi !== "undefined") {
        console.log("Bye!");
        process.exit(0);
    }
    else {
        console.log("Bye!");
        process.exit(0);
    }
}

Чтобы получить хорошую статью по этой теме, вы можете прочитать эту статью: Почему typeof больше не безопасен.

person jfriend00    schedule 20.02.2020
comment
Да, я понимаю, но разве не нужно это понимать? Я знаю, что мне пришлось обмануть машинописный линтер, чтобы я мог даже скомпилировать его в js. Но дело в том, что если я открою узел в режиме интерпретации, где я могу ввести код, он интерпретирует одну строку за другой (например, просто запустив команду узла в терминале ^^). Я могу ввести typeof foo, и он вернет undefined. А также, если я изменю API переменных на что-нибудь еще, например foobar в моем коде, это тоже будет работать. var работает, потому что он создает каждую переменную, объявленную с помощью var, даже если интерпретатор еще не достиг объявления для нее. - person Lukas; 20.02.2020
comment
Хорошо, я думаю, что теперь понял, вы имеете в виду, потому что интерпретатор знает, что будет переменная API, он выдаст ошибку ссылки? И если я хочу сослаться на переменную, которая не существует, а интерпретатор не знает о ее существовании, он вернет undefined? Связано ли это с временными мертвыми зонами и как let обрабатывается иначе, чем var? И как бы вы проверили наличие API, я мог бы использовать try catch или переделать свой код так, чтобы он не вызывался до определения API. Спасибо ;) - person Lukas; 20.02.2020
comment
Не могли бы вы добавить es-discourse.com/t/why -typeof-is-not-more-safe / 15 на ваш ответ? это сделало для меня более ясным ^^ - person Lukas; 20.02.2020
comment
@Lukas - Добавил к моему ответу. - person jfriend00; 21.02.2020