Var self = this; một mô hình xấu?


77

Tôi thấy mình cần:

var self = this;

rất nhiều trong các 'lớp' javascript của tôi. Mặc dù điều này thường được thực hiện, nó cảm thấy hơi sai. Điều tôi hy vọng sẽ tìm thấy trong câu hỏi này là một cách tốt hơn để giải quyết vấn đề này, hoặc một điều gì đó để thuyết phục tôi rằng điều này khá ổn.

Đây có phải là cách tiêu chuẩn để giữ các ràng buộc chính xác xung quanh không? Tôi có nên tiêu chuẩn hóa việc sử dụng 'self' ở mọi nơi không, trừ khi tôi rõ ràng cần 'this'.

chỉnh sửa : Tôi biết chính xác lý do tại sao tôi cần cái này, tôi chỉ tự hỏi liệu nó có bị coi là hơi ác không và tại sao. Tôi biết rằng cũng có hàm javascript tích hợp 'áp dụng' để xác định rõ ràng phạm vi khi gọi một phương thức. Nó có tốt hơn không?


2
Tôi luôn luôn sử dụng var that = this, thành thật mà nói, tôi thậm chí còn không bận tâm đến việc áp dụng / gọi, nhưng bây giờ tôi đã đọc về những phương pháp đó, cảm ơn vì câu hỏi này!
Anders

Cá nhân tôi nghĩ rằng điều này không phải là xấu, nhưng là một dấu hiệu cho thấy bạn có thể tối ưu hóa thiết kế của mình một chút.
Dmitry

Câu trả lời:


50

Như những người khác đã nói: "Biến phụ" này (ở một mức độ nào đó) là cách duy nhất để biết thực tế thislà một biểu thức đặc biệt và do đó, không phải là một biến, không bị ràng buộc trong một ngữ cảnh thực thi / đóng.

Tuy nhiên, những gì tôi nghĩ bạn đang hỏi (hoặc những gì tôi thực sự muốn trả lời) là:

Có nên đặt var self = thisở đầu mỗi phương thức / hàm tạo không?

Tóm lược

Trong khi tôi đã thử cách này một lần và có cùng câu hỏi, tôi không còn sử dụng phương pháp này nữa. Bây giờ tôi bảo lưu cấu trúc khi tôi cần truy cập khi đóng. Đối với tôi nó thêm một chút "này, đây là những gì tôi thực sự muốn!" ngữ nghĩa với mã của tôi:

this -> thisself -> this (but really that) in a closure

Câu hỏi ala carte:

... Mặc dù điều này thường được thực hiện, nó cảm thấy hơi sai. Điều tôi hy vọng sẽ tìm thấy trong câu hỏi này là một cách tốt hơn để giải quyết vấn đề này, hoặc một điều gì đó để thuyết phục tôi rằng điều này khá ổn.

Làm những gì cảm thấy phù hợp với bạn. Đừng ngại thử một phương pháp và chuyển lại sau (nhưng hãy cố gắng duy trì tính nhất quán trong mỗi dự án :-)

Đây có phải là cách tiêu chuẩn để giữ các ràng buộc chính xác xung quanh không? Tôi có nên tiêu chuẩn hóa việc sử dụng 'self' ở mọi nơi không, trừ khi tôi rõ ràng cần 'this'.

"self" là tên phổ biến nhất được sử dụng. Như đã nói ở trên, tôi thích cách tiếp cận ngược lại - sử dụng thisngoại trừ khi cần có ràng buộc đóng.

..nếu nó bị coi là hơi ác và tại sao.

Ác ma là một thuật ngữ chủ quan ngớ ngẩn (mặc dù đôi khi rất vui). Tôi chưa bao giờ nói đó là điều xấu xa, chỉ tại sao tôi không làm theo cách tiếp cận. Một số người nói với tôi rằng tôi "xấu xa" vì không sử dụng dấu chấm phẩy. Tôi nói với họ rằng họ thực sự nên đưa ra các lập luận tốt và / hoặc học JavaScript tốt hơn :-)

