Bagaimana cara menetapkan variabel global dalam CoffeeScript?

317

Di Coffeescript.org:

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

bawbag(5, 10) 

akan dikompilasi ke:

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

kompilasi melalui skrip kopi di bawah node.js membungkus itu jadi:

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

Documents katakan:

Jika Anda ingin membuat variabel tingkat atas untuk digunakan skrip lain, lampirkan sebagai properti di jendela, atau pada objek ekspor di CommonJS. Operator eksistensial (dibahas di bawah), memberi Anda cara yang andal untuk mencari tahu di mana menambahkannya, jika Anda menargetkan CommonJS dan browser: root = ekspor? ini

Bagaimana cara mendefinisikan Variabel Global lalu di CoffeeScript. Apa yang dimaksud dengan 'melampirkannya sebagai properti di jendela'?

Handloomweaver
sumber
4
Perhatikan bahwa menggunakan variabel global itu buruk, c2.com/cgi/wiki?GlobalVariablesAreBad , dan bahkan dianggap berbahaya, c2.com/cgi/wiki?GotoConsideredHarmful . Dan benar-benar tidak ada alasan untuk menggunakannya dalam JavaScript sama sekali, karena Anda memiliki fitur hebat seperti penutupan yang dapat memecahkan sebagian besar masalah yang Anda gunakan untuk menyelesaikan variabel global.
Evgeny
9
@ Evgeny Sementara saya setuju dengan Anda di sini, dalam beberapa kasus itu perlu untuk membuat objek 'aplikasi' pusat dan memiliki modul yang melekat padanya.
jackyalcine
1
objek sentral dapat disimpan ke dalam objek kondisi global yang ada, seperti windowobjek atau exportsobjek. tidak perlu membuat variabel global.
Evgeny
9
@Evgeny variabel global disimpan sebagai properti dari objek window(atau globalpada nodejs)
shesek
21
Ya, tidak buruk untuk memiliki var global. Hanya praktik buruk untuk menghentikan aplikasi Anda tanpa berpikir panjang. Mendeklarasikan satu dan menggunakannya sebagai pabrik adaptor seperti jQuery atau semacam namespace adalah praktik yang sangat umum.
Erik Reppen

Jawaban:

419

Karena skrip kopi tidak memiliki varpernyataan, skrip kopi secara otomatis memasukkannya ke semua variabel dalam skrip kopi, sehingga mencegah versi JavaScript yang dikompilasi dari membocorkan semuanya ke namespace global .

Jadi karena tidak ada cara untuk membuat sesuatu "bocor" ke ruang nama global dari sisi naskah kopi hal-hal yang disengaja, Anda perlu mendefinisikan variabel global Anda sebagai properti dari objek global .

lampirkan sebagai properti di jendela

Ini berarti Anda perlu melakukan sesuatu seperti window.foo = 'baz';, yang menangani kasus browser, karena di sana objek global adalah window.

Node.js

Di Node.js tidak ada windowobjek, melainkan ada exportsobjek yang dilewatkan ke pembungkus yang membungkus modul Node.js (Lihat: https://github.com/ry/node/blob/master/src/node.js# L321 ), jadi di Node.js apa yang perlu Anda lakukan adalah exports.foo = 'baz';.

Sekarang mari kita lihat apa yang tercantum dalam kutipan Anda dari dokumen:

... menargetkan CommonJS dan browser: root = ekspor? ini

Ini jelas skrip kopi, jadi mari kita lihat apa yang sebenarnya dikompilasi:

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

Pertama akan memeriksa apakah exportsdidefinisikan, karena mencoba referensi variabel yang tidak ada dalam JavaScript akan menghasilkan SyntaxError (kecuali ketika digunakan dengan typeof)

Jadi jika exportsada, yang merupakan kasus di Node.js (atau dalam WebSite yang ditulis dengan buruk ...) root akan menunjuk ke exports, sebaliknya this. Jadi apa this?

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

Menggunakan .callpada suatu fungsi akan mengikat bagian thisdalam fungsi ke parameter pertama yang dilewati, dalam hal browser thissekarang akan menjadi windowobjek, dalam kasus Node.js itu akan menjadi konteks global yang juga tersedia sebagai globalobjek.

Tetapi karena Anda memiliki requirefungsi di Node.js, Anda tidak perlu menetapkan sesuatu ke globalobjek di Node.js, sebagai gantinya Anda menetapkan ke exportsobjek yang kemudian dikembalikan oleh requirefungsi.

Kopi-Naskah

Setelah semua penjelasan itu, inilah yang perlu Anda lakukan:

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

Ini akan mendeklarasikan fungsi kita foodi namespace global (apa pun yang terjadi).
Itu saja :)

