Почему это странное поведение?

Я изменяю часть кода тремя способами. В этих трех условиях поведение ведет себя по-разному. Опишите, как это выполняется?

var a=1;
function myFunc(){
    console.log(a);
    console.log(a)
}
myFunc();
//Output is:
1 
1

var a=1;
function myFunc(){
    console.log(a);
    var a=2;
    console.log(a)
}
myFunc();
//Output is:
undefined
2

var a=1;
function myFunc(){
    console.log(a);
    var a=2;
    console.log(a)
}
myFunc(a);
//Output is:
undefined
2

Почему во втором случае он печатает undefined? И в 3-м случае я отправляю свой глобальный как аргумент, затем также печатаю undefined.

Ответ 1

Это потому, что JavaScript перемещает объявления var в начало области, поэтому ваш код на самом деле:

var a = 1;
function myFunc(){
    var a;            // a is redeclared, but no value is assigned
    console.log(a);   // therefore it evaluates to undefined
    a = 2;            // now a = 2
    console.log(a);   // and then it logs to 2
}
myFunc();

Это поведение называется Variable Подъем.

ИЗМЕНИТЬ Как сказал Beterraba, в третьем коде он записывает undefined, потому что в заголовке функции не было объявлено никаких аргументов:

var a = 1;
function myFunc(a) {    // a is declared
    console.log(a);     // now a logs 1
    var a = 2;          // now a = 2
    console.log(a);
}
myFunc(a);

Ответ 2

Второй случай - печать undefined из-за того, как работает контекст JavaScript Execution. Возможно, вы столкнулись с термином hoisting.

Чтобы объяснить это более подробно, при вызове второй функции интерпретатор войдет в процесс с двумя фазами.

Стадия создания

  • Создает цепочку областей действия
  • Создает аргументы, функции, переменные, так называемый объект переменной
  • Определяет значение "this" ключевое слово

Активация или этап выполнения кода

  • интерпретирует и выполняет код

Поэтому, когда вы вызываете myFunc(), интерпретатор JavaScript создает контекст выполнения, который вы можете рассматривать как литерал объекта, который выглядит так:

myFuncExecutionContext = {
   scopeChain: { ... },
   variableObject: {
      arguments: {
        length: 0
      },
     a: undefined,
  },
  this: { ... }
}

Вы можете видеть, что локальная переменная имеет начальное значение undefined. На этапе выполнения кода интерпретатор будет запускать функцию по строкам; Итак, первая строка, которую он видит, - console.log(a);. Интерпретатор будет смотреть на variableObject, чтобы увидеть, есть ли переменная с этим именем. Если нет, то он будет использовать цепочку областей видимости и попытается найти ее в переменном объекте внешних областей. Поскольку в объекте переменной есть переменная a, она будет читать ее значение, которое равно undefined.

Тогда это будет сделано в строке 2, где будет присваиваться значение локальной переменной a; var a=2;. Затем он выполнит последнюю строку - console.log(a) -, которая выведет значение, которое мы назначили ранее.

Тот же механизм оправдывает, почему мы можем вызывать функцию до ее определения, если мы используем синтаксис объявления функции.

someFunc(); // VALID
function someFunc(){  };

В то время как следующее приведет к ошибке:

someFunc(); // TypeError: undefined is not a function
var someFunc = function() {  }