Bạn có thể liên kết 'cái này' trong một hàm mũi tên không?


133

Tôi đã thử nghiệm ES6 được một thời gian và tôi chỉ gặp một vấn đề nhỏ.

Tôi thực sự thích sử dụng các chức năng mũi tên, và bất cứ khi nào tôi có thể, tôi sử dụng chúng.

Tuy nhiên, có vẻ như bạn không thể ràng buộc họ!

Đây là chức năng:

var f = () => console.log(this);

Đây là đối tượng tôi muốn liên kết hàm với:

var o = {'a': 42};

Và đây là cách tôi sẽ liên kết fvới o:

var fBound = f.bind(o);

Và sau đó tôi chỉ có thể gọi fBound:

fBound();

Cái nào sẽ xuất cái này ( ođối tượng):

{'a': 42}

Mát mẻ! Đáng yêu! Ngoại trừ việc nó không hoạt động. Thay vì xuất ra ođối tượng, nó xuất ra windowđối tượng.

Vì vậy, tôi muốn biết: bạn có thể liên kết các chức năng mũi tên? (Và nếu vậy, làm thế nào?)


Tôi đã kiểm tra mã ở trên trong Google Chrome 48 và Firefox 43 và kết quả là như nhau.


29
Toàn bộ điểm của các hàm mũi tên là chúng sử dụng thisphạm vi cha của chúng.
loganfsmyth

Câu trả lời:


158

Bạn không thể rebind this trong một chức năng mũi tên. Nó sẽ luôn được định nghĩa là bối cảnh mà nó được xác định. Nếu bạn yêu cầu thiscó ý nghĩa, bạn nên sử dụng một chức năng bình thường.

Từ Thông số ECMAScript 2015 :

Bất kỳ tham chiếu nào đến các đối số, siêu, này hoặc new.target trong Mũi tên đều phải giải quyết ràng buộc trong một môi trường bao quanh từ vựng. Thông thường, đây sẽ là Môi trường chức năng của một chức năng kèm theo ngay lập tức.


7
Câu đầu tiên của câu trả lời này là sai. Có lẽ bởi vì câu hỏi cũng khó hiểu ràng buộc và this. Hai cái này có liên quan, nhưng không giống nhau. thisbên trong một hàm mũi tên luôn 'kế thừa' thistừ phạm vi kèm theo. Đó là một tính năng của các chức năng mũi tên. Nhưng bạn vẫn có thể bindtất cả các tham số khác của hàm mũi tên. Chỉ là không this.
Stijn de Witt

54

Để hoàn thành, bạn có thể liên kết lại các chức năng mũi tên, bạn không thể thay đổi ý nghĩa của this.

bind vẫn có giá trị cho các đối số hàm:

((a, b, c) => {
  console.info(a, b, c) // 1, 2, 3
}).bind(undefined, 1, 2, 3)()

Dùng thử tại đây: http://jsbin.com/motihanopi/edit?js,console


1
Có cách nào để phát hiện hoặc sử dụng bất kỳ đối tượng nào mà hàm (mũi tên) bị ràng buộc, mà không cần tham chiếu this(tất nhiên, được định nghĩa theo từ vựng) không?
Florrie

3
Sử dụng một đối số cho ngữ cảnh: ((bối cảnh) => {someOtherFunction.apply (bối cảnh)}). Bind (willBeIgnored, bối cảnh) ()
cvazac

13

Từ MDN :

Biểu thức hàm mũi tên có cú pháp ngắn hơn so với biểu thức hàm và liên kết từ vựng với giá trị này (không ràng buộc chính nó, đối số, siêu hoặc new.target). Các chức năng mũi tên luôn ẩn danh.

Điều này có nghĩa là bạn không thể liên kết một giá trị thisnhư bạn muốn.


6

Trong nhiều năm, các nhà phát triển js đã vật lộn với ràng buộc bối cảnh, hỏi tại sao thisthay đổi trong javascript, rất nhiều nhầm lẫn trong nhiều năm do ràng buộc bối cảnh và sự khác biệt giữa ý nghĩa của thisjavascript vàthis trong hầu hết các ngôn ngữ OOP khác.

Tất cả điều này dẫn tôi đến hỏi, tại sao, tại sao! Tại sao bạn sẽ không rebind một chức năng mũi tên! Những nơi được tạo ra đặc biệt để giải quyết tất cả các vấn đề và nhầm lẫn này và tránh phải sử dụng bindhoặccall hoặc bất cứ cách nào khác để duy trì phạm vi của hàm.

TL; DR

Không, bạn không thể rebind các chức năng mũi tên.


7
Các hàm mũi tên được tạo trước hết để cung cấp cú pháp sạch hơn và nhanh hơn. Hãy suy nghĩ lambda-tính toán. Quá tệ, các OO-zealots làm xáo trộn cuộc sống của các lập trình viên chức năng JS đã nắm lấy cơ hội để lấy đi sự tự do để xác định bối cảnh gọi.
Marco Faustinelli

