Làm cách nào để xác định các biến toàn cục trong CoffeeScript?


317

Trên Coffeescript.org:

bawbag = (x, y) ->
    z = (x * y)

bawbag(5, 10) 

sẽ biên dịch thành:

var bawbag;
bawbag = function(x, y) {
  var z;
  return (z = (x * y));
};
bawbag(5, 10);

biên dịch thông qua tập lệnh cà phê dưới tệp node.js kết thúc như vậy:

(function() {
  var bawbag;
  bawbag = function(x, y) {
    var z;
    return (z = (x * y));
  };
  bawbag(5, 10);
}).call(this);

Tài liệu nói:

Nếu bạn muốn tạo các biến cấp cao nhất cho các tập lệnh khác để sử dụng, hãy đính kèm chúng làm thuộc tính trên cửa sổ hoặc trên đối tượng xuất khẩu trong CommonJS. Toán tử hiện sinh (được trình bày bên dưới), cung cấp cho bạn một cách đáng tin cậy để tìm ra nơi thêm chúng, nếu bạn đang nhắm mục tiêu cả CommonJS và trình duyệt: root = export? điều này

Làm cách nào để xác định Biến toàn cầu sau đó trong CoffeeScript. "Gắn chúng làm tài sản trên cửa sổ" nghĩa là gì?


4
Xin lưu ý rằng việc sử dụng các biến toàn cục là xấu, c2.com/cgi/wiki?GlobalVariablesAreBad và thậm chí được coi là có hại, c2.com/cgi/wiki?GotoConsideredHarmful . Và thực sự không có lý do gì để sử dụng chúng trong JavaScript cả, vì bạn có các tính năng tuyệt vời như các bao đóng có thể giải quyết hầu hết các vấn đề bạn đang sử dụng các biến toàn cục để giải quyết.
Evgeny

9
@Evgeny Mặc dù tôi đồng ý với bạn ở đây, trong một số trường hợp, cần phải tạo một đối tượng 'ứng dụng' trung tâm và có các mô-đun được đính kèm.
jackyalcine

1
các đối tượng trung tâm có thể được lưu vào các đối tượng trạng thái toàn cầu hiện có, như windowđối tượng hoặc exportsđối tượng. không cần tạo các biến toàn cục.
Evgeny

9
Các biến toàn cục @Evgeny được lưu dưới dạng các thuộc tính của đối tượng window(hoặc globaltrên nodejs)
shesek

21
Vâng, nó không phải là xấu khi có một var toàn cầu. Chỉ cần thực hành xấu để vòi ứng dụng của bạn xuống với họ một cách thiếu suy nghĩ. Khai báo một và sử dụng nó như một nhà máy sản xuất bộ điều hợp như jQuery hoặc một loại không gian tên nào đó thực sự phổ biến.
Erik Reppen

Câu trả lời:


419

Vì tập lệnh cà phê không có varcâu lệnh, nó sẽ tự động chèn nó cho tất cả các biến trong tập lệnh cà phê, theo cách đó nó ngăn phiên bản JavaScript được biên dịch rò rỉ mọi thứ vào không gian tên toàn cục .

Vì vậy, vì không có cách nào để làm cho một cái gì đó "rò rỉ" vào không gian tên toàn cầu từ phía kịch bản cà phê của những thứ có chủ đích, bạn cần xác định các biến toàn cục của mình là thuộc tính của đối tượng toàn cầu .

đính kèm chúng như các thuộc tính trên cửa sổ

Điều này có nghĩa là bạn cần phải làm một cái gì đó như window.foo = 'baz';, xử lý trường hợp trình duyệt, vì có đối tượng toàn cầuwindow.

Node.js

Trong Node.js không có windowđối tượng, thay vào đó, có exportsđối tượng được chuyển vào trình bao bọc bọc mô-đun Node.js (Xem: https://github.com/ry/node/blob/master/src/node.js# L321 ), vì vậy trong Node.js, điều bạn cần làm là exports.foo = 'baz';.

Bây giờ chúng ta hãy xem những gì nó nêu trong trích dẫn của bạn từ các tài liệu:

... nhắm mục tiêu cả CommonJS và trình duyệt: root = export? điều này

Đây rõ ràng là kịch bản cà phê, vì vậy chúng ta hãy xem những gì nó thực sự biên dịch thành:

var root;
root = (typeof exports !== "undefined" && exports !== null) ? exports : this;

Đầu tiên, nó sẽ kiểm tra xem có exportsđược xác định hay không, vì cố gắng tham chiếu một biến không tồn tại trong JavaScript sẽ mang lại một SyntaxError (trừ khi nó được sử dụng với typeof)

Vì vậy, nếu exportstồn tại, đó là trường hợp trong Node.js (hoặc trong một WebSite được viết xấu ...) sẽ chỉ đến exports, nếu không thì this. Vậy là thissao

(function() {...}).call(this);

Sử dụng .calltrên một hàm sẽ liên kết thisbên trong hàm với tham số đầu tiên được truyền, trong trường hợp trình duyệt thisbây giờ sẽ là windowđối tượng, trong trường hợp Node.js, đó sẽ là bối cảnh toàn cầu cũng có sẵn dưới dạng globalđối tượng.

Nhưng vì bạn có requirehàm trong Node.js, nên không cần gán một cái gì đó cho globalđối tượng trong Node.js, thay vào đó bạn gán cho exportsđối tượng mà sau đó được hàm trả về require.

Cà phê-Script

Sau tất cả những lời giải thích đó, đây là những gì bạn cần làm:

root = exports ? this
root.foo = -> 'Hello World'

Điều này sẽ khai báo chức năng của chúng ta footrong không gian tên toàn cầu (bất cứ điều gì xảy ra).
Đó là tất cả :)


