38 Вопрос: var functionName = function () {} против функции functionName () {}

вопрос создан в Mon, Apr 15, 2019 12:00 AM

Недавно я начал поддерживать чужой код JavaScript. Я исправляю ошибки, добавляю функции, а также пытаюсь привести в порядок код и сделать его более согласованным.

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

Два способа:

 
var functionOne = function() {
    // Some code
};
 
function functionTwo() {
    // Some code
}

Каковы причины использования этих двух разных методов и каковы плюсы и минусы каждого? Можно ли что-то сделать одним методом, а другим - нет?

    
6485
  1. permadi.com/tutorial /jsFunc/index.html - очень хорошая страница о функциях JavaScript
    2010-04-09 11: 51: 59Z
  2. С этим связана превосходная статья на Выражения именованных функций .
    2011-04-21 21: 30: 10Z
  3. @ CMS ссылается на эту статью: 2011-08-03 14: 18: 43Z
  4. Вам необходимо знать о двух вещах: # 1 В JavaScript объявления поднимаются. Это означает, что var a = 1; var b = 2; становится var a; var b; a = 1; b = 2. Поэтому, когда вы объявляете functionOne, он объявляется, но его значение не устанавливается сразу. Принимая во внимание, что так как functionTwo является просто объявлением, он помещается в верхнюю часть области видимости. # 2 functionTwo позволяет получить доступ к свойству name, что очень помогает при попытке отладки чего-либо.
    2012-08-21 16: 20: 22Z
  5. Да, кстати, правильный синтаксис с ";" после назначения и без объявления после. Например. function f(){} против var f = function(){};.
    2012-08-21 16: 27: 11Z
30 ответов                              30                         

Разница заключается в том, что functionOne является выражением функции и поэтому определяется только при достижении этой строки, тогда как functionTwo является объявлением функции и определяется, как только исполняется окружающая его функция или скрипт (из-за подъем ).

Например, выражение функции:

р>

 
// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

И объявление функции:

р>

 
// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

Это также означает, что вы не можете условно определять функции, используя объявления функций:

 
if (test) {
   // Error or misbehavior
   function functionThree() { doSomething(); }
}

Выше фактически определяет functionThree независимо от значения test - если только не действует use strict, в этом случае просто возникает ошибка.

    
4805
2018-05-15 09: 03: 16Z
  1. @ Грег: Кстати, разница не только в том, что они анализируются в разное время. По сути, ваш functionOne - это просто переменная, которой назначена анонимная функция, тогда как functionTwo на самом деле является именованной функцией. Позвоните .toString() на обоих, чтобы увидеть разницу. Это важно в некоторых случаях, когда вы хотите получить имя функции программно.
    2011-08-05 21: 18: 09Z
  2. @ Jason Bunting .. не уверен, что вы здесь делаете, .toString (), по-видимому, возвращает по существу одно и то же значение (определение функции) для обоих: cl.ly/2a2C2Y1r0J451o0q0B1B
    2011-09-09 16: 29: 42Z
  3. Они оба разные. Первый - function expression, второй - function declaration. Вы можете прочитать больше по этой теме здесь: javascriptweblog. wordpress.com/2010/07/06/...
    2011-11-14 06: 03: 31Z
  4. @ Greg Часть вашего ответа относительно времени разбора и времени выполнения неверна. В JavaScript объявления функций определяются не во время разбора, а во время выполнения. Процесс выглядит следующим образом: исходный код анализируется - > Программа JavaScript проверяется - > Глобальный контекст выполнения инициализирован - > Декларация связывания выполняется. В ходе этого процесса создаются объявления функций (см. Шаг 5 в главе 10.5 ).
    2012-01-04 00: 59: 56Z
  5. Терминология для этого явления известна как подъем.
    2013-01-03 07: 22: 33Z

Сначала я хочу исправить. Грег: function abc(){} также ограничен - имя abc определено в области, где встречается это определение. Пример: р>  

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

Во-вторых, можно комбинировать оба стиля:

 
var xyz = function abc(){};