Tôi biết rằng cũng có hàm javascript tích hợp 'áp dụng' để xác định rõ ràng phạm vi khi gọi một phương thức. Nó có tốt hơn không?

Vấn đề apply/calllà bạn phải sử dụng chúng tại thời điểm gọi hàm. Nó không phải sẽ giúp đỡ nếu có ai khác gọi một trong những phương pháp của bạn như thisthể đã được tắt. Nó hữu ích nhất để thực hiện những việc như gọi lại kiểu jQuery trong đó thisphần tử / mục của lệnh gọi lại, v.v.

Như một bên ...

Tôi muốn tránh "cần tự" đối với các thành viên và do đó nói chung thúc đẩy tất cả các chức năng của thành viên đến các thuộc tính mà receiver ( this) chỉ "chảy qua", thường là "như mong đợi".

Các phương thức "riêng tư" trong mã của tôi bắt đầu bằng dấu "_" và nếu người dùng gọi chúng, thì đó là trên chúng. Điều này cũng hoạt động tốt hơn (thực sự là bắt buộc) khi sử dụng cách tiếp cận nguyên mẫu để tạo đối tượng. Tuy nhiên, Douglas Crockford không đồng ý với cách tiếp cận "riêng tư" này của tôi và có một số trường hợp chuỗi tra cứu có thể cản trở bạn bằng cách đưa vào một bộ thu không mong muốn:

Sử dụng giới hạn "self" trong hàm tạo cũng khóa giới hạn trên của chuỗi tra cứu đối với một phương thức (nó không còn đa hình trở lên!) Có thể đúng hoặc không. Tôi nghĩ rằng nó bình thường không chính xác.

Chúc bạn viết mã vui vẻ.


1
Câu trả lời chính xác. Bạn có thể giải thích thêm về tuyên bố này: "Tôi muốn tránh" cần tự "đối với các thành viên và do đó nói chung thúc đẩy tất cả các chức năng của thành viên đến các thuộc tính mà người nhận (cái này) chỉ" chảy qua ", thường là" như mong đợi "." Làm thế nào để bạn làm điều này?
Evert

1
@Evert Nếu mọi phương thức là một thành viên của đối tượng, thì nó sẽ / nên được gọi dưới dạng một số dạng obj.memberthường sẽ đảm bảo thislà đúng (kể từ obj->this(obj)->this(obj)->...). Nếu bạn thấy liên kết Crockford trong bài đăng, bạn sẽ thấy rằng cách tiếp cận phương thức riêng phá vỡ mẫu vì các "phương thức" riêng hiện được lưu trữ trong các biến trong hàm tạo và do đó không được gọi trong obj.memberbiểu mẫu. Điều này sẽ xuất hiện thisbên trong chúng ( thischỉ đơn thuần là bộ nhận của hàm tại thời điểm gọi - objtrong các ví dụ trên) trừ khi thực hiện thêm công việc.

3
aelflà đối tượng cửa sổ - Tôi sẽ tránh sử dụng những từ dành riêng, và sử dụng var that = thishayvar _self = this
chovy

@chovy Điểm thú vị mà tôi chưa từng nghĩ tới. Tôi đoán tôi luôn tránh vấn đề bằng cách luôn [trừ những lần hiếm hoi tôi quên] sử dụng một window.xtrình độ chuyên môn trong mã của tôi (đó và tôi chưa bao giờ sử dụng window.self..)

Hãy ác gấp đôi. Đặt var self = falsetrong bối cảnh chung, sau đó thêm var self = this;các phương thức mà bạn cần. Sau đó, nếu có bao giờ nghi ngờ, hãy viết (self || this).method(...). Nó là một mã hóa tồi tệ, nhưng nó cảm thấy tốt.
Orwellophile

17

Vâng, đây là cách tiêu chuẩn.

Function.apply()Function.call()có thể giúp đỡ, nhưng không phải lúc nào cũng vậy.