1
@IvoWetzel - phần chênh lệch giữa là gì global, GLOBALrootcác đối tượng trong Node.js?
Aadit M Shah

1
cố gắng tham chiếu một biến không tồn tại trong JavaScript nếu không sẽ mang lại một SyntaxError Ý bạn là ReferenceErrorsao?
alex

12
Hoặc thậm chí ngắn hơn:(exports ? this).foo = -> 'Hello World'
Dane O'Connor

3
this.foo thường là! = window.foo mặc dù nếu bạn 'bối cảnh' này đã là một đối tượng. Đây là một cú pháp khó hiểu.
Kevin

1
Trong khi tôi đồng ý với việc sử dụng global = exports ? this. Khiếu nại rằng "trong trường hợp Node.js, đó sẽ là bối cảnh toàn cầu ..." là sai vì thisbiến, khi được yêu cầu hoặc chạy bởi node.js, được đánh giá là phạm vi mô-đun. Vì vậy, nếu bạn mong đợi cài đặt đạo cụ cho nó sẽ khiến nó có thể truy cập được trên toàn cầu, bạn sẽ thất vọng. Nếu bạn muốn đặt mọi thứ trên toàn cầu trong ngữ cảnh node.js, bạn cần sử dụng globalbiến, thay vì this.
KFL

58

Đối với tôi có vẻ như @atomicules có câu trả lời đơn giản nhất, nhưng tôi nghĩ nó có thể được đơn giản hóa hơn một chút. Bạn cần đặt @trước bất cứ thứ gì bạn muốn là toàn cầu, để nó biên dịch this.anythingthisđề cập đến đối tượng toàn cầu.

vì thế...

@bawbag = (x, y) ->
    z = (x * y)

bawbag(5, 10)

biên dịch thành ...

this.bawbag = function(x, y) {
  var z;
  return z = x * y;
};
bawbag(5, 10);

và hoạt động bên trong và bên ngoài trình bao bọc được cung cấp bởi node.js

(function() {
    this.bawbag = function(x, y) {
      var z;
      return z = x * y;
    };
    console.log(bawbag(5,13)) // works here
}).call(this);

console.log(bawbag(5,11)) // works here

7
Nhưng điều này sẽ không hoạt động nếu bạn đã ở trong một phạm vi khác phải không? Bởi vì sau đó thiskhông còn đề cập đến đối tượng toàn cầu
Sherwin Yu

1
Điều đó là chính xác, vì vậy bạn có thể xác định biến của mình trong một phạm vi thích hợp (và sử dụng nó trong các phạm vi khác) hoặc xác định biến window.myVariableđó sẽ hoạt động ở bất cứ đâu.
Billy Moon

2
Bạn không cần xác định một biến khác chỉ cần sử dụng =>thay vì ->hướng dẫn coffeescript để tạo hàm theo không gian tên toàn cầu / này
Ricardo Villamil

2
điều này rất hữu ích, bây giờ tôi có thể tạo các đối tượng và chức năng toàn cầu trong một kịch bản cà phê riêng
Diego Fernando Murillo Valenci

Điều này tốt hơn rất nhiều. Chuyển JS sang CS cần tôi thay đổi rất nhiều lệnh gọi hàm để sử dụng đối tượng cửa sổ, bây giờ tôi có thể hoàn nguyên điều đó
casraf

33

Ivo đóng đinh nó, nhưng tôi sẽ đề cập rằng có một mẹo bẩn mà bạn có thể sử dụng, mặc dù tôi không khuyên bạn nên sử dụng điểm phong cách: Bạn có thể nhúng mã JavaScript trực tiếp vào CoffeeScript bằng cách thoát khỏi nó bằng backticks.

Tuy nhiên, đây là lý do tại sao đây thường là một ý tưởng tồi: Trình biên dịch CoffeeScript không biết về các biến đó, điều đó có nghĩa là chúng sẽ không tuân theo các quy tắc phạm vi CoffeeScript thông thường. Vì thế,

`foo = 'bar'`
foo = 'something else'

biên dịch thành

foo = 'bar';
var foo = 'something else';

và bây giờ bạn đã có cho mình hai foos trong phạm vi khác nhau. Không có cách nào để sửa đổi toàn cục foo từ mã CoffeeScript mà không tham chiếu đến đối tượng toàn cầu, như Ivy mô tả.