xyz будет определен как обычно, abc не определен во всех браузерах, кроме Internet Explorer - не полагайтесь на его определение. Но это будет определено внутри его тела:

 
var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

Если вы хотите использовать псевдонимы для всех браузеров, используйте такой тип объявления:

 
function abc(){};
var xyz = abc;

В этом случае и xyz, и abc являются псевдонимами одного и того же объекта:

 
console.log(xyz === abc); // prints "true"

Одной из веских причин для использования комбинированного стиля является атрибут «имя» функциональных объектов ( не поддерживается Internet Explorer ). В основном, когда вы определяете функцию, такую ​​как

 
function abc(){};
console.log(abc.name); // prints "abc"

его имя присваивается автоматически. Но когда вы определяете это как

 
var abc = function(){};
console.log(abc.name); // prints ""

его имя пустое - мы создали анонимную функцию и присвоили ее некоторой переменной.

Еще одна веская причина использовать комбинированный стиль - использовать короткое внутреннее имя для ссылки на себя, предоставляя длинное не конфликтующее имя для внешних пользователей:

 
// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

В приведенном выше примере мы можем сделать то же самое с внешним именем, но оно будет слишком громоздким (и медленным).

(Другой способ сослаться на себя - это использовать arguments.callee, который все еще относительно длинный и не поддерживается в строгом режиме.)

В глубине души JavaScript обрабатывает оба утверждения по-разному. Это объявление функции:

 
function abc(){}

abc здесь определяется везде в текущей области видимости:

 
// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Кроме того, он поднял оператор return:

 
// We can call it here
abc(); // Works
return;
function abc(){}

Это выражение функции:

 
var xyz = function(){};

xyz здесь определяется с точки назначения:

 
// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Объявление функции и выражение функции - реальная причина, по которой Грег демонстрирует разницу.

Забавный факт:

 
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

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

 
var abc = function(){};

Я знаю, что я определил функцию локально. Когда я определяю функцию как

 
abc = function(){};

Я знаю, что я определил это глобально, при условии, что я не определил abc нигде в цепочке областей. Этот стиль определения устойчив, даже когда используется внутри eval(). Хотя определение

 
function abc(){};

зависит от контекста и может угадать, где он на самом деле определен, особенно в случае с eval() - ответ таков: это зависит от браузера.

    
1880
2016-10-10 20: 38: 55Z
  1. Я ссылаюсь на Роборга, но его нигде не найти. Просто: Роборг === Грег. Вот как история может быть переписана в эпоху Интернета. ; -)
    2009-07-26 02: 52: 14Z
  2. var xyz = function abc () {}; console.log (xyz === abc); Все браузеры, которые я тестировал (Safari 4, Firefox 3.5.5, Opera 10.10), выдают «Неопределенная переменная: abc».