Ivo Wetzel
sumber
1
@IvoWetzel - Apa perbedaan antara global, GLOBALdan rootbenda-benda di node.js?
Aadit M Shah
1
mencoba referensi variabel yang tidak ada dalam JavaScript kalau tidak akan menghasilkan SyntaxError Bukankah maksud Anda ReferenceError?
alex
12
Atau bahkan lebih pendek:(exports ? this).foo = -> 'Hello World'
Dane O'Connor
3
this.foo sering! = window.foo meskipun jika Anda sudah konteks ini sudah objek. Ini adalah sintaks yang membingungkan.
Kevin
1
Sementara saya setuju dengan penggunaan global = exports ? this. Klaim bahwa "dalam kasus Node.js itu akan menjadi konteks global ..." salah karena thisvariabel, ketika diperlukan atau dijalankan oleh node.js, dievaluasi sebagai cakupan modul. Jadi, jika Anda mengharapkan pengaturan alat peraga akan membuatnya dapat diakses secara global, Anda akan kecewa. Jika Anda ingin mengatur sesuatu secara global dalam konteks node.js, Anda perlu menggunakan globalvariabel, bukan this.
KFL
58

Bagi saya sepertinya @atomicules memiliki jawaban yang paling sederhana, tapi saya pikir ini bisa disederhanakan sedikit lagi. Anda harus meletakkan @sebelum apa pun yang Anda ingin menjadi global, sehingga ia mengkompilasi this.anythingdan thismengacu pada objek global.

begitu...

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

bawbag(5, 10)

mengkompilasi ke ...

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

dan bekerja di dalam dan di luar pembungkus yang diberikan oleh 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
Billy Moon
sumber
7
Tetapi ini tidak akan berhasil jika Anda sudah berada dalam ruang lingkup yang lain bukan? Karena itu thistidak lagi mengacu pada objek global
Sherwin Yu
1
Itu benar, sehingga Anda bisa mendefinisikan variabel Anda dalam lingkup yang sesuai (dan menggunakannya dalam orang lain), atau menentukan window.myVariableyang akan bekerja di mana saja.
Billy Moon
2
Anda tidak perlu mendefinisikan variabel lain cukup gunakan =>alih-alih ->yang menginstruksikan naskah kopi untuk membuat fungsi di bawah namespace global ini
Ricardo Villamil
2
ini sangat membantu, sekarang saya dapat membuat objek dan fungsi global dalam skrip kopi terpisah
Diego Fernando Murillo Valenci
Ini jauh lebih baik. Mentransfer JS ke CS membutuhkan saya untuk mengubah banyak pemanggilan fungsi untuk menggunakan objek jendela, sekarang saya dapat mengembalikannya
casraf
33

Ivo berhasil, tetapi saya akan menyebutkan bahwa ada satu trik kotor yang dapat Anda gunakan, meskipun saya tidak merekomendasikannya jika Anda mencari poin gaya: Anda dapat menyematkan kode JavaScript langsung di CoffeeScript Anda dengan menghindarinya dengan backticks.

Namun, inilah mengapa ini biasanya ide yang buruk: Kompiler CoffeeScript tidak mengetahui variabel-variabel tersebut, yang berarti mereka tidak akan mematuhi aturan pelingkupan CoffeeScript yang normal. Begitu,

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

kompilasi ke

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

dan sekarang Anda memiliki dua foos dalam lingkup yang berbeda. Tidak ada cara untuk mengubah global foo dari kode CoffeeScript tanpa merujuk objek global, seperti yang dijelaskan Ivy.

Tentu saja, ini hanya masalah jika Anda membuat tugas foodi CoffeeScript — jika foomenjadi read-only setelah diberi nilai awal (yaitu konstanta global), maka pendekatan solusi JavaScript yang disematkan mungkin agak dapat diterima (meskipun masih tidak direkomendasikan).

