events: make .listeners() return a copy
authorJoe Andaverde <joe@andaverde.net>
Fri, 20 Jul 2012 17:51:45 +0000 (12:51 -0500)
committerBen Noordhuis <info@bnoordhuis.nl>
Fri, 27 Jul 2012 18:28:51 +0000 (20:28 +0200)
Make EventEmitter.listeners(event) return a copy of the listeners array instead
of the array itself.

Fixes #3442.

doc/api/events.markdown
lib/events.js
test/simple/test-event-emitter-listeners.js [new file with mode: 0644]

index b9be5dc..692efa8 100644 (file)
@@ -64,9 +64,6 @@ Remove a listener from the listener array for the specified event.
 
 Removes all listeners, or those of the specified event.
 
-Note that this will **invalidate** any arrays that have previously been
-returned by `emitter.listeners(event)`.
-
 
 ### emitter.setMaxListeners(n)
 
@@ -85,19 +82,6 @@ Returns an array of listeners for the specified event.
     });
     console.log(util.inspect(server.listeners('connection'))); // [ [Function] ]
 
-This array **may** be a mutable reference to the same underlying list of
-listeners that is used by the event subsystem.  However, certain
-actions (specifically, removeAllListeners) will invalidate this
-reference.
-
-If you would like to get a copy of the listeners at a specific point in
-time that is guaranteed not to change, make a copy, for example by doing
-`emitter.listeners(event).slice(0)`.
-
-In a future release of node, this behavior **may** change to always
-return a copy, for consistency.  In your programs, please do not rely on
-being able to modify the EventEmitter listeners using array methods.
-Always use the 'on' method to add new listeners.
 
 ### emitter.emit(event, [arg1], [arg2], [...])
 
index c2b604f..9778960 100644 (file)
@@ -242,5 +242,5 @@ EventEmitter.prototype.listeners = function(type) {
   if (!isArray(this._events[type])) {
     this._events[type] = [this._events[type]];
   }
-  return this._events[type];
+  return this._events[type].slice(0);
 };
diff --git a/test/simple/test-event-emitter-listeners.js b/test/simple/test-event-emitter-listeners.js
new file mode 100644 (file)
index 0000000..3e62283
--- /dev/null
@@ -0,0 +1,52 @@
+
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var events = require('events');
+
+function listener() {}
+function listener2() {}
+
+var e1 = new events.EventEmitter();
+e1.on('foo', listener);
+var fooListeners = e1.listeners('foo');
+assert.deepEqual(e1.listeners('foo'), [listener]);
+e1.removeAllListeners('foo');
+assert.deepEqual(e1.listeners('foo'), []);
+assert.deepEqual(fooListeners, [listener]);
+
+var e2 = new events.EventEmitter();
+e2.on('foo', listener);
+var e2ListenersCopy = e2.listeners('foo');
+assert.deepEqual(e2ListenersCopy, [listener]);
+assert.deepEqual(e2.listeners('foo'), [listener]);
+e2ListenersCopy.push(listener2);
+assert.deepEqual(e2.listeners('foo'), [listener]);
+assert.deepEqual(e2ListenersCopy, [listener, listener2]);
+
+var e3 = new events.EventEmitter();
+e3.on('foo', listener);
+var e3ListenersCopy = e3.listeners('foo');
+e3.on('foo', listener2);
+assert.deepEqual(e3.listeners('foo'), [listener, listener2]);
+assert.deepEqual(e3ListenersCopy, [listener]);