Cách xác định đối tượng JS này có mục đích gì không?


87

Tôi đang duy trì một số mã kế thừa và tôi nhận thấy rằng mẫu sau để xác định đối tượng được sử dụng:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

Có mục đích gì cho việc này không? Nó có tương đương với việc chỉ làm như sau không?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

Tôi không định bắt tay vào một nhiệm vụ thần thánh để cấu trúc lại toàn bộ cơ sở mã theo ý thích của mình, nhưng tôi thực sự muốn hiểu lý do đằng sau cách xác định đối tượng vòng vo đó.

Cảm ơn!


1
Trong ví dụ chính xác của bạn không có sự khác biệt. Nếu bạn mở rộng nó, có thể có sự khác biệt nhưng sau đó cũng sẽ có các cách tiếp cận khác nhau để chơi.
Travis J

Nó không có gì khác biệt, các đối tượng được truyền như một bản sao của một tham chiếu, vì vậy, ngay cả khi xác định myFunction bên trong IIFE, nó vẫn có thể truy cập được bên ngoài nó.
adeneo

1
@adeneo Không phải đối với ví dụ này, bởi myFunctioncó thể sử dụng một số biến được định nghĩa bên ngoài chính nó mà sẽ không thể truy cập được từ bên ngoài. Xem câu trả lời của tôi
Juan Mendes

2
có thể trùng lặp với Mẫu JavaScript này được gọi là gì và tại sao nó được sử dụng? (không chắc liệu tôi có nên đóng hay không). Xem thêm Khai báo không gian tên JavaScript hoặc khai báo này .
Bergi

Câu trả lời:


116

Nó được gọi là mô-đun http://toddmotto.com/mastering-the-module-pattern/

Lý do chính là để bạn tạo các phương thức và biến thực sự riêng tư. Trong trường hợp của bạn, nó không có ý nghĩa vì nó không ẩn bất kỳ chi tiết triển khai nào.

Dưới đây là một ví dụ về việc sử dụng mô-đun một cách hợp lý.

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

Trong khi nếu bạn chỉ sử dụng một đối tượng thẳng addervaluesẽ trở thành công khai

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

Và bạn có thể phá vỡ nó bằng cách làm những việc như

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

Nhưng với phiên bản mô-đun

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3

2
Điều này không thực sự trả lời câu hỏi về sự khác biệt giữa hai lựa chọn thay thế là gì?

20
@torazaburo Ví dụ của OP không phải là một ví dụ tốt, tôi đã cung cấp một ví dụ thực tế cho thấy khi nào sử dụng mẫu mô-đun.
Juan Mendes

Điều này ns.getNext: function () {sẽ không biên dịch.
punund

Tôi sẽ có, nếu tôi chắc chắn làm thế nào để sửa chữa nó. Tôi nghĩ rằng có một số cấu trúc để ngăn chặn delete MyNameSpace.getNext.
punund

2
@punund JS không có trình biên dịch mà nó có trình thông dịch:)
ếchatto

22

Mục đích là để giới hạn khả năng truy cập của các chức năng trong bao đóng để giúp ngăn các tập lệnh khác thực thi mã trên đó. Bằng cách quấn nó xung quanh một bao đóng, bạn đang xác định lại phạm vi thực thi cho tất cả mã bên trong bao đóng và tạo một phạm vi riêng một cách hiệu quả. Xem bài viết này để biết thêm thông tin:

http://lupomontero.com/using-javascript-closures-to-create-private-scope/

Từ bài báo:

Một trong những vấn đề được biết đến nhiều nhất trong JavaScript là sự phụ thuộc của nó vào phạm vi toàn cục, về cơ bản có nghĩa là bất kỳ biến nào bạn khai báo bên ngoài một hàm đều nằm trong không gian cùng tên: đối tượng cửa sổ đáng ngại. Do bản chất của các trang web, nhiều tập lệnh từ các nguồn khác nhau có thể (và sẽ) chạy trên cùng một trang chia sẻ một phạm vi toàn cầu chung và đây có thể là một điều thực sự tồi tệ vì nó có thể dẫn đến xung đột tên (các biến có cùng tên bị ghi đè) và các vấn đề bảo mật. Để giảm thiểu vấn đề, chúng ta có thể sử dụng các bao đóng mạnh mẽ của JavaScript để tạo các phạm vi riêng tư nơi chúng ta có thể chắc chắn rằng các biến của chúng ta ẩn với các tập lệnh khác trên trang.



