Download
ProxyListener.jsAbout
ProxyListener.js is a library for observing changes for any data types of object properties, such as object, array, function, etc .
It uses Proxy API and Object.defineProperty method, you can detect changes easily by creating a listener.
Compatibility
By using proxy-polyfill, it can works with IE9+, Chrome5+, Firefox4+, Opera11.6+, Node.js.
Index
-
- Basic
- Advanced
- new ProxyListener().proxyListen(object,address,funSet,propSet).subscribe(callback(passing))
- subscribe(callback(passing))
- Delete & Redefine callback function
- Create multiple listeners at once
- new ProxyListener().proxyListenGroup(object, addressArray, funSet,propSet).subscribeGroup (callback(passing))
- Combine with Rxjs
- Compatible with target's getter/setter
-
- Using ProxyListener.js with HTML script tag
- Avoid infinite Loop
- Detect changes or execution for asynchronous function type target
- Deeply Detect changes for array type target
- Deeply detect changes for object type target
- Detect view data changes in Vue component
- Detect instance's methods's execution
- Application in Composite pattern
Installing
HTML Script TAG
Via NPM
npm install proxylistenerjs
Import as ECMA2015 module
Require
var ProxyListener = //require es2015 versionvar ProxyListener = //require compatible version for browservar ProxyListener = //require compatible version for node.js
Usage
Basic
Initialization
var pListener =
Create an object which contains listening target
var object = word: 'word' //the listening target is word property
Create a listener
var objectListener = pListener
Define callback function
var objectSubscription = objectListener
Trigger callback function
object.word = 'new'
/**
* console output: detect changes
*/
Advanced
new ProxyListener().proxyListen (object, address, funSet,propSet).subscribe(callback(passing))
- ### proxyListen (object, address, funSet,propSet)
Options:
-
object: :
object
: necessary — target's parent object. -
address:
string
: necessary — target's location. (example: 'path', 'path1/path2') -
funSet:
object
: optional — settings for function type target.-
sub options:
-
exePos:
string
(default value: 'after') — position for callback function executing . --values:
---'before': callback function execute before the target function's execution.
---'after': callback function execute after the target function's execution.
---'both': callback function execute before and after the target function's execution.
-
funRepListen:
boolean
(default value: false) — whether to execute callback function after the target function is replaced by another function. -
isAsync:
boolean
(default value: false) — whether to execute callback function asynchronously after the target function's execution when target function is asynchronous.
-
-
-
propSet:
object
:optional — general listening settings.-
sub options:
-
thisArgs:
object
— the this argument for validator function's execution. -
validator:
function:boolean
— a function for intercepting the target's changes or the target function's execution by returning a boolean.Listener will pass an object when validator function is excuted, the object contains:
---locatePath : the location of the target's changing property.
---method: the way that the target changes.
---val: the data that will be added to the target's changing property.
For more detail, see the description of Passing Object's Properties
-
isTrigger:
boolean
(default value: false) — whether to execute listener's callback function when validator function return false. -
change:
object
— setting for listener passing object when callback function execute.- sub options:
- isPassOldValue:
boolean
(default value: false) — whether to pass old value when execute callback function. Notice: The true value setting may lower performance. - defaultValue:
string
(default value: undefined) — custom value for listener passing when callback function execute.
- isPassOldValue:
- sub options:
-
deepListenLv :
number
|string
(default value: 0) — the additional levels for enumerable properties listening for the target , by default, 1 level nested properties can be listened for the target. --values:
---1~?
number
: add additional levels for the target's properties listening. ---'max'
string
: listen all level of nested enumerable properties of the target -
coverSet:
object
— settings for the listener's reaction when the listener is covered, by default, a target's listener can be covered by creating duplicately.- sub options:
- isCanCover:
boolean
(default value: true) — whether the listener can be covered, if not, error function will execute when the listener is being covered. - errFunc:
function
— a function for throwing error whenisCanCover
's value is false.
- isCanCover:
- sub options:
-
funcListenSet:
object
— settings for the listener's reaction when function type properties execute.- sub options:
-
listenOn:
boolean
(default value: false) -
exePos:
string
(default value: 'after') — position for callback function executing . --values:
---'before': callback function execute before the target's function type properties's execution.
---'after': callback function execute after the target's function type properties's execution.
---'both': callback function execute before and after the target function's execution.
-
isAsync:
boolean
(default value: false) — whether to execute callback function asynchronously after the target's asynchronous function type properties's execution. -
instanceMethodOn :
boolean
|object
(default value: false) — whether to execute callback function partially or completely when the function type property is a instance's method. --values:
---true: detect all the execution of instances's methods within the target.
---{include: ['ClassOne', {class: 'ClassTwo', method: ['methodOne']}], notInclude:['ClassThree', {class: 'ClassFour', method: ['methodOne']}]}
-- sub options:
--- include: setting for specific class type instance or instance's methods can be detected after execution. For detecting all methods of a specific class type instance, use
{include:['ClassOne']}
, for detecting part of methods of a specific class type instance, use{include:[ {class: 'ClassFour', method: ['methodOne']}]}
. --- notInclude: setting for all the execution of instances's methods can be detected except for a few specific class type instances. The way of setting is the same as the
include
property. When you set a specific class in bothinclude
andnotInclude
property, the specific class's setting innotInclude
property will be invalid.Notice: The instanceMethodOn setting may lower performance.
-
- sub options:
-
-
-
### subscribe(callback(passing))
When callback function execute, listener will pass a parameters object . According to listener options and situation of target's changes, the passing object's properties will change responsively:
Passing Object's Properties:
-
passing.glob:
object
|function
|array
|string
|null
|boolean
|number
|undefined
— access the primitive target which without detecting changes. -
passing.locatePath :
string
— whatever a property is existing or new in target object or target array, listener will pass the location of this property when callback function execute. -
passing.method :
string
— ('assign'|'update'|'function'|array method's name) — the way that the target changes. --values:
---'assign': target or target's properties is replaced.
---'update' : detect changes of properties of target's array type properties.
---'function': target's function type properties
---array method's name: detect changes of array type target or target's array type properties with array methods.
-
passing.oldValue :
object
|function
|array
|string
|null
|boolean
|number
|undefined
— whenisPassOldValue
's value is true, listener will pass the past status of target or target's properties. -
passing.newValue :
object
|function
|array
|string
|null
|boolean
|number
|undefined
— listener will pass the undated status of target or target's properties.
Delete & Redefine callback function
When create listener and define callback function separately, you can keep the listener and delete callback function with unsubscribe function.
When you need to detect changes for the same target again, just redefine callback function without creating a listener duplicately.
Delete callback function
objectSubscription
Redefine callback function
var objectReSubscription = objectListener
Create multiple listeners at once
If you want to create multiple listeners quickly, you can use proxyListenGroup
function after initialization.
new ProxyListener().proxyListenGroup(object, addressArray, funSet,propSet).subscribeGroup (callback(passing))
Options:
- addressArray:
array
— array of targets's locations. - other options are equal to proxyListen function.
Delete callback function
use unsubscribeGroup
function to delete multiple listener's callback function
objectSubscription
Combine with Rxjs
You can add a Observable callback function to listener using Rxjs.
;// initialize with rxjsvar pListener = Subjectvar control = listen: word: 'word' // setting callback function with Observable methodspListener // trigger listener's detectioncontrollistenword = 'new'/* console output: * print listen/word changes once * print listen/word changes twice */
Compatible with target's getter/setter
If taeget has getter/setter before creating listener, the listener will keep them rather than covering them.
Examples
Using ProxyListener.js with HTML script tag
<script type="text/javascript" src="proxylistener.min.js"></script>
<script>
var pListener = new ProxyListener()
var object = {
word: 'word' //the listening target is word property
}
var objectListener = pListener.proxyListen(object, 'word')
var objectSubscription = objectListener.subscribe(function (x) {
console.log('detect changes');
})
object.word = 'new'
</script>
Avoid infinite Loop
If you change the object, array, function type target in it's callback function but don't want to execute the callback function again , just access target's glob property to change target
var pListener = var object = target: word: 'word' var objectListener = pListener var objectSubscription = objectListener objecttargetword = 'new' console; //second change /** * console output: * detect cahnges * second change */
Detect changes or execution for asynchronous function type target
var pListener = var object = { console; return { ; } } { console; return { ; } } var oneListener = pListener var twoListener = pListener //execute callback function synchronously oneListener //execute callback function asynchronously twoListener object object /* console output: * asyncFunctionTwo * asyncFunctionTwo's execution is finish * asyncFunctionOne * asyncFunctionTwo is still executing * asyncFunctionOne is still executing * asyncFunctionOne's execution is finish */
Deeply Detect changes for array type target
var pListener = var object = array: 0 1 2 3 3 2 1 0 var arrayListener = pListener var arraySubscription = arrayListener // change target by array method objectarray0 // change target by property assignment objectarray3 = 1 2 3 /* console output: * location is array/0 * method is reverse * location is array/3 * method is update */
Deeply detect changes for object type target
var pListener = var object = targetZero: { console; } array: 0 1 2 3 3 2 1 0 targetMax: {} targetMultiple: targetOne: word: 'One' targetTwo: word: 'Two' var lvZeroListener = pListener var lvMaxListener = pListener lvZeroListener lvMaxListener var multipleListener = pListener multipleListener objecttargetZero objecttargetZeroarray objecttargetZeronested = content: 'nested' // new property can't be added to target because validator return false objecttargetMaxnested = content: 'nested' console; // undefined // new property can be added to target because validator return true objecttargetZeronested = word: 'nested' // callback function can be triggered because taget's listen level is max objecttargetMaxnestedcontent = 'new'; // callback function can't be triggered because taget's listen level is 0 objecttargetZeronestedword = 'new'; objecttargetMultipletargetOneword = 'new'; //execute the same callback function as targetOne objecttargetMultipletargetTwoword = 'new'; /* console output: * this is targetZero * validator return false * location is targetMax/nested * method is assign * undefined * validator return true * location is targetZero/nested * method is assign * location is targetMax/nested/content * method is assign * location is targetMultiple/targetOne/word * execute the same callback function * location is targetMultiple/targetTwo/word * execute the same callback function */
Detect view data changes in Vue component
The listener keep view data's getter/setter so that it can keep the ability to update DOM automatically.
{{item.num}}
Detect instance's methods's execution
By customizing instance's methods's listening settings, the listener can detect instance's methods's execution partially or completely.
var pListener = {} { console; } { super } var instance = var object = instance: instance pListener // listener can detect instance's methods's execution through prototype chain objectinstance /* console output: * this is Class instance * location is instance/output * method is function */
Application in Composite pattern
You can execute specific marco tree nodes's commands rather than executing all nodes's commands.
static marcoTree
var pListener = // organize the macroTree var object = macroTree: command1: node: 'command1' children: 'command1.1': node: 'command1.1' 'command1.2': node: 'command1.2' command2: node: 'command2' command3: node: 'command3' { thispara = para } { console; } var funcObj = {} { for var key in macroTree macroTreekey'node' = key if macroTreekey'children' } var macrotreeListerner = pListener macrotreeListerner // add functions to funcObj // execute specific node command's in macro commands /* console output: * command execution from node path: macroTree/command1/node * command execution from node path: macroTree/command1/children/command1.1/node * command execution from node path: macroTree/command1/children/command1.2/node */
dynamic marcoTree
// dynamic marcoTree var pListener = { if type !== 'main' thisnode = name thischildren = {} } { thisnode'node' = node } { thischildrennode'node' = node } { console; } { for var key in macroTree macroTreekey'node' = key if macroTreekey'children' } // organize the macroTree var object = macroTree: null 'main' var command1 = 'command1' var command2 = 'command2' var command3 = 'command3' command1 command1 objectmacroTree objectmacroTree objectmacroTree var macrotreeListerner = pListener macrotreeListerner // add new node after creating a listener objectmacroTreecommand1 /* console output: * command execution from node path: macroTree/command1/node * command execution from node path: macroTree/command1/children/command1.1/node * command execution from node path: macroTree/command1/children/command1.2/node * new node is added */
Testing Composite pattern with proxylistener and without proxylistener
The testing files are in __tests__
folder and the result's review is here.