2010-10-21 33 views
7

Tôi xin lỗi trước nếu câu hỏi này quá rộng. Trong thực tế nó là 4 câu hỏi khác nhau, nhưng tất cả liên quan đến cùng một đoạn mã, và tôi nghĩ rằng tất cả chúng xoay quanh cùng một nguyên tắc.Javascript: Mã này có tác dụng gì?

Tôi quyết định hôm nay, sau khi sử dụng JS trong nhiều năm, để bắt đầu học cách JS hoạt động thay vì xử lý nó như C chạy trong trình duyệt. Vì vậy, tôi bắt đầu đào sâu vào mã jQuery để xem các nhà phát triển JS thực sự sử dụng ngôn ngữ lang thang như thế nào. Đó là khi tôi tìm thấy một khối mã trông giống như mã bên dưới. Lưu ý, tôi lấy mã này ra khỏi một bài đăng xếp chồng khác ở đây In Javascript, can you extend the DOM?. Vì vậy, điều đó không có nghĩa là người đã viết mã này thậm chí còn biết anh ta đang nói về cái gì.

var myDOM = (function(){ // #1 
    var myDOM = function(elems){ // #2 
      return new MyDOMConstruct(elems); 
     }, 
     MyDOMConstruct = function(elems) { 
      this.collection = elems[1] ? Array.prototype.slice.call(elems) : [elems]; 
      return this; // #3 
     }; 
    myDOM.fn = MyDOMConstruct.prototype = { 
     forEach : function(fn) { 
      var elems = this.collection; 
      for (var i = 0, l = elems.length; i < l; i++) { 
       fn(elems[i], i); 
      } 
      return this; 
     }, 
     addStyles : function(styles) { 
      var elems = this.collection; 
      for (var i = 0, l = elems.length; i < l; i++) { 
       for (var prop in styles) { 
        elems[i].style[prop] = styles[prop]; 
       } 
      } 
      return this; 
     } 
    }; 
    return myDOM; // #4 
})(); 

1 Tại sao phải khai báo hàm bằng var myDOM = (function() {})(); thay vì var myDOM = function() {};

2 Tại sao phải khai báo hàm khác bên trong hàm myDOM có cùng tên chính xác? Tại sao không đặt tất cả logic của myDOM bên trong bên trong hàm myDOM bên ngoài?

3 Tại sao trả lại "điều này" một cách rõ ràng? Điều đó sẽ được thực hiện tự động, đúng không?

4 Điều gì đang xảy ra ở đây? Nó có trả về hàm tạo của myDOM bên trong không? Nếu vậy, tại sao?

Cập nhật

Vì vậy, hầu hết nó làm cho cảm giác bây giờ. Về # 1, tôi nghĩ myDOM đã được gán chức năng được xác định sau khi =, nhưng nó không phải. Nó đang được gán bất cứ chức năng nào trả về. Mà chỉ xảy ra là một chức năng.

Tôi vẫn chưa rõ về # 3. Có, tôi hiểu sử dụng chức năng như thế này

console.log(MyDomConstruct('foo')) 

Sẽ hiển thị 'không xác định'. Nhưng đó không phải là cách nó được sử dụng. Một vài dòng lên là này

return new MyDomConstruct(elems); 

tôi có thể hiểu explicity trở về "này" nếu tuyên bố đã đi như thế này

return MyDomConstruct(elems); 

Nhưng đó không phải là trường hợp.

+0

Thực ra 'new MyDomConstruct (elems)' không trả về 'this' mà là một thể hiện mới của' MyDomConstruct'. Có thể trong một ngữ cảnh khác, điều này dễ hiểu hơn. – nre

+0

Phải. Nhưng "điều này" và một thể hiện mới của MyDomConstruct là cả hai trường hợp của MyDomConstruct, không? Ngoài ra tại sao thậm chí sử dụng câu lệnh "mới" nếu hàm sẽ trả về một cá thể của chính nó? – mellowsoon

+0

Tất cả các câu trả lời hay. Tôi đưa ra câu trả lời cho jAndy bởi vì anh ta trả lời trước, nhưng +1 cho mọi người khác. – mellowsoon