5
Sử dụng thiskhông phải là chức năng. lambdas nên được arguments => output. Nếu bạn cần một số bối cảnh bên ngoài, hãy chuyển nó vào. Chính sự tồn tại của thisnó đã tạo điều kiện thuận lợi cho tất cả các mẫu giày OO vào ngôn ngữ. Bạn sẽ không bao giờ nghe cụm từ "lớp javascript" mà không có nó.
erich2k8

3
Bạn có thể rebind các chức năng mũi tên. Chỉ là không this.
Stijn de Witt

6

Bạn không thể sử dụng bindđể thay đổi giá trị thisbên trong hàm mũi tên. Tuy nhiên, bạn có thể tạo một hàm thông thường mới thực hiện tương tự như hàm mũi tên cũ và sau đó sử dụng callhoặc bindliên kết lại thisnhư bình thường.

Chúng tôi sử dụng một evalcuộc gọi ở đây để tạo lại chức năng mũi tên mà bạn truyền vào như một chức năng bình thường và sau đó sử dụng callđể gọi nó với một chức năng khác this:

var obj = {value: 10};
function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});


3
Cảm ơn bạn đã chia sẻ ví dụ này về cách ràng buộc một chức năng mũi tên vào một bối cảnh khác! Để làm cho câu trả lời dễ hiểu hơn cho người đọc trong tương lai, có lẽ bạn có thể chỉnh sửa nó để mô tả cách thức và lý do tại sao mã hoạt động?
Florrie

4

Do chức năng mũi tên ES6 thực sự giải quyết được điều này trong JavaScript

Liên kết trên giải thích rằng các chức năng mũi tên this không thay đổi với các bind, call, applychức năng.

Nó được giải thích với một ví dụ rất hay.

chạy cái này trong node v4 để thấy hành vi "mong đợi",

this.test = "attached to the module";
var foo = { test: "attached to an object" };
foo.method = function(name, cb){ 
    // bind the value of "this" on the method 
    // to try and force it to be what you want 
    this[name] = cb.bind(this); };
foo.method("bar", () => { console.log(this.test); });
foo.bar(); 

Yêu cầu bạn cung cấp thêm thông tin có liên quan trong chính câu trả lời, có thể là mã mẫu hoặc phiên bản chỉnh sửa của câu hỏi
Jithin Scaria

// chạy cái này trong nút v4 để xem hành vi "mong đợi" this.test = "được gắn vào mô-đun"; var foo = {test: "gắn liền với một đối tượng"}; foo.method = function (name, cb) {// liên kết giá trị của "this" trên phương thức // để thử và buộc nó trở thành cái bạn muốn [name] = cb.bind (this); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati

Điều thú vị ở đây là thử cái này và sau đó thử lại thay thế foo.method ('bar', () => {console.log (this.test);}); với foo.method ('bar', function () {console.log (this.test);}); - bản ghi phiên bản đầu tiên "được gắn vào mô-đun" và bản ghi thứ hai "được gắn vào đối tượng" - Tôi thực sự thích cách xử lý "cái này" ổn định hơn bằng cách sử dụng các hàm mũi tên. Bạn vẫn có thể đạt được các hiệu ứng khác bằng cách sử dụng các mẫu khác nhau và kết quả là IMO dễ đọc và dễ đoán hơn.
phương pháp


2

Ngắn gọn, bạn KHÔNG THỂ liên kết các hàm mũi tên, nhưng đọc tiếp:

Hãy tưởng tượng bạn có chức năng mũi tên này bên dưới mà in thistrên bàn điều khiển:

const myFunc = ()=> console.log(this);

Vì vậy, cách khắc phục nhanh cho việc này sẽ là sử dụng chức năng bình thường, vì vậy chỉ cần thay đổi nó thành:

function myFunc() {console.log(this)};

Sau đó, bạn có thể gắn nó với bất kỳ môi trường từ vựng sử dụng bindhoặc callhay apply:

const bindedFunc = myFunc.bind(this);

và gọi nó trong trường hợp bind.

bindedFunc();

Cũng có những cách để sử dụng eval()để làm điều đó, điều này không được khuyến khích .


function myFunclà hành vi khác nhau theo những cách bên cạnh việc bị ràng buộc; một trận đấu gần hơn sẽ được const myFunc = function() {...}. Tôi cũng tò mò ý của bạn là gì khi sử dụng eval, vì đó là cách tiếp cận tôi không nghĩ có câu trả lời nào được chia sẻ ở đây trước đây - thật thú vị khi xem cách thực hiện, và sau đó đọc lý do tại sao nó lại nản lòng đến vậy.
Florrie

1

Có lẽ ví dụ này giúp bạn:

let bob = {
   _name: "Bob",
   _friends: ["stackoverflow"],
   printFriends:(x)=> {
      x._friends.forEach((f)=> {
         console.log(x._name + " knows " + f);
      });
   }
}

bob.printFriends = (bob.printFriends).bind(null,bob);
bob.printFriends();


gøød thứ ... nhẹ nhàng không giới hạn
Aleks
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.