Hãy xem xét những điều sau

function foo()
{
  var self = this;
  this.name = 'foo';

  setTimeout( function()
  {
    alert( "Hi from " + self.name );
  }, 1000 );       
}

new foo();

Nếu bạn muốn làm điều này nhưng tránh sử dụng một biến như selfvà sử dụng call()hoặc apply()thay vào đó ... tốt ... bạn nhìn vào nó và bắt đầu thử, nhưng sớm nhận ra rằng bạn không thể. setTimeout()chịu trách nhiệm về việc gọi lambda, khiến bạn không thể tận dụng các kiểu gọi thay thế này. Cuối cùng bạn vẫn phải tạo một số biến trung gian để giữ một tham chiếu đến đối tượng.


4
Xin lỗi nếu tôi hiểu sai, nhưng nó hoạt động nếu bạn làm điều này: setTimeout( (function() { alert( "Hi from " + this.name ); }).apply(this), 1000 ); }
drodsou

@drodsou Không, mã của bạn không hoạt động, hãy xem JSBin này . Nó không thành công với lỗi Cú pháp, bởi vì bạn không thể chỉ bọc một cái gì đó trong "()" trong JS. Như bạn có thể thấy, window.setTimeout không trả về một hàm / đối tượng, mà là một nguyên thủy của kiểu "số" đại diện cho id của bộ đếm.
Sentenza

@Peter Bailey thực sự có một cách khác , tuy nhiên, tôi không nói rằng đó là giải pháp tốt hơn.
Sentenza

3
@drodsou Sử dụng apply (this) sẽ thực thi hàm ngay lập tức, do đó trả về không xác định là đối số đầu tiên của setTimeout, cách nhau có một hàm ràng buộc: window.setTimeout (function () {alert ("Xin chào từ" + this.name);} .bind (cái này), 1e3);
AlexanderB

1
Vì vậy, ... cách tôi đang nhìn thấy điều này: sử dụng ràng buộc cung cấp mã NGẮN HẠN hơn mã được đưa ra trong câu trả lời này :). Vì vậy, câu trả lời này là sai. Dấu 'nhưng không phải luôn luôn' gây hiểu nhầm.
Adam Skobodzinski

9

Đây có phải là cách tiêu chuẩn để giữ các ràng buộc chính xác xung quanh không?

Không có tiêu chuẩn nào liên quan đến JavaScript và hệ thống lớp / phiên bản. Bạn sẽ phải chọn loại mô hình đối tượng mà bạn thích. Đây là một liên kết khác tới trình nền; kết luận: không có sự kết luận.

Thông thường, việc giữ một bản sao var self= this;(*) trong một bao đóng đi đôi với một mô hình đối tượng được xây dựng xung quanh các bao đóng với các bản sao theo từng trường hợp của mỗi phương thức. Đó là một cách làm hợp lệ; một chút kém hiệu quả, nhưng cũng thường là một chút ít công việc hơn thay thế, một mô hình đối tượng xây dựng xung quanh prototyping, sử dụng this, apply()và ECMAScript Fifth Edition bind()để có được phương pháp ràng buộc.

Điều gì có thể được coi là 'ác' hơn là khi bạn có một hỗn hợp của cả hai kiểu trong cùng một mã. Thật không may, rất nhiều mã JS phổ biến thực hiện điều này (vì hãy đối mặt với nó, không ai thực sự hiểu được mô hình đối tượng gốc kỳ lạ của JavaScript).

(*: Tôi thường sử dụng thatthay vì self; bạn có thể sử dụng bất kỳ tên biến nào bạn thích, nhưng selfđã có một ý nghĩa hơi tối nghĩa và hoàn toàn vô nghĩa vì một windowthành viên trỏ đến chính cửa sổ.)


7

Tôi chỉ gặp câu hỏi này vì đồng nghiệp của tôi nghiện các biến số của bản thân / đó và tôi muốn hiểu tại sao ...

