Hiểu $ .proxy () trong jQuery


167

Từ tài liệu tôi hiểu rằng .proxy()sẽ thay đổi phạm vi của hàm được truyền dưới dạng đối số. Ai đó có thể vui lòng giải thích cho tôi điều này tốt hơn? Tại sao chúng ta nên làm điều này?


1
Theo tài liệu này, "Phương thức này hữu ích nhất để gắn các trình xử lý sự kiện vào một phần tử trong đó bối cảnh đang quay lại một đối tượng khác. Ngoài ra, jQuery đảm bảo rằng ngay cả khi bạn liên kết hàm được trả về từ jQuery.proxy (), nó sẽ vẫn hủy liên kết chức năng chính xác, nếu thông qua bản gốc ". Có điều gì đặc biệt về cụm từ mà bạn thấy thiếu không?
bzlm

1
Điều này không rõ ràng ở đây Ngoài ra, jQuery đảm bảo rằng ngay cả khi bạn liên kết hàm được trả về từ jQuery.proxy () thì nó vẫn sẽ hủy liên kết đúng chức năng, nếu được chuyển qua bản gốc ". Ý nghĩa của bản gốc là gì?
Aditya Shukla

Bản gốc là một proxy được tạo. Nhưng vì bạn không hoàn toàn nắm bắt được công cụ này, bạn có chắc chắn cần sử dụng nó không?
bzlm

1
Đây là một video hướng dẫn tuyệt vời của nettuts cho thấy $ .proxy hoạt động như thế nào. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein

1
@bzlm, tôi đã đọc tài liệu jquery khi tôi nghĩ ra phương pháp này.
Aditya Shukla

Câu trả lời:


381

Những gì nó cuối cùng làm là nó đảm bảo rằng giá trị của this hàm sẽ là giá trị bạn mong muốn.

Một ví dụ phổ biến là trong một setTimeoutdiễn ra bên trong một clickxử lý.

Thực hiện việc này:

$('#myElement').click(function() {
        // In this function, "this" is our DOM element.
    $(this).addClass('aNewClass');
});

Ý định là đủ đơn giản. Khi myElementđược nhấp, nó sẽ nhận được lớp aNewClass. Bên trong trình xử lý thisđại diện cho các yếu tố đã được nhấp.

Nhưng nếu chúng ta muốn một sự chậm trễ ngắn trước khi thêm lớp thì sao? Chúng ta có thể sử dụng một setTimeoutđể thực hiện nó, nhưng rắc rối là bất kỳ chức năng nào chúng ta cung cấp setTimeout, giá trị thisbên trong chức năng đó sẽ windowthay vì phần tử của chúng ta.

$('#myElement').click(function() {
    setTimeout(function() {
          // Problem! In this function "this" is not our element!
        $(this).addClass('aNewClass');
    }, 1000);
});

Vì vậy, những gì chúng ta có thể làm thay vào đó là gọi $.proxy(), gửi cho nó hàm và giá trị chúng ta muốn gán thisvà nó sẽ trả về một hàm sẽ giữ lại giá trị đó.

$('#myElement').click(function() {
   // ------------------v--------give $.proxy our function,
    setTimeout($.proxy(function() {
        $(this).addClass('aNewClass');  // Now "this" is again our element
    }, this), 1000);
   // ---^--------------and tell it that we want our DOM element to be the
   //                      value of "this" in the function
});

Vì vậy, sau khi chúng tôi đưa ra $.proxy()hàm và giá trị chúng tôi muốn this, nó trả về một hàm sẽ đảm bảo thisđược đặt đúng.

Làm thế nào để nó làm điều đó? Nó chỉ trả về một hàm ẩn danh gọi hàm của chúng ta bằng .apply()phương thức, cho phép nó đặt giá trị của this.

Một cái nhìn đơn giản về chức năng được trả về có thể trông như sau:

function() {
    // v--------func is the function we gave to $.proxy
    func.apply( ctx );
    // ----------^------ ctx is the value we wanted for "this" (our DOM element)
}

Vì vậy, chức năng ẩn danh này được trao cho setTimeoutvà tất cả những gì nó thực hiện là chức năng ban đầu của chúng tôi với thisbối cảnh thích hợp .


