Самовиконання анонімної функції проти прототипу


26

У Javascript є кілька чітко видатних методів створення та управління просторами класів / імен у javascript.

Мені цікаво, які ситуації вимагають використання однієї техніки проти іншої. Я хочу вибрати один і дотримуватися його, рухаючись вперед.

Я пишу код підприємства, який підтримується та ділиться різними командами, і хочу знати, яка найкраща практика при написанні постійного JavaScript?

Я, як правило, віддаю перевагу самостійному виконанню анонімних функцій, проте мені цікаво, що голосування громади стосується цих методів.

Прототип:

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Анонімна функція самозакриття:

//Self-Executing Anonymous Function 
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }     
}( window.skillet = window.skillet || {}, jQuery ));   
//Public Properties      
console.log( skillet.ingredient ); //Bacon Strips  

//Public Methods 
skillet.fry(); //Adding Butter & Fraying Bacon Strips 

//Adding a Public Property 
skillet.quantity = "12"; console.log( skillet.quantity ); //12   

//Adding New Functionality to the Skillet 
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
     };     

}( window.skillet = window.skillet || {}, jQuery ));
//end of skillet definition


try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception 
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}

Я вважаю, що слід зазначити, що анонімна функція самовиконання - це шаблон, який використовується командою jQuery.

Оновлення Коли я задав це запитання, я по-справжньому не бачив важливості того, що я намагався зрозуміти. Справжня проблема полягає в тому, чи слід використовувати нові для створення примірників ваших об'єктів чи використовувати шаблони, які не потребують конструкторів / використання newключового слова.

Я додав власну відповідь, тому що, на мою думку, ми повинні використовувати шаблони, в яких не використовується newключове слово.

Для отримання додаткової інформації дивіться мою відповідь.


1
чи можете ви навести короткі приклади двох методів, які ви описуєте?
Гей,

Не варто недооцінювати прототип через мій простий зразок.
Робоцуші

1
це не виконує себе = /
Ей,

2
Я не бачу жодних дужок, щоб закрити вираз або назвати його ...
Привіт

1
(+1) Багато розробників не помічають просторів імен.
umlcat

Відповіді:


22

Самостійне виконання анонімних функцій використовується для автоматизації виконання сценарію без підключення до зовнішніх подій (тобто window.onload).

У цьому прикладі він використовується для формування класичного шаблону модуля, основною метою якого є введення простору імен у глобальне середовище, та надання інкапсуляції для будь-яких внутрішніх властивостей, які не "експортуються" або не приєднуються до простору імен.

З іншого боку, модифікація прототипу об'єктів використовується для встановлення спадкування (або розширення тубільців). Ця схема використовується для отримання об'єктів 1: n із загальними методами чи властивостями.

Не слід вибирати один візерунок, віддаючи перевагу іншому, оскільки вони виконують різні завдання. Що стосується простору імен, функція самовиконання - це відповідний вибір.


7
Зауважте, що "самовиконання анонімних функцій" зазвичай називають виразами функцій, що негайно викликаються (IIFE) .
voithos

Я уникаю їх, і якщо чесно, я не отримую любові до IIFE. Вони безладно налагоджувати та порушувати контур коду в Eclipse. Якщо вам потрібен простір імен, наклейте його на об’єкт, якщо вам потрібно його виконати, просто зателефонуйте, і інкапсуляція насправді нічого не отримує від мене.
Даніель Соколовський

4

Ось шаблон, який я тільки почав використовувати (використовував його варіанти до вчорашнього дня):