Trevor Burnham
sumber
1
Ini adalah solusi yang berguna bagi saya karena saya menggunakan Titanium dengan CoffeeScript. Ekspor dan objek jendela tidak ada di sana.
Dermaga-Olivier Thibault
Sebenarnya itu hanya satu foovariabel lokal , karena varmengangkat (JS memindai ke depan untuk semua vardeklarasi dan menafsirkannya seolah-olah mereka berada di atas fungsi)
Kornel
@porneL Anda benar; Saya mengambil contoh yang buruk. Intinya adalah bahwa kompiler CoffeeScript tidak melakukan analisis backtick-escaped JavaScript, sehingga Anda bisa mendapatkan output yang aneh.
Trevor Burnham
2
@ Pier-OlivierThibault Jika Anda ingin menggunakan Globals di Titanium, Anda dapat menggunakan Ti.App.myGlobalVar = "ImAGlobalVar" dan tidak perlu backticks
Jakob Lnr
inilah jawaban yang benar, setidaknya untuk Node.js. melakukan expect = require('chai').expect;membuat expectvariabel tersedia di semua file pengujian saya!
pocesar
11

Anda bisa meneruskan opsi -b ketika Anda mengkompilasi kode melalui coffee-script di bawah node.js. Kode yang dikompilasi akan sama dengan di coffeescript.org.

phongnh
sumber
Bagaimana? Di mana saya meletakkan opsi -b?
Harry
1
@ Harry - -b/ --barelangsung setelah coffeeperintah.
ocodo
9

Untuk menambah jawaban Ivo Wetzel

Tampaknya ada sintaks steno untuk exports ? thisitu saya hanya dapat menemukan didokumentasikan / disebutkan pada posting grup Google .

Yaitu di halaman web untuk membuat fungsi tersedia secara global Anda mendeklarasikan fungsi lagi dengan @awalan:

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

<a href="javascript:aglobalfunction()" >Click me!</a>
atomicules
sumber
9
'@' Di @aglobalfunction diganti dengan 'this.', Jadi kompilasi ke 'this.aglobalfunction'. Ini berfungsi karena ruang lingkup fungsi pembungkus kopi (jika diterapkan) adalah lingkup global.
Chris
9

Saya pikir apa yang Anda coba capai dapat dengan mudah dilakukan seperti ini:

Saat Anda mengompilasi naskah kopi, gunakan parameter "-b".

-b/ --bare Kompilasi JavaScript tanpa pembungkus fungsi keselamatan tingkat atas.

Jadi sesuatu seperti ini: coffee -b --compile somefile.coffee whatever.js

Ini akan menampilkan kode Anda seperti di situs CoffeeScript.org.

Sankalp Singha
sumber
7

Jika Anda orang jahat (saya orang jahat.), Anda bisa sesederhana ini: (->@)()

Seperti dalam,

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

Ini berfungsi, karena ketika memanggil a Referenceke Function'bare' (yaitu func(),, bukannya new func()atau obj.func()), sesuatu yang biasa disebut sebagai 'pola panggilan fungsi-panggilan', selalu mengikat thiske objek global untuk konteks eksekusi itu .

CoffeeScript di atas hanya mengkompilasi ke (function(){ return this })(); jadi kami menjalankan perilaku itu untuk secara andal mengakses objek global.

DAPAT DILAKUKAN
sumber
Ini brilian!
metalim
Satu-satunya hal yang berhasil untuk saya. Benci CoffeeScript.
pcv
Love CoffeeScript. Sejauh ini adalah bahasa pemrograman terbaik di luar sana. Sayang sekali ia diciptakan dan dipertahankan sebagai proyek hobi, yang mengarah ke kekacauan dan kebodohan dalam pola penggunaannya.
metalim
3

Karena naskah kopi jarang digunakan sendiri, Anda dapat menggunakan globalvariabel yang disediakan oleh node.js atau browserify (dan keturunan apa pun seperti coffeeify, skrip pembuatan gulp, dll).

Di node.js globaladalah namespace global.

Di browserify globalsama dengan window.

Jadi hanya:

somefunc = ->
  global.variable = 123
metalim
sumber