diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js
index 815b3fbeff465601aae35d5f6cc1f7a5aa66a420..bd9754e53a354f982084fd4eaf43800a0ab33738 100644
--- a/misc/wasm/wasm_exec.js
+++ b/misc/wasm/wasm_exec.js
@@ -95,6 +95,9 @@
 
 			const loadValue = (addr) => {
 				const f = mem().getFloat64(addr, true);
+				if (f === 0) {
+					return undefined;
+				}
 				if (!isNaN(f)) {
 					return f;
 				}
@@ -112,14 +115,18 @@
 						mem().setUint32(addr, 0, true);
 						return;
 					}
+					if (v === 0) {
+						mem().setUint32(addr + 4, nanHead, true);
+						mem().setUint32(addr, 1, true);
+						return;
+					}
 					mem().setFloat64(addr, v, true);
 					return;
 				}
 
 				switch (v) {
 					case undefined:
-						mem().setUint32(addr + 4, nanHead, true);
-						mem().setUint32(addr, 1, true);
+						mem().setFloat64(addr, 0, true);
 						return;
 					case null:
 						mem().setUint32(addr + 4, nanHead, true);
@@ -334,7 +341,7 @@
 			this._inst = instance;
 			this._values = [ // TODO: garbage collection
 				NaN,
-				undefined,
+				0,
 				null,
 				true,
 				false,
@@ -396,14 +403,14 @@
 		}
 
 		static _makeCallbackHelper(id, pendingCallbacks, go) {
-			return function() {
+			return function () {
 				pendingCallbacks.push({ id: id, args: arguments });
 				go._resolveCallbackPromise();
 			};
 		}
 
 		static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
-			return function(event) {
+			return function (event) {
 				if (preventDefault) {
 					event.preventDefault();
 				}
diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go
index 336586ca2dd1831d9e695d2d5fb2d9fd5a172fd7..9d826c38864f49a58c1aadbed77f8b4326f5a8c2 100644
--- a/src/syscall/js/js.go
+++ b/src/syscall/js/js.go
@@ -16,15 +16,17 @@ import (
 )
 
 // ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly.
-// A JavaScript number (64-bit float, except NaN) is represented by its IEEE 754 binary representation.
+//
+// The JavaScript value "undefined" is represented by the value 0.
+// A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation.
 // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as
 // an ID and bits 32-33 used to differentiate between string, symbol, function and object.
 type ref uint64
 
-// nanHead are the upper 32 bits of a ref which are set if the value is not a JavaScript number or NaN itself.
+// nanHead are the upper 32 bits of a ref which are set if the value is not encoded as an IEEE 754 number (see above).
 const nanHead = 0x7FF80000
 
-// Value represents a JavaScript value.
+// Value represents a JavaScript value. The zero value is the JavaScript value "undefined".
 type Value struct {
 	ref ref
 }
@@ -38,6 +40,9 @@ func predefValue(id uint32) Value {
 }
 
 func floatValue(f float64) Value {
+	if f == 0 {
+		return valueZero
+	}
 	if f != f {
 		return valueNaN
 	}
@@ -56,8 +61,9 @@ func (e Error) Error() string {
 }
 
 var (
+	valueUndefined = Value{ref: 0}
 	valueNaN       = predefValue(0)
-	valueUndefined = predefValue(1)
+	valueZero      = predefValue(1)
 	valueNull      = predefValue(2)
 	valueTrue      = predefValue(3)
 	valueFalse     = predefValue(4)
@@ -318,13 +324,18 @@ func (v Value) New(args ...interface{}) Value {
 func valueNew(v ref, args []ref) (ref, bool)
 
 func (v Value) isNumber() bool {
-	return v.ref>>32&nanHead != nanHead || v.ref == valueNaN.ref
+	return v.ref == valueZero.ref ||
+		v.ref == valueNaN.ref ||
+		(v.ref != valueUndefined.ref && v.ref>>32&nanHead != nanHead)
 }
 
 func (v Value) float(method string) float64 {
 	if !v.isNumber() {
 		panic(&ValueError{method, v.Type()})
 	}
+	if v.ref == valueZero.ref {
+		return 0
+	}
 	return *(*float64)(unsafe.Pointer(&v.ref))
 }
 
diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go
index 9cc931a31d38d99b3e465508fcc717410e555f70..ed39fe3714187a394e04673846e2c28ff877d8e3 100644
--- a/src/syscall/js/js_test.go
+++ b/src/syscall/js/js_test.go
@@ -22,6 +22,7 @@ var dummys = js.Global().Call("eval", `({
 	add: function(a, b) {
 		return a + b;
 	},
+	zero: 0,
 	NaN: NaN,
 })`)
 
@@ -74,6 +75,9 @@ func TestInt(t *testing.T) {
 	if dummys.Get("someInt") != dummys.Get("someInt") {
 		t.Errorf("same value not equal")
 	}
+	if got := dummys.Get("zero").Int(); got != 0 {
+		t.Errorf("got %#v, want %#v", got, 0)
+	}
 }
 
 func TestIntConversion(t *testing.T) {
@@ -237,6 +241,9 @@ func TestType(t *testing.T) {
 	if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want {
 		t.Errorf("got %s, want %s", got, want)
 	}
+	if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want {
+		t.Errorf("got %s, want %s", got, want)
+	}
 	if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want {
 		t.Errorf("got %s, want %s", got, want)
 	}
@@ -269,6 +276,13 @@ func TestValueOf(t *testing.T) {
 	}
 }
 
+func TestZeroValue(t *testing.T) {
+	var v js.Value
+	if v != js.Undefined() {
+		t.Error("zero js.Value is not js.Undefined()")
+	}
+}
+
 func TestCallback(t *testing.T) {
 	c := make(chan struct{})
 	cb := js.NewCallback(func(args []js.Value) {