2009-12-03 17: 43: 04Z
  • В целом, я думаю, что этот пост хорошо объясняет различия и преимущества использования объявления функции. Я согласен не согласиться с тем, насколько выгодно использование назначения выражений функций для переменной, тем более что «выгода», по-видимому, является пропагандой объявления глобальной сущности ... и все знают, что вы не должны загромождать глобальное пространство имен , право? ; -)
    2013-10-08 16: 30: 28Z
  • Я считаю, что огромная причина для использования именованной функции заключается в том, что отладчики могут использовать имя, чтобы помочь вам разобраться в вашем стеке вызовов или трассировке стека. это отстой, когда вы смотрите на стек вызовов и видите «анонимную функцию» глубиной 10 уровней ...
    2014-01-26 18: 25: 18Z
  • var abc = function(){}; console.log(abc.name); больше не производит "", а вместо этого "abc".
    2018-05-04 12: 49: 49Z
  • Вот краткое изложение стандартных форм, которые создают функции: (Первоначально написано для другого вопроса, но адаптировано после перемещения в канонический вопрос.)

    Условия:

    Быстрый список:

    • Объявление функции

    • "Anonymous" function Выражение (которое, несмотря на термин, иногда создает функции с именами)

    • Именное выражение function

    • Инициализатор функций доступа (ES5 +)

    • Выражение функции стрелки (ES2015 +) (которое, как и выражения анонимной функции, не содержит явного имени и может создавать функции с именами)

    • Объявление метода в инициализаторе объектов (ES2015 +)

    • Объявления конструктора и метода в class (ES2015 +)

    Объявление функции

    Первая форма - это объявление функции , которая выглядит следующим образом:

     
    function x() {
        console.log('x');
    }
    

    Объявление функции - это объявление ; это не утверждение или выражение. Таким образом, вы не следуете за ним с ; (хотя это безвредно).

    Объявление функции обрабатывается, когда исполнение входит в контекст, в котором оно появляется, до выполнения любого пошагового кода. Создаваемой ей функции присваивается собственное имя (x в приведенном выше примере), и это имя помещается в область, в которой появляется объявление.

    Поскольку он обрабатывается перед любым пошаговым кодом в том же контексте, вы можете сделать что-то вроде этого:

     
    x(); // Works even though it's above the declaration
    function x() {
        console.log('x');
    }
    

    До ES2015 спецификация не охватывала то, что должен делать движок JavaScript, если вы поместили объявление функции внутри структуры управления, такой как try, if, switch, while и т. д., например так:

     
    if (someCondition) {
        function foo() {    // <===== HERE THERE
        }                   // <===== BE DRAGONS
    }
    

    И поскольку они обрабатываются до того, как запускается пошаговый код, очень сложно знать, что делать, когда они находятся в управляющей структуре.

    Хотя до ES2015 этого не было указано , это было допустимое расширение для поддержки объявлений функций в блоках. К сожалению (и неизбежно), разные движки делали разные вещи.

    Начиная с ES2015, в спецификации сказано, что делать. Фактически, это дает три отдельных действия:

    1. Если в свободном режиме нет в веб-браузере, механизм JavaScript должен делать одну вещь
    2. Если в свободном режиме в веб-браузере движок JavaScript должен делать что-то еще
    3. Если в режиме строгий (браузер или нет), движок JavaScript должен делать еще одну вещь

    Правила для свободных режимов хитры, но в режиме строгого объявления функций в блоках просты: они локальны для блока (у них есть область видимости блока , который также является новым в ES2015), и они подняты к вершине блока. Итак:

     
    "use strict";
    if (someCondition) {
        foo();               // Works just fine
        function foo() {
        }
    }
    console.log(typeof foo); // "undefined" (`foo` is not in scope here
                             // because it's not in the same block)
    

    Выражение «Anonymous» function

    Вторая распространенная формавызвал выражение анонимной функции :

     
    var y = function () {
        console.log('y');
    };
    

    Как и все выражения, оно оценивается, когда достигается при пошаговом выполнении кода.

    В ES5 созданная функция не имеет имени (она анонимна). В ES2015, функции по возможности присваивается имя, выводя его из контекста. В приведенном выше примере имя будет y. Нечто подобное происходит, когда функция является значением инициализатора свойства. (Подробную информацию о том, когда это происходит и правилах, ищите SetFunctionName в спецификации - она ​​появляется по всему месту.)

    Именное выражение function

    Третья форма - это выражение с именованной функцией ("NFE"):

     
    var z = function w() {
        console.log('zw')
    };
    

    Функция, которую она создает, имеет правильное имя (в данном случае w). Как и все выражения, это оценивается, когда оно достигается при пошаговом выполнении кода. Имя функции не добавлено в область, в которой появляется выражение; имя находится в области действия самой функции:

     
    var z = function w() {
        console.log(typeof w); // "function"
    };
    console.log(typeof w);     // "undefined"
    

    Обратите внимание, что NFE часто являются источником ошибок для реализаций JavaScript. Например, IE8 и более ранние версии обрабатывают NFE совершенно неправильно , создавая два разных функционирует в два разных времени. Ранние версии Safari также имели проблемы. Хорошей новостью является то, что в текущих версиях браузеров (IE9 и выше, текущий Safari) таких проблем больше нет. (К сожалению, на момент написания этой статьи IE8 все еще широко используется, и поэтому использование NFE с кодом для Интернета в целом все еще проблематично.)

    Инициализатор функции доступа (ES5 +)

    Иногда функции могут проникнуть в основном незамеченными; это относится к функциям доступа . Вот пример:

     
    var obj = {
        value: 0,
        get f() {
            return this.value;
        },
        set f(v) {
            this.value = v;
        }
    };
    console.log(obj.f);         // 0
    console.log(typeof obj.f);  // "number"
    

    Обратите внимание, что когда я использовал эту функцию, я не использовал ()! Это потому, что это функция доступа для свойства. Мы получаем и устанавливаем свойство обычным способом, но за кулисами вызывается функция.

    Вы также можете создавать функции доступа с Object.defineProperty, Object.defineProperties и менее известным вторым аргументом с Object.create.

    Выражение функции стрелки (ES2015 +)

    ES2015 приносит нам функцию стрелки . Вот один пример:

     
    var a = [1, 2, 3];
    var b = a.map(n => n * 2);
    console.log(b.join(", ")); // 2, 4, 6
    

    Видишь эту вещь n => n * 2, скрывающуюся в звонке map()? Это функция.

    Несколько вещей о функциях стрелок:

    1. У них нет своих this. Вместо этого они закрывают this контекста, в котором они определены. (Они также закрывают arguments и, при необходимости, super.) Это означает, что this внутри них совпадает с this, в котором они были созданы, и не может быть изменено.

    2. Как вы уже заметили, вы не используете ключевое слово function; вместо этого вы используете =>.

    Приведенный выше пример n => n * 2 является одной из них. Если у вас есть несколько аргументов для передачи функции, вы используете parens:

     
    var a = [1, 2, 3];
    var b = a.map((n, i) => n * i);
    console.log(b.join(", ")); // 0, 2, 6
    

    (Помните, что Array#map передает запись в качестве первого аргумента и индекс в качестве второго.)

    В обоих случаях тело функции является просто выражением; возвращаемое значение функции автоматически будет результатом этого выражения (вы не используете явный return).

    Если вы делаете больше, чем просто одно выражение, используйте {} и явное return (если вам нужно вернуть значение), как обычно:

     
    var a = [
      {first: "Joe", last: "Bloggs"},
      {first: "Albert", last: "Bloggs"},
      {first: "Mary", last: "Albright"}
    ];
    a = a.sort((a, b) => {
      var rv = a.last.localeCompare(b.last);
      if (rv === 0) {
        rv = a.first.localeCompare(b.first);
      }
      return rv;
    });
    console.log(JSON.stringify(a));
    

    Версия без { ... } называется функцией стрелки с телом выражения или кратким телом . (Также: функция стрелки краткая .) Функция с телом { ... }, определяющим тело, является функцией стрелки с телом функции . (Также: функция многословная стрелка.)

    Объявление метода в инициализаторе объекта (ES2015 +)

    ES2015 допускает более короткую форму объявления свойства, которое ссылается на функцию, называемую определение метода ; это выглядит так:

     
    var o = {
        foo() {
        }
    };
    

    почти эквивалентный в ES5 и более ранних версиях будет:

     
    var o = {
        foo: function foo() {
        }
    };
    

    Разница (кроме многословия) в том, что метод может использовать super, а функция - нет. Так, например, если бы у вас был объект, который определил (скажем) valueOf с использованием синтаксиса метода, он мог бы использовать super.valueOf() для получения значения Object.prototype.valueOf, которое должно быть возвращено (прежде чем предположительно делать что-то еще с ним), тогда как версия ES5 должна была бы сделать Object.prototype.valueOf.call(this) вместо этого.

    Это также означает, что метод имеет ссылку на объект, для которого он был определен, поэтому, если этот объект является временным (например, вы передаете его в Object.assign как один из исходных объектов), синтаксис метода может означать, что объект сохраняется в памяти, в противном случае он мог бы быть подвергнут сборке мусора (если механизм JavaScript не обнаруживает эту ситуацию и обрабатывает ее, если ее нет)из методов использует super).

    Объявления конструктора и метода в class (ES2015 +)

    ES2015 предоставляет нам синтаксис class, включая объявленные конструкторы и методы:

     
    class Person {
        constructor(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        getFullName() {
            return this.firstName + " " + this.lastName;
        }
    }
    

    Выше приведены два объявления функций: одно для конструктора, который получает имя Person, и одно для getFullName, которое является функцией, назначенной Person.prototype.

        
    593
    2019-01-11 13: 58: 57Z
    1. тогда имя w просто игнорируется?
      2014-03-04 13: 37: 32Z
    2. @ PellePenna: имена функций полезны для многих вещей. На мой взгляд, двумя важными элементами являются рекурсия и имя функции, отображаемой в стеках вызовов, трассировках исключений и т. Д.
      2014-03-04 13: 42: 57Z
    3. @ ChaimEliyah - «Принятие не означает, что это лучший ответ, это просто означает, что это сработало для человека, который спросил». источник
      2016-02-10 10: 19: 34Z
    4. @ A.R .: Совершенно верно. Забавно, однако, прямо над ним написано: «Лучшие ответы появляются первыми, чтобы их всегда было легко найти». Поскольку принятый ответ появляется первым даже над ответами с более высоким рейтингом, тур может быть несколько противоречивым. ;-) Также немного неточно, если мы определяем «лучший» по голосам (что ненадежно, это только то, что у нас есть), «лучшие» ответы отображаются первыми, только если вы используете вкладку «Голоса» - иначе ответы, которые являются первыми, являются активными или самыми старыми.
      2016-02-10 10: 32: 23Z
    5. @ T.J.Crowder: согласовано. «упорядочено по дате» иногда раздражает.
      2016-02-10 10: 46: 56Z

    Говоря о глобальном контексте, оператор var и FunctionDeclaration в конце создадут свойство не удаляемое для глобального объекта, но значение обоих может быть перезаписано .

    Тонкое различие между двумя способами заключается в том, что когда создание переменных Процесс a> запускается (до фактического выполнения кода), все идентификаторы, объявленные с var, будут инициализированы с undefined, а те, которые используются FunctionDeclaration, будут доступны с того момента, например:

     
     alert(typeof foo); // 'function', it's already available
     alert(typeof bar); // 'undefined'
     function foo () {}
     var bar = function () {};
     alert(typeof bar); // 'function'
    

    Назначение bar FunctionExpression происходит до времени выполнения.

    Глобальное свойство, созданное FunctionDeclaration, может быть без проблем перезаписано, как и значение переменной, например:

     
     function test () {}
     test = null;
    

    Другое очевидное различие между вашими двумя примерами состоит в том, что у первой функции нет имени, но у второго оно есть, что может быть очень полезно при отладке (то есть проверке стека вызовов).

    Что касается вашего отредактированного первого примера (foo = function() { alert('hello!'); };), это незадекларированное назначение, я настоятельно рекомендую вам всегда использовать ключевое слово var.

    Если в присвоении, без оператора var, указанный идентификатор не найден в цепочке областей действия, он станет свойством deleteable глобального объекта.

    Кроме того, незадекларированные назначения выдают ReferenceError в ECMAScript 5 в строгий режим а>. р>

    А должен читать:

    Примечание . Этот ответ был объединен с другой вопрос , в котором основное сомнение и неправильное представление со стороны OP было то, что идентификаторы, объявленные с FunctionDeclaration, не могут быть перезаписаны, что не так. р>     

    138
    2017-05-23 12: 10: 54Z
    1. Я не знал, что функции могут быть перезаписаны в JavaScript! Кроме того, этот порядок разбора является для меня большой точкой продажи. Думаю, мне нужно посмотреть, как я создаю функции.
      2010-08-08 19: 43: 48Z
    2. + 0 к статье "Расчеркивание выражений функций имен", так как это 404ing. Возможно зеркало ?: kangax.github.com/nfe
      2011-11-29 15: 10: 16Z
    3. @ CMS Хороший. Имейте в виду, что я никогда не видел оригинал, поэтому я не знаю, зеркало это или просто другая статья с таким же названием!
      2011-11-29 15: 25: 01Z
    4. @ Mr_Chimp Я уверен, что это так, thewaybackmachine говорит, что он получил 302 во время сканирования и перенаправление было на указанную вами ссылку.
      2012-01-30 15: 18: 46Z

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

    Однако разница в поведении заключается в том, что в первом варианте (var functionOne = function() {}) эту функцию можно вызывать только после этой точки в коде.

    Во втором варианте (function functionTwo()) функция доступна для кода, который выполняется выше, где функция объявлена.

    Это связано с тем, что в первом варианте функция назначается переменной foo во время выполнения. Во втором случае функция назначается этому идентификатору, foo, во время разбора.

    Дополнительная техническая информация

    JavaScript имеет три способа определения функций.

    1. Ваш первый фрагмент показывает функциональное выражение . Это включает использование оператора "function" для создания функции - результат этого оператора может быть сохранен в любой переменной или свойстве объекта. Таким образом, выражение функции является мощным. Выражение функции часто называют «анонимной функцией», потому что оно не обязательно должно иметь имя,
    2. Ваш второй пример - объявление функции . При этом используется оператор "function" для создания функции. Функция становится доступной во время анализа и может вызываться в любом месте этой области. Вы все еще можете сохранить его в переменной или свойстве объекта позже.
    3. Третий способ определения функции - это конструктор "Function ()" , который не отображается в исходном сообщении. Не рекомендуется использовать это, так как он работает так же, как eval(), у которого есть свои проблемы.
    118
    2015-12-28 19: 47: 08Z

    Более подробное объяснение ответа Грега

     
    functionTwo();
    function functionTwo() {
    }
    

    Почему нет ошибок? Нас всегда учили, что выражения выполняются сверху вниз (??)

    Потому что:

      

    Объявления функций и объявления переменных всегда незаметно перемещаются (hoisted) в верхнюю часть их содержащей области действия интерпретатором JavaScript. Параметры функций и определяемые языком имена, очевидно, уже есть. Бен вишня

    Это означает, что код такой:

     
    functionOne();                  ---------------      var functionOne;
                                    | is actually |      functionOne();
    var functionOne = function(){   | interpreted |-->
    };                              |    like     |      functionOne = function(){
                                    ---------------      };
    

    Обратите внимание, что часть объявлений объявлений не была поднята. Поднимается только имя.

    Но в случае с объявлениями функций все тело функции также будет поднято :

     
    functionTwo();              ---------------      function functionTwo() {
                                | is actually |      };
    function functionTwo() {    | interpreted |-->
    }                           |    like     |      functionTwo();
                                ---------------
    
        
    98
    2017-05-23 12: 26: 42Z
    1. HI suhail спасибо за ясную информацию о теме функции. Теперь мой вопрос: какое из них будет первым объявлением в иерархии объявлений: объявлением переменной (functionOne) или объявлением функции (functionTwo)?
      2016-02-02 12: 09: 06Z

    Другие комментаторы уже рассмотрели семантическое различие двух вариантов выше. Я хотел бы отметить стилистическую разницу: только вариант «назначение» может установить свойство другого объекта.

    Я часто строю модули JavaScript с таким шаблоном:

     
    (function(){
        var exports = {};
    
        function privateUtil() {
                ...
        }
    
        exports.publicUtil = function() {
                ...
        };
    
        return exports;
    })();
    

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

    (Обратите внимание также, что для присваивания требуется точка с запятой после оператора, в то время как объявление запрещает это.)

        
    88
    2014-10-19 22: 24: 30Z
    1. yuiblog. Насколько я могу судить, com /blog /2007/06/12 /module-pattern - это исходная ссылка на шаблон модуля. (Хотя эта статья использует синтаксис var foo = function(){...} даже для частных переменных.
      2011-06-03 12: 32: 22Z
    2. На самом деле это не совсем так в некоторых старых версиях IE. (function window.onload() {} была вещь.)
      2013-04-21 19: 42: 51Z
    источник размещен Вот