Giá trị của việc sử dụng $.proxy(function () {...}, this)chứ không phải là (function() {...}).call(this)gì? Có sự khác biệt?
Justin Morgan

11
@JustinMorgan: với .callbạn đang gọi hàm ngay lập tức. Với $.proxy, nó giống như Function.prototype.bindnơi nó trả về một chức năng mới. Hàm mới đó có thisgiá trị bị ràng buộc vĩnh viễn, do đó, khi nó được chuyển đến setTimeoutsetTimeoutgọi hàm sau, nó vẫn sẽ có thisgiá trị đúng .
trạng thái màu xám sẽ đến vào

2
Lợi thế gì, nếu có, của kỹ thuật này so với cái gì đó như thế này? $ ('# myEuity'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg

1
Bạn không phải sử dụng phương thức $ .proxy cho ví dụ này. Thay vào đó, bạn có thể chỉ cần viết lại nó như thế này $ ('# myEuity'). Click (function () {var that = this; setTimeout (function () {/ / bối cảnh mới thông qua một biến được khai báo trong phạm vi của phương thức xử lý $ (that) .addClass ('aNewClass');}, 1000);});
paul

4
Một người dùng ẩn danh với đại diện 112k, kiến ​​thức JavaScript / jQuery đáng sợ và đã không được nhìn thấy kể từ tháng 10 năm 2011 ... Có lẽ John Resig?
cantera

49

Không đi sâu vào chi tiết hơn (điều này sẽ cần thiết bởi vì đây là về Ngữ cảnh trong ECMAScript, biến bối cảnh này, v.v.)

Có ba loại "Bối cảnh" khác nhau trong ECMA- / Javascript:

  • Bối cảnh toàn cầu
  • Chức năng bối cảnh
  • bối cảnh tệ nạn

Mỗi mã được thực thi trong bối cảnh thực hiện của nó . Có một bối cảnh toàn cầu và có thể có nhiều trường hợp bối cảnh chức năng (và eval). Bây giờ là phần thú vị:

Mỗi cuộc gọi của một chức năng đi vào bối cảnh thực hiện chức năng. Một bối cảnh thực hiện của một chức năng trông giống như:


Chuỗi phạm vi đối tượng kích hoạt
giá trị này

Vì vậy, này giá trị là một đối tượng đặc biệt có liên quan với bối cảnh thực hiện. Có hai hàm trong ECMA- / Javascript có thể thay đổi giá trị này trong ngữ cảnh thực thi chức năng:

.call()
.apply()

Nếu chúng ta có một chức năng, foobar()chúng ta có thể thay đổi giá trị này bằng cách gọi:

foobar.call({test: 5});

Bây giờ chúng ta có thể truy cập vào foobarđối tượng chúng ta đã truyền vào:

function foobar() { 
    this.test // === 5
}

Đây là chính xác những gì jQuery.proxy()làm. Nó nhận một functioncontext(không gì khác hơn là một đối tượng) và liên kết hàm bằng cách gọi .call()hoặc .apply()trả về hàm mới đó.


1
Giải thích tuyệt vời, đơn giản / tốt hơn các tài liệu jQuery chính thức cho hàm
higuaro

4

Tôi đã viết chức năng này:

function my_proxy (func,obj)
{
    if (typeof(func)!="function")
        return;

    // If obj is empty or another set another object 
    if (!obj) obj=this;

    return function () { return func.apply(obj,arguments); }
}

1

Có thể đạt được cùng một mục tiêu bằng cách sử dụng chức năng tự thực hiện "Biểu hiện chức năng được gọi ngay lập tức, gọi tắt là: IIFE" :

    $('#myElement').click(function() {  
      (function(el){
         setTimeout(function() {
              // Problem! In this function "this" is not our element!
            el.addClass('colorme');
        }, 1000);
      })($(this)); // self executing function   
    });
.colorme{
  color:red;
  font-size:20px;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

  <div id="myElement">Click me</div>
</body>
</html>


2
Điều đó thường được gọi là "Biểu thức chức năng được gọi ngay lập tức" (IIFE) chứ không phải là "chức năng tự thực thi", xem en.wikipedia.org/wiki/Immediately-invoking_feft_expression .
Chris Seed
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.