Trả lời

9

Tại sao phải khai báo hàm bằng var myDOM = (function() {})(); thay vì var myDOM = function() {};

Đó được gọi là self-invoking anonymous function hoặc self-executing anonymous function. Nó thực hiện chính xác điều đó, nó tự gọi là thời gian chạy. Bạn cũng sẽ thấy mẫu:

(function($){ 
}(jQuery)); 

khá nhiều trong thế giới jQuery. Đó là điều tương tự, khi chạy, hàm gọi chính nó và đảm bảo rằng dấu hiệu $ có tham chiếu đến đối tượng jQuery trong phần thân hàm.

Trong đoạn mã của bạn, hàm tự gọi và trả về myDOM, tham chiếu hàm.

Tại sao phải khai báo một hàm khác bên trong hàm myDOM có cùng tên chính xác? Tại sao không đặt tất cả logic của myDOM bên trong bên trong hàm myDOM bên ngoài?

Đó chỉ là một quy ước. Nó có thể được gọi là bất cứ điều gì bạn muốn nó được, có lẽ tác giả nghĩ rằng nó là thuận lợi để làm điều này. Lý do cho mẫu này là bảo mật & bảo mật. Bằng cách trả lại tham chiếu myDOM bên trong, closure được tạo. Vì vậy, sau khi tuyên bố một cái gì đó như

var mytest = myDOM([]); 

bạn sẽ không có quyền truy cập vào MyDOMConstruct, nhưng chức năng bên trong của bạn có quyền truy cập. Bằng cách đó bạn có thể bảo vệ các phương pháp và biến của bạn. Nó còn được gọi là method pattern. Luôn đọc tốt trong ngữ cảnh này Douglas Crockford: Javascript the good parts.

Tại sao trả lại "điều này" một cách rõ ràng? Điều đó sẽ được thực hiện tự động, đúng không?

Không, hàm sẽ trả về giá trị undefined theo mặc định.Bằng một cách rõ ràng trở this bạn có thể chain các phương pháp tương tự (từ ví dụ cuộc gọi trên):

mytest.forEach([]).addStyles([]); ... 

vì mỗi phương pháp đang trở thành đối tượng của sự thỉnh nguyện, trong trường hợp này myDOM.

Điều gì đang xảy ra ở đây? Nó có trả về hàm tạo của myDOM bên trong không? Nếu vậy, tại sao?

Tôi hy vọng điều đó sẽ rõ ràng tại thời điểm này.

Sửa

Dựa trên bản cập nhật của bạn:

new MyDOMConstruct(); 

tạo ra một đối tượng mới mà inherits từ

MyDOMConstruct.prototype 

Nếu không có sự new keyword các this sẽ không bị ràng buộc với đối tượng mới . Thay vào đó, nó sẽ bị ràng buộc với đối tượng toàn cục (cửa sổ) và bạn sẽ truy cập các biến toàn cầu bằng cách sử dụng this.

+0

Cảm ơn jAndy. Tôi đã cập nhật bài đăng của mình để có thêm thông tin về # 3. – mellowsoon

+0

@mellowsoon: cập nhật – jAndy

6

1. Tại sao khai báo hàm sử dụng var myDOM = (function() {})(); thay vì var myDOM = function() {};

Các hình thức sử dụng là một chức năng thực hiện tự. Điều này có nghĩa là myDOM được đặt thành bất kỳ hàm nào trả về. Dạng thứ hai đặt myDOM thành hàm nhưng không thực thi hàm ngay lập tức.

var myDOM = (function() {   // <== This is a function that does something 
       // Something 
      })();     // The function is executed right HERE with(). 


2. Tại sao tuyên bố chức năng khác bên trong của hàm myDOM với tên chính xác giống nhau không? Tại sao không đặt tất cả logic của myDOM bên trong bên trong hàm myDOM bên ngoài?