Tôi nghĩ rằng có một cách tốt hơn để giải quyết vấn đề này trong ngày hôm nay:

function () {}.bind(this);      // native
_.bind(function () {}, this);   // lodash
$.proxy(function () {}, this);  // jquery

4

Trong javascript và các ngôn ngữ khác có bao đóng , đây có thể là một việc rất quan trọng cần làm. Đối tượng thistham chiếu đến trong một phương thức thực sự có thể thay đổi . Khi bạn đặt selfbiến của mình bằng this, thì tự sẽ vẫn là một tham chiếu đến đối tượng được đề cập, ngay cả khi thissau đó trỏ đến một cái gì đó khác.

Đây là một sự khác biệt quan trọng trong javascript so với nhiều ngôn ngữ khác mà chúng tôi làm việc. Tôi đến từ .Net, vì vậy loại này thoạt đầu cũng có vẻ lạ lẫm với tôi.

Chỉnh sửa : À, được rồi, bạn biết tất cả những điều đó. (có thể vẫn hữu ích cho người khác.) Tôi sẽ thêm rằng Áp dụng (và Gọi) nhiều hơn để sử dụng từ "bên ngoài", cho một chức năng bạn đang gọi một phạm vi cụ thể mà bạn đã biết. Khi bạn đang ở trong một hàm và bạn sắp xếp tầng sâu hơn vào các phần đóng, kỹ thuật:

  var self = this;

là cách thích hợp hơn ( dễ dàngrõ ràng ) để neo phạm vi hiện tại của bạn .


2

Nhiều khả năng điều này được thực hiện như một cách để duy trì một tham chiếu về thisthời điểm phạm vi sắp thay đổi (trong trường hợp đóng). Tôi không biết rằng tôi sẽ coi đó là một thói quen hay một khuôn mẫu xấu, không. Bạn thấy những điều tương tự rất nhiều với các thư viện như jQuery và rất nhiều khi làm việc với AJAX.


1

Tôi nghĩ rằng có một lập luận được đưa ra để luôn bao gồm var self = thistrong mọi phương pháp: yếu tố con người.

Nó đủ thường xuyên để bạn kết thúc với một mớ hỗn hợp các phương thức truy cập thisvà những phương thức khác sử dụngself cho cùng một mục đích. Nếu bạn di chuyển mã từ mã này sang mã khác, đột nhiên bạn gặp một loạt lỗi.

Đồng thời, tôi bắt gặp mình lơ đãng viết theo self.foothói quen khi chưa cần hoặc thêm dấu a var self = this. Vì vậy, tôi nghĩ rằng nó có thể có ý nghĩa nếu chỉ cần tạo thói quen luôn bao gồm nó, cần thiết hoặc không.

Rắc rối duy nhất là ... this, selfhoặc thattất cả là một vết đậu xấu xí trên mã của bạn và tôi thực sự ghét tất cả chúng. Vì vậy, tôi nghĩ rằng nó là tốt hơn để tránh sử dụng các phương pháp phân nếu có thể để bạn có thể tránh sử dụng this, thathoặc selfphần lớn thời gian, và sử dụng .bind(this)khi bạn khác có thể dùng đến self/that . Rất hiếm khi việc sử dụng các đại diện trên nguyên mẫu thực sự sẽ giúp bạn tiết kiệm bất kỳ lượng bộ nhớ đáng kể nào.

Một tác dụng phụ tốt đẹp của cách tiếp cận đó là bạn không cần phải thêm tiền tố cho tất cả các biến private của mình _, vì chúng sẽ thực sự là private và các thuộc tính public sẽ được gọi ra bởi phần đầu this., làm cho mã của bạn dễ đọc hơn.

Như bobince đã nói, var that = thistốt hơn vì nó không bóng window.self. self = thisđối với tôi nghe có vẻ ít khó xử hơn, nhưng đôi khi bạn nhận được thông báo lỗi khó hiểu như property myMethod not found on globaldo bạn quên self = thisdòng.