Mã:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function

1
myFunctionkhông có trong phạm vi toàn cầu trong phiên bản thứ hai. Mục đích thực sự là cung cấp một phạm vi mà các chức năng phụ trợ bên trong có thể được xác định.
Barmar

myFunctionnằm trong phạm vi toàn cục vì nó được định nghĩa trong đối tượng toàn cục myObject. Trong phiên bản thứ hai, bất kỳ mã nào khác trong ứng dụng có thể thực thi myFunction. Trong phiên bản đầu tiên chỉ mã trong việc đóng cửa có quyền truy cập vàomyFunction
Jonathan Crowe

Không, myFunctionchỉ có thể được thực thi MyObject.myFunction()giống như phiên bản đầu tiên.
Barmar

@JonathanCrowe Ví dụ của OP không phải là một ví dụ điển hình, nó để lộ mọi thứ bên trong mô-đun, vì vậy có, nó đang trở nên có thể truy cập bên ngoài. Xem câu trả lời của tôi cho một trường hợp mô-đun hữu ích
Juan Mendes

@JuanMendes điểm tốt, ví dụ OP không phải là sử dụng tuyệt vời của kiểu mô-đun
Jonathan Crowe

6

ưu điểm:

  1. duy trì các biến trong phạm vi riêng tư.

  2. bạn có thể mở rộng chức năng của đối tượng hiện có.

  3. hiệu suất được tăng lên.

Tôi nghĩ rằng ba điểm đơn giản trên là vừa đủ để tuân theo các quy tắc đó. Và để giữ cho nó đơn giản không có gì khác ngoài việc viết các hàm bên trong.


6

Trong trường hợp cụ thể mà bạn hiển thị, không có sự khác biệt có ý nghĩa, về chức năng hoặc khả năng hiển thị.

Có khả năng là lập trình viên ban đầu đã áp dụng cách tiếp cận này như một loại khuôn mẫu cho phép anh ta xác định các biến riêng có thể được sử dụng trong định nghĩa của những thứ như myFunction:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

Điều này giúp tránh tính toán seconds_per_daymỗi khi hàm được gọi, đồng thời giữ cho nó không làm ô nhiễm phạm vi toàn cầu.

Tuy nhiên, về cơ bản không có gì khác với điều đó và chỉ nói

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

Người lập trình ban đầu có thể thích có thể thêm các chức năng vào đối tượng bằng cách sử dụng cú pháp khai báo của root.myFunction = function, hơn là cú pháp đối tượng / thuộc tính của myFunction: function. Nhưng sự khác biệt đó chủ yếu là vấn đề sở thích.

Tuy nhiên, cấu trúc được sử dụng bởi người viết mã gốc có lợi thế là các thuộc tính / phương thức có thể dễ dàng được thêm vào các nơi khác trong mã:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

Tóm lại, không cần áp dụng cách tiếp cận này nếu bạn không cần thiết, nhưng cũng không cần phải thay đổi nó, vì nó hoạt động hoàn toàn tốt và thực sự có một số ưu điểm.


6
  1. Mẫu đầu tiên có thể được sử dụng như một mô-đun lấy một đối tượng và trả về đối tượng đó với một số sửa đổi. Nói cách khác, bạn có thể định nghĩa các mô-đun như sau.

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }
    

    Và sử dụng nó như:

    var obj = {};
    module(obj);
    

    Vì vậy, một lợi thế có thể là khả năng tái sử dụng của mô-đun này cho những lần sử dụng sau.


  1. Trong mẫu đầu tiên, bạn có thể xác định phạm vi riêng tư để lưu trữ nội dung riêng tư của mình như thuộc tính và phương thức riêng. Ví dụ: hãy xem xét đoạn mã này:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);
    

  1. Mẫu này có thể được sử dụng để thêm một phương thức hoặc thuộc tính cho tất cả các loại đối tượng như mảng, đối tượng ký tự, hàm.

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12
    

Theo tôi, ưu điểm chính có thể là ưu điểm thứ hai (tạo phạm vi riêng tư)


5

Mẫu này cung cấp một phạm vi trong đó bạn có thể xác định các chức năng trợ giúp không hiển thị trong phạm vi chung:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo là cục bộ của hàm ẩn danh, nó không thể được tham chiếu từ bên ngoài.

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.