Vì bạn đang trả về hàm myDOM bên trong ở cuối .... do đó, việc đặt tên thực sự có ý nghĩa dù nó lúc đầu khó hiểu.Điều này thường được sử dụng để tạo các biến riêng trong JS. Vì hàm bên trong sẽ có quyền truy cập vào phạm vi nó được đính kèm trong (chức năng ẩn danh tự thực hiện) nhưng người dùng sẽ không.

var myDOM = (function() { // <== This is a function that is going to return 
          // a function/object 
       var myDOM = function() { // <== We're going to return this 
        ...     //  The outer myDom will be set to it 
       };      //  So it's actually helpful to name 
             //  it the same for clarity. 
       return myDOM; 
      })(); 

// Now we can access that inner object by using the outer myDOM. 
// We could access the inner object using myDOM even if the inner object 
// was named otherTHING... It's confusing to acces something 
// called otherTHING in the code by 
// writing myDOM().... so better name the inner returned function 
// myDOM as well. 

Vì vậy, các đối tượng bên trong có thể được đặt tên là gì, và nó có thể được thực hiện với myDOM(), nhưng nếu bạn đặt tên cho chức năng bên trong blah nhưng bạn vẫn có thể thực hiện nó bằng cách sử dụng myDOM() ... đó không phải là rất rõ ràng .. tốt hơn nhiều để đặt tên cho nó giống nhau.

3. Tại sao trả lại "điều này" một cách rõ ràng? Điều đó sẽ được thực hiện tự động, đúng không?

Không, nếu bạn không viết gì, Javascript sẽ tự động trả lại undefined. MDC reference:.

Trả lại this thường được sử dụng để bảo toàn ngữ cảnh của phương thức. Nó được sử dụng để làm cho chuỗi các phương pháp ($(here).is().a().chain().of().methods()) có thể. Vì vậy, một phương thức trong chuỗi biết ngữ cảnh hoạt động.

+0

Cảm ơn câu trả lời. Về phần 2, tôi đoán đây là lý do tại sao hiển thị một đối tượng jquery - tức là console.log ($ (document)) - cho thấy "jQuery()" chứ không phải là "jQuery.init()" là đối tượng jQuery thực sự là (như tôi có thể nói). Đó là vì jQuery.init đang được gán cho biến có tên là jQuery. Kỳ dị. – mellowsoon

3

Chỉ một lưu ý nữa là chức năng ẩn danh tự thực hiện . Việc gói một số đoạn mã JavaScript vào khối (function() { })(); là một cách để tạo ra một không gian tên riêng. Mọi thứ bạn làm bên trong hàm bên trong sẽ là hàm riêng tư ngoại trừ giá trị được trả về (trong trường hợp này là myDOM).

Đó là bạn Savely có thể làm những việc như

var counter = (function() { 
    var i = 0; 
    var counter = function() { 
     alert(i++); 
     return i; 
    } 
    return counter; 
})(); 

counter(); // will alert 0 
counter(); // will alert 1 
// ... and so on 

để giữ một số bí mật nhà nước bên trong từ bên ngoài hàm. Lý do thứ hai, như đã nêu trong các bài viết khác là nó sẽ không gây ô nhiễm không gian tên chung với biến số i. Đó là lý do tại sao nó là thực hành tốt nhất để sử dụng các loại chức năng trong phát triển plugin jQuery.

+0

Mặc dù tôi đã biết chức năng ẩn danh tự thực hiện về nguyên tắc, vì một lý do nào đó nó không bao giờ vượt qua tâm trí của tôi để sử dụng nó như một cách hoặc cung cấp các biến riêng cho các chức năng. Chúng không chính xác là thuộc tính riêng tư, nhưng nó hoàn thành điều tương tự. – mellowsoon

1

giống như này:


(function() { 
    function b(a) { 
     return new c(a) 
    } 
    function c(a) { 
     this.a = a[1] ? Array.prototype.slice.call(a) : [a]; 
     return this 
    } 
    b.b = c.prototype = {}; 
    return b 
})(); 

hy vọng rằng sẽ giúp ... :)

1

ví dụ: var fn = (function() { chức năng trở lại() {alert (11) }; })(); khi mã chạy, bây giờ là fn = function() {alert (11)}; thử nó !!!