Chức năng ẩn danh tự thực hiện so với nguyên mẫu


26

Trong Javascript, có một vài kỹ thuật nổi bật rõ ràng để tạo và quản lý các lớp / không gian tên trong javascript.

Tôi tò mò không biết tình huống nào bảo đảm sử dụng một kỹ thuật so với kỹ thuật kia. Tôi muốn chọn một và gắn bó với nó di chuyển về phía trước.

Tôi viết mã doanh nghiệp được duy trì và chia sẻ trên nhiều nhóm và tôi muốn biết cách thực hành tốt nhất khi viết javascript duy trì là gì?

Tôi có xu hướng thích Tự thực hiện các chức năng ẩn danh tuy nhiên tôi tò mò không biết phiếu bầu của cộng đồng là gì đối với các kỹ thuật này.

Nguyên mẫu:

function obj()
{
}

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

Chức năng ẩn danh tự đóng:

//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
}

Tôi cảm thấy rằng tôi nên đề cập rằng Hàm ẩn danh tự thực thi là mẫu được sử dụng bởi nhóm jQuery.

Cập nhật Khi tôi hỏi câu hỏi này, tôi không thực sự thấy tầm quan trọng của những gì tôi đang cố gắng hiểu. Vấn đề thực sự trong tay là liệu có nên sử dụng mới để tạo phiên bản của các đối tượng của bạn hay sử dụng các mẫu không yêu cầu các nhà xây dựng / sử dụng newtừ khóa.

Tôi đã thêm câu trả lời của riêng mình, vì theo tôi, chúng ta nên sử dụng các mẫu không sử dụng newtừ khóa.

Để biết thêm thông tin xin vui lòng xem câu trả lời của tôi.


1
bạn có thể đưa ra ví dụ ngắn về hai kỹ thuật bạn đang mô tả?
Này

Đừng đánh giá thấp nguyên mẫu vì mẫu đơn giản của tôi.
Robotsushi

1
nó không tự thực hiện = /
Hey

2
tôi không thấy bất kỳ dấu ngoặc đơn nào để đóng biểu thức hoặc gọi nó ...
Hey

1
(+1) Không gian tên được bỏ qua cho nhiều nhà phát triển.
umlcat

Câu trả lời:


22

Tự thực thi các hàm ẩn danh được sử dụng để tự động thực thi tập lệnh mà không cần nối vào các sự kiện bên ngoài (ví dụ window.onload).

Trong ví dụ này, nó được sử dụng để tạo mẫu Mô-đun cổ điển, mục đích chính của nó là giới thiệu một không gian tên vào môi trường toàn cầu và cung cấp đóng gói cho bất kỳ thuộc tính bên trong nào không được "xuất" hoặc gắn vào không gian tên.

Sửa đổi một nguyên mẫu đối tượng, mặt khác, được sử dụng để thiết lập sự kế thừa (hoặc mở rộng bản địa). Mẫu này được sử dụng để tạo ra các đối tượng 1: n với các phương thức hoặc thuộc tính phổ biến.

Bạn không nên chọn một mẫu theo sở thích khác, vì chúng thực hiện các nhiệm vụ khác nhau . Về mặt không gian tên, Chức năng Tự thực hiện là một lựa chọn thích hợp.


7
Lưu ý rằng "tự thực thi các hàm ẩn danh" thường được gọi là biểu thức hàm được gọi ngay lập tức (IIFE) .
voithos

Tôi tránh chúng và thành thật mà nói tôi không có được tình yêu với IIFE. Chúng là một mớ hỗn độn để gỡ lỗi và phá vỡ phác thảo mã trong Eclipse. Nếu bạn cần một không gian tên, hãy dán nó vào một đối tượng, nếu bạn cần thực thi nó, chỉ cần gọi nó và đóng gói không thực sự mang lại cho tôi bất cứ điều gì.
Daniel Sokolowski

4

Đây là mẫu mà tôi mới bắt đầu sử dụng (đã sử dụng các biến thể của nó cho đến ngày hôm qua):

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

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

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