bạn có thể cung cấp jsfiddle với ý tưởng thay thế self bằng .bind (this) được không?
Sentenza

1
window.setTimeout (function () {this. $ btnAccept.tooltip ('show');} .bind (this), 256);
AlexanderB

1

Tôi chỉ muốn chỉ ra rằng 'self' tương đương với 'window', hãy thử xuất window === self vào bảng điều khiển. Bạn nên sử dụng mẫu này với 'that' hoặc một cái gì đó tương tự như một tên biến, tránh sử dụng 'self' vì nó đã được trình duyệt sử dụng (một sai lầm và bạn sẽ tự tạo cho mình một biến toàn cục). Mặc dù nghe có vẻ kỳ lạ, nhưng tốt hơn là sử dụng 'that' cho tên của nó vì các nhà phát triển khác sẽ ngay lập tức biết bạn đang cố gắng hoàn thành điều gì trong mã của mình, tránh sử dụng các tên biến không chuẩn. Tôi tin rằng đây là một lưu ý quan trọng, nhưng nó chỉ được đề cập trong một bình luận, vì vậy tôi muốn hiển thị nó rõ ràng hơn.


Cố gắng tạo một biến toàn cục trong trình duyệt bằng cách sử dụng đối tượng self và đăng một trò chơi. Trong trình duyệt của tôi (Chromium), điều này không hoạt động, vì nó sẽ xác định thuộc tính "self" trên "window" nếu bạn sử dụng "self = ..." mà không có "var".
Sentenza,

Tôi không chắc tại sao bạn lại tạo biến toàn cục mới có tên là 'self'. Trong JavaScript, chắc chắn bạn có thể sửa đổi chính JavaScript, nhưng bạn nên cực kỳ cẩn thận khi làm như vậy, bởi vì các nhà phát triển khác sẽ mong đợi một 'phiên bản chuẩn' của JavaScript khi bạn cung cấp cho họ mã của mình. Khi bạn gán một cái gì đó cho 'self', bạn thực sự đang ghi đè lên nó, tôi sẽ tránh làm điều đó. Tôi chỉ nói rằng khi bạn so sánh đối tượng window mặc định và self thì chúng mặc định là cùng một đối tượng. Nói cách khác, 'tự' và 'cửa sổ' là từ đồng nghĩa
Goran Vasic

(một sai lầm và bạn sẽ tự tạo cho mình một biến toàn cục)
Sentenza

1

Đã 6 năm sau, và tôi có một số điều cần bổ sung:

bind()bây giờ đã đủ phổ biến để được sử dụng ở mọi nơi. Tôi sử dụng nó thường xuyên như một sự thay thế. Đôi khi nó cảm thấy rõ ràng hơn. Tôi vẫn sử dụng trong dịp này var self = this;. Tuy nhiên.

Các chức năng mũi tên đang dần trở nên khả thi để sử dụng. Cú pháp ngắn hơn một chút, điều này khá hay, nhưng tôi nghĩ rằng tính năng tuyệt vời thực sự là theo mặc định, chúng luôn liên kết với phạm vi cha.

Điều này:

var self = this;
var foo = function(a) {

  return self.b + a;

};

Bây giờ có thể được viết là:

var foo = a => this.b + a;

Đây là cách sử dụng 'lạc quan nhất' của các hàm mũi tên, nhưng nó khá ngọt ngào.

Và chỉ để kết luận, không có gì sai với:

var self = this;

0

Tôi thích nó. Nó là "tự" -explanatory. Douglas Crockford có một số điều muốn nói về điều này. Ông nói rằng sử dụng "that" là quy ước. Bạn có thể xem Crockford miễn phí nếu bạn đến yui-Theater và xem các video của anh ấy về Javascript .


1
Cảm ơn phản hôi của bạn. Tôi nghĩ tôi hiểu ý bạn. Tuy nhiên, tôi không nghĩ rằng câu hỏi có thể được trả lời mà không cần bối cảnh. Tôi
tình cờ
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.