function MyClass() {
    // attributes
    var privateVar = null;

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

MyClass.prototype = new ParentClass(); // if required

Кілька думок з цього приводу:

  1. У (anonymous)вашому стеку налагоджувального стеку не повинно бути ніяких слідів, як все названо (немає анонімних функцій).
  2. Це найчистіша картина, яку я ще бачив
  3. Ви можете легко згрупувати відкритий API, не маючи їх реалізацій, з'єднаних із декларацією (це означає, що хтось може легко переробляти ваш інтерфейс публічного класу без прокрутки)

Єдиний час, який я б prototypeбільше не використовував - це визначити спадщину.


5
З цим є кілька проблем. Для кожного "методу" створюється новий об'єкт функції з кожним викликом конструктора. Крім того, викликає конструктор для отримання копії прототипу об'єкта для успадкування. Використовуйте Object.create (ParentClass.prototype), або штрих для Object.create якfunction clone(obj){return this typeof 'clone' ? this : new clone(clone.prototype=obj)}
Привіт

@GGG: Так, ти маєш рацію зі своїм першим моментом. Я б сказав (і повинен був згадати у своєму дописі), що кожен конкретний випадок використання реалізації повинен бути продуманий. Моя проблема із prototypeзапропонованим вами методом (якщо не знайомий мені метод, що може бути), ви втрачаєте можливість інкапсулювати атрибути, тобто все відкрито як публічне (що не є кінцем світу , просто особисті переваги).
Дем'ян Брехт

Крім того, переглянувши таку роботу, виконану на jsperf ( jsperf.com/object-create-vs-constructor-vs-object-literal/12 ), я міг би підвищити продуктивність у порівнянні з вартістю пам'яті додаткових копій майже будь-який день (знову ж, дуже суб’єктивний для конкретного випадку використання).
Дем'ян Брехт

Тепер, сказавши все це, я проходжу лише ECMA-262, тому може бути маса речей, яких я не бачу .. Також я не сприймаю слово Крокфорда як євангелію. Так, він один експертів у цій галузі (один із головних, звичайно), але це не завжди робить його на 100% правильним у будь-який час. Є інші експерти (я не один з них;)), які мають суперечливі думки з переконливими аргументами.
Дем'ян Брехт

2
вас може зацікавити ця річ, над якою я працюю .
Гей,

3

Я використовую прототипи, оскільки вони чистіші та дотримуються стандартних схем успадкування. Функції самовикликання чудово підходять для розробки браузера або ситуації, коли ви не знаєте, де виконується код, але в іншому випадку це просто шум.

Приклад:

var me;

function MyObject () {
    this.name = "Something";
}

MyObject.prototype.speak = function speak () {
    return "Hello, my name is " + this.name;
};

me = new MyObject();
me.name = "Joshua";
alert(me.speak());

1
Самостійне виконання анонімних функцій дуже корисно, оскільки дозволяє мати приватні функції, доступні класу.
Zee

1

Я б пішов з функцією самовиконання, але з невеликою різницею:

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

     return function MyClass() {
         this.methodOne = methodOne;
         this.methodTwo = methodTwo;
         this.publicProperty = publicProperty;
     };
})();

Якщо я вважаю цей підхід набагато чистішим, тому що я відокремлюю повернуту глобальну змінну від будь-яких вхідних параметрів (наприклад, jQuery) (спосіб, який ви це написали, еквівалентний поверненню недійсності та використанню параметра ref в C #, який я вважаю трохи відключеним, або перехід вказівника до вказівника та повторне призначення його в C ++). Якби я тоді збирався приєднати додаткові методи або властивості до класу, я б використав прототипічне успадкування (наприклад, метод $ .extend методу jQuery, але досить просто прокрутити власний розширення ()):

var additionalClassMethods = (function () {
    var additionalMethod = function () { alert('Test Method'); };
    return { additionalMethod: additionalMethod };
})();

$.extend(MyClass.prototype, additionalClassMethods);

var m = new MyClass();
m.additionalMethod(); // Pops out "Test Method"

Таким чином ви маєте чітке розмежування між доданими методами та оригінальними.


1
Я єдиний, хто думає, що використовувати NFE так, як це погана ідея?
Гей,

1

Живий приклад

(function _anonymouswrapper(undefined) {

    var Skillet = {
        constructor: function (options) {
            options && extend(this, options);
            return this; 
        },
        ingredient: "Bacon Strips",
        _isHot: true,
        fry: function fry(oliveOil) {
            this._addItem("\t\n Butter \n\t");
            this._addItem(oliveOil);
            this._addItem(this.ingredient);
            console.log("Frying " + this.ingredient);
        },
        _addItem: function addItem(item) {
            console.log("Adding " + item.toString().trim());
        }
    };

    var skillet = Object.create(Skillet).constructor();

    console.log(skillet.ingredient);
    skillet.fry("olive oil");

    var PrintableSkillet = extend(Object.create(Skillet), {
        constructor: function constructor(options) {
            options && extend(this, options);
            return this;
        },
        _amountOfGrease: "1 Cup",
        quantity: 12,
        toString: function toString() {
            console.log(this.quantity + " " +
                        this.ingredient + " & " +
                        this._amountOfGrease + " of Grease");
            console.log(this._isHot ? "Hot" : "Cold");
        }
    });

    var skillet = Object.create(PrintableSkillet).constructor();

    skillet.toString();

    function extend(target, source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            var pd = Object.getOwnPropertyDescriptor(source, name);
            Object.defineProperty(target, name, pd);
        });
        return target;
    }
}());

Ви можете використовувати IIFE для емуляції "області модуля" навколо свого коду. Тоді ви можете просто використовувати об'єкти, як зазвичай.

Не «емулюйте» приватний стан, використовуючи закриття, оскільки це велике покарання за пам'ять.

Якщо ви пишете корпоративну заявку і хочете зберегти обсяг пам’яті до 1 Гб, уникайте зайвого використання закривань для зберігання стану.


У вас є кілька помилок друку в кодовому партнері. Я не впевнений, що ваше твердження про закриття автоматично спричиняє надмірне використання пам'яті, я думаю, це залежить від того, наскільки сильно ви їх використовуєте, і від того, наскільки добре ви вирішуєте проблеми, пов’язані з визначенням (змішування речей із закриття з речами ззовні, як правило, погано, наприклад (ваше використання глобальної консолі - хороший приклад цього, наведений вище код був би набагато ефективнішим, якби ви передавали в консоль як змінну)).
Ед Джеймс

@EdWoodcock Я зрозумів, що код баггі, просто переробив його та виправив.
Райнос

@EdWoodcock Різниця в ефективності між локальним consoleта глобальним console- це мікрооптимізація. Варто зробити так, щоб ви могли мінімізувати, consoleале це інша справа
Райнос

Закриття @EdWoodcock відбувається повільно, якщо ваші дублюючі об’єкти в них. Що повільно, це створення функцій всередині функцій, коли це не потрібно. у закриття також є невеликі накладні витрати для зберігання стану порівняно зі зберіганням стану на об’єктах безпосередньо
Райнос

Так, я просто хотів це зазначити, оскільки ваша відповідь - це розумний спосіб вести справи (хоч і не так, як я вирішив би використовувати). (Я б зробив редагування, але я все ще не впевнений в етикеті тих, хто знаходиться на цьому веб-сайті SE).
Ед Джеймс

0

Оновлення Зараз я набагато краще розумію JavaScript і вважаю, що я можу правильно вирішити це питання. Я думаю, що це була погано сформульована, але дуже важлива тема JavaScript.

Шаблон анонімної функції самовиконання - це не той, який вимагає використання нового ключового слова, якщо ви уникаєте використання цього за межами функцій. Я погоджуюся з думкою, що використання new - це стара методика, і ми повинні замість цього прагнути використовувати моделі, що уникають використання нового.

Анонімна функція самовиконання відповідає цим критеріям.

Відповідь на це запитання суб'єктивна, оскільки в JavaScript дуже багато стилів кодування. Однак, виходячи з моїх досліджень та досвіду, я рекомендую вибрати анонімну функцію самовиконання для визначення API і уникати використання нових, коли це можливо.


1
Що поганого в новому ключовому слові? Мені цікаво.
Аллі

0

Ось як я б пішов з цього приводу, IIFE SelfExecutingFunctionщоб продовжити Myclassс Myclass.anotherFunction();

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

    //Returning the anonymous object {} all the methods and properties are reflected in Myclass constructor that you want public those no included became hidden through closure; 
     return {
         methodOne = methodOne;
         methodTwo = methodTwo;
         publicProperty = publicProperty;
     };
})();

@@@@@@@@@@@@@@@@@@@@@@@@
//then define another IIFE SEF to add var function=anothermethod(){};
(function(obj){
return obj.anotherfunction=function(){console.log("Added to Myclass.anotherfunction");};
})(Myclass);

//the last bit : (function(obj){})(Myclass); obj === Myclass obj is an alias to pass the Myclass to IIFE

var myclass = new Myclass();
myclass.anotherfunction(); //"Added to Myclass.anotherfunction"

1
Програмісти - це концептуальні питання туру, на які очікуються відповіді, щоб пояснити речі. Викидання скидів коду замість пояснення - це як копіювання коду з IDE на дошку: це може здатися звичним і навіть іноді бути зрозумілим, але це виглядає дивно ... просто дивно. У дошці немає компілятора
gnat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.