Một vài suy nghĩ về điều này:

  1. Bạn không nên lấy bất kỳ (anonymous)dấu vết nào trong ngăn xếp trình gỡ lỗi của mình dưới dạng mọi thứ được đặt tên (không có chức năng ẩn danh).
  2. Đó là mẫu sạch nhất mà tôi từng thấy
  3. Bạn có thể dễ dàng nhóm nhóm API được hiển thị của mình mà không cần triển khai chúng cùng với khai báo (có nghĩa là ai đó có thể dễ dàng tìm kiếm giao diện lớp công khai của bạn mà không cần phải cuộn)

Lần duy nhất tôi sử dụng prototypenữa thực sự là xác định tính kế thừa.


5
Có một vài vấn đề với điều này. Một đối tượng hàm mới được tạo cho mỗi "phương thức" với mỗi lần gọi của hàm tạo. Ngoài ra, việc gọi một hàm tạo để có được một bản sao của đối tượng nguyên mẫu để kế thừa được chấp nhận. Sử dụng Object.create (ParentClass.prototype) hoặc shim cho Object.create nhưfunction clone(obj){return this typeof 'clone' ? this : new clone(clone.prototype=obj)}
Hey

@GGG: Vâng, bạn đúng với điểm đầu tiên của bạn. Tôi muốn nói (và nên được đề cập trong bài viết của tôi) rằng mỗi trường hợp sử dụng cụ thể của việc triển khai nên được suy nghĩ kỹ. Vấn đề của tôi với prototypephương pháp như bạn đề xuất là (trừ khi có một phương pháp mà tôi không quen thuộc, có thể là trường hợp đó) rằng bạn mất khả năng đóng gói các thuộc tính, nghĩa là mọi thứ đều mở như công khai (không phải là ngày tận thế , chỉ là một sở thích cá nhân).
Demian Brecht

Ngoài ra, sau khi xem các công việc sau được thực hiện trên jsperf ( jsperf.com/object-create-vs-constructor-vs-object-literal/12 ), tôi sẽ tăng hiệu suất so với chi phí bộ nhớ của các bản sao bổ sung gần như bất kỳ ngày nào (một lần nữa, rất chủ quan với trường hợp sử dụng cụ thể).
Demian Brecht

Bây giờ, đã nói tất cả những điều đó, tôi chỉ mới tham gia ECMA-262, nên có thể có một loạt những thứ tôi không thấy .. Ngoài ra, tôi không lấy lời của Crockford làm phúc âm .. Vâng, anh ấy là một của các chuyên gia trong lĩnh vực này (một trong những điều quan trọng nhất), nhưng điều đó không phải lúc nào cũng khiến anh ta đúng 100%. Có những chuyên gia khác ngoài kia (tôi không phải là một trong số họ;)) có những ý kiến ​​trái ngược với những lập luận thuyết phục.
Demian Brecht

2
bạn có thể quan tâm đến điều này tôi đang làm việc .
Này

3

Tôi sử dụng các nguyên mẫu vì chúng sạch hơn và tuân theo các mẫu thừa kế tiêu chuẩn. Các chức năng tự gọi rất tốt cho phát triển trình duyệt hoặc trong trường hợp bạn không biết mã được thực thi ở đâu, nhưng nếu không thì đó chỉ là nhiễu.

Thí dụ:

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
Tự thực thi các hàm ẩn danh rất hữu ích để cho phép bạn có các hàm riêng có thể truy cập được vào một lớp.
Zee

1

Tôi sẽ sử dụng chức năng tự thực thi, nhưng có một chút khác biệt:

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