Tất nhiên, đây chỉ là vấn đề nếu bạn thực hiện chuyển nhượng footrong CoffeeScript, nếu footrở thành chỉ đọc sau khi được cung cấp giá trị ban đầu (nghĩa là hằng số toàn cầu), thì cách tiếp cận giải pháp JavaScript nhúng có thể được chấp nhận (mặc dù vậy không được khuyến khích).


1
Đây là một giải pháp hữu ích cho tôi vì tôi đang sử dụng Titanium với CoffeeScript. Xuất khẩu và đối tượng cửa sổ là không tồn tại ở đó.
Cầu tàu-Olivier Thibault

Trên thực tế, đó chỉ là một foobiến cục bộ, bởi vì varcẩu (JS quét trước tất cả các varkhai báo và diễn giải chúng như thể chúng ở trên cùng của hàm)
Kornel

@yheL Bạn đúng rồi; Tôi đã chọn một ví dụ xấu. Vấn đề là trình biên dịch CoffeeScript thực hiện không có phân tích về JavaScript thoát backtick, do đó bạn có thể nhận được đầu ra lẻ.
Trevor Burnham

2
@ Pier-OlivierThibault Nếu bạn muốn sử dụng Globals trong Titanium, bạn có thể sử dụng Ti.App.myGlobalVar = "ImAGlobalVar" và không cần backticks
Jakob Lnr

đây là câu trả lời đúng, ít nhất là cho Node.js. làm expect = require('chai').expect;cho expectbiến có sẵn trong tất cả các tập tin thử nghiệm của tôi!
pocesar

11

Bạn có thể truyền tùy chọn -b khi bạn biên dịch mã qua tập lệnh cà phê dưới tệp node.js. Mã được biên dịch sẽ giống như trên coffeescript.org.


Làm sao? Tôi đặt tùy chọn -b ở đâu?
Harry

1
@Harry - -b/ --baređi trực tiếp sau coffeelệnh.
ocodo

9

Để thêm vào câu trả lời của Ivo Wetzel

Dường như có một cú pháp tốc ký exports ? thismà tôi chỉ có thể tìm thấy tài liệu / được đề cập trên một bài đăng của nhóm Google .

Tức là trong một trang web để cung cấp một chức năng có sẵn trên toàn cầu, bạn khai báo lại chức năng đó với @tiền tố:

<script type="text/coffeescript">
    @aglobalfunction = aglobalfunction = () ->
         alert "Hello!"
</script>

<a href="javascript:aglobalfunction()" >Click me!</a>

9
'@' Trong @aglobalfalf chỉ đơn giản được thay thế bằng 'this.', Vì vậy, biên dịch thành 'this.aglobalfloyment'. Điều này hoạt động vì phạm vi của hàm bao bọc coffeescript (nếu được áp dụng) là phạm vi toàn cục.
Chris

9

Tôi nghĩ những gì bạn đang cố gắng để đạt được có thể được thực hiện như thế này:

Trong khi bạn biên dịch coffeescript, hãy sử dụng tham số "-b".

-b/ --bare Biên dịch JavaScript mà không có trình bao bọc an toàn chức năng cấp cao nhất.

Vì vậy, một cái gì đó như thế này: coffee -b --compile somefile.coffee whatever.js

Điều này sẽ xuất mã của bạn giống như trong trang web CoffeeScript.org.


7

Nếu bạn là người xấu (Tôi là người xấu.), Bạn có thể đơn giản như thế này: (->@)()

Như trong

(->@)().im_a_terrible_programmer = yes
console.log im_a_terrible_programmer

Công trình này, bởi vì khi gọi một Referenceđến một Function'trần' (có nghĩa là, func()thay vì new func()hay obj.func()), một cái gì đó thường được gọi là 'chức năng gọi gọi mẫu', luôn với phím tắt thischo đối tượng toàn cầu cho rằng bối cảnh thực hiện .

CoffeeScript ở trên chỉ đơn giản biên dịch thành (function(){ return this })(); vì vậy chúng tôi đang thực hiện hành vi đó để truy cập một cách đáng tin cậy vào đối tượng toàn cầu.


Thật là tuyệt vời!
metalim

Điều duy nhất làm việc cho tôi. Ghét CoffeeScript.
pcv

Yêu cà phê. Cho đến nay nó là ngôn ngữ lập trình tốt nhất hiện có. Quá tệ, nó đã được tạo ra và duy trì như một dự án sở thích, dẫn đến sự hỗn loạn và ngu ngốc trong mô hình sử dụng của nó.
metalim

3

Vì coffeescript hiếm khi được sử dụng riêng, nên bạn có thể sử dụng globalbiến được cung cấp bởi node.js hoặc browserify (và bất kỳ hậu duệ nào như coffeeify, gulp build script, v.v.).

Trong node.js globallà không gian tên toàn cầu.

Trong browserify globallà bằng window.

Vì vậy chỉ cần:

somefunc = ->
  global.variable = 123
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.