Nếu tìm cách tiếp cận này sạch hơn nhiều, khi tôi tách biến toàn cục được trả về khỏi bất kỳ tham số đầu vào nào (chẳng hạn như jQuery) (cách bạn viết nó tương đương với trả về void và sử dụng tham số ref trong C #, tôi thấy hơi bị tắt, hoặc chuyển một con trỏ đến một con trỏ và gán lại nó trong C ++). Nếu sau đó tôi sẽ đính kèm các phương thức hoặc thuộc tính bổ sung vào lớp thì tôi sẽ sử dụng kế thừa nguyên mẫu (ví dụ với phương thức $ .extend của jQuery, nhưng nó đủ dễ để cuộn phần mở rộng của riêng bạn ()):

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"

Bằng cách này, bạn có sự phân biệt rõ ràng giữa các phương thức được thêm vào và các phương thức ban đầu.


1
Tôi có phải là người duy nhất nghĩ rằng sử dụng NFE như thế này là một ý tưởng tồi không?
Này

1

Ví dụ trực tiếp

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

Bạn có thể sử dụng IIFE để mô phỏng "phạm vi mô-đun" xung quanh mã của mình. Sau đó, bạn có thể chỉ cần sử dụng các đối tượng như bạn thường làm.

Không "mô phỏng" trạng thái riêng tư bằng cách sử dụng các bao đóng vì có hình phạt bộ nhớ lớn.

Nếu bạn viết một ứng dụng doanh nghiệp và muốn duy trì mức sử dụng bộ nhớ dưới 1GB, hãy tránh sử dụng các lần đóng để lưu trữ trạng thái.


Bạn đã có một vài lỗi chính tả trong mã mate. Tôi cũng không tích cực về việc bạn tự động đóng cửa gây ra việc sử dụng bộ nhớ quá mức, tôi nghĩ điều đó phụ thuộc vào mức độ bạn sử dụng chúng và mức độ bạn xử lý các vấn đề phạm vi (nói chung là trộn lẫn mọi thứ từ bên ngoài với mọi thứ từ bên ngoài xấu, chẳng hạn (việc sử dụng bảng điều khiển toàn cầu của bạn là một ví dụ tốt về điều này, đoạn mã trên sẽ hiệu quả hơn nhiều nếu bạn chuyển qua bàn điều khiển dưới dạng một biến)).
Ed James

@EdWoodcock Tôi hình dung mã bị lỗi, chỉ cần cấu trúc lại và sửa nó.
Raynos

@EdWoodcock sự khác biệt hiệu quả giữa địa phương consolevà toàn cầu consolelà tối ưu hóa vi mô. Điều đáng làm là bạn có thể giảm thiểu consolenhưng đó là một vấn đề khác
Raynos

Đóng cửa @EdWoodcock chậm nếu các đối tượng trùng lặp của bạn trong đó. Cái gì chậm là tạo chức năng bên trong chức năng khi không cần thiết. Các bao đóng cũng có chi phí bộ nhớ nhỏ để lưu trữ trạng thái so với trạng thái lưu trữ trực tiếp trên các đối tượng
Raynos

Vâng, chỉ muốn chỉ ra vì câu trả lời của bạn là một cách hợp lý để đi về mọi thứ (mặc dù không phải là cách tôi chọn sử dụng). (Tôi đã thực hiện chỉnh sửa nhưng tôi vẫn không chắc chắn về nghi thức của những người trên trang SE cụ thể này).
Ed James

0

Cập nhật Bây giờ tôi đã hiểu rõ hơn về javascript và cảm thấy tôi có thể giải quyết chính xác câu hỏi. Tôi nghĩ rằng đó là một chủ đề javascript kém, nhưng rất quan trọng để giải quyết.

Mẫu Chức năng ẩn danh tự thực thi không phải là mẫu yêu cầu sử dụng từ khóa mới nếu bạn tránh sử dụng hàm ngoài này. Tôi đồng ý với ý kiến ​​cho rằng sử dụng cái mới là một kỹ thuật cũ và thay vào đó chúng ta nên cố gắng sử dụng các mẫu tránh sử dụng cái mới.

Chức năng ẩn danh tự thực hiện đáp ứng tiêu chí này.

Câu trả lời cho câu hỏi này là chủ quan, bởi vì có rất nhiều kiểu mã hóa trong javascript. Tuy nhiên, dựa trên nghiên cứu và kinh nghiệm của tôi, tôi khuyên bạn nên chọn sử dụng Chức năng ẩn danh tự thực thi để xác định API của mình và tránh sử dụng mới bất cứ khi nào có thể.


1
Điều gì là xấu về từ khóa mới? Tôi tò mò.
minh

0

Đây là cách tôi sẽ đi về điều này IIFE SelfExecutingFunctionđể mở rộng Myclassvới 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
Lập trình viên là câu hỏi khái niệm tour du lịch câu trả lời dự kiến ​​sẽ giải thích mọi thứ. Ném các đoạn mã thay vì giải thích giống như sao chép mã từ IDE sang bảng trắng: nó có thể trông quen thuộc và thậm chí đôi khi có thể hiểu được, nhưng nó cảm thấy kỳ lạ ... chỉ là lạ. Bảng trắng không có trình biên dịch
gnat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.