• Jump To … +
    array.js can-observe.js object.js -getter-helpers.js -helpers.js -make-array.js -make-function.js -make-object.js -make-observe.js -make-simple-function.js -memoize-getter-test.js -memoize-getter.js -observable-store.js -symbols.js
  • ¶

    -make-function.js

    This module’s .observable method proxies an function to make it an any instances created by it observable.

    var canReflect = require("can-reflect");
    var makeObject = require("./-make-object");
    var symbols = require("./-symbols");
    var observableStore = require("./-observable-store");
    var mapBindings = require("can-event-queue/map/map");
    var typeBindings = require("can-event-queue/type/type");
    var helpers = require("./-helpers");
  • ¶

    proxyKeys

    A function’s proxyKeys is a combination of:

    • object’s symbols (.onKeyValue)
    • type event symbols (.onInstancePatches, .onInstanceBound)
    • type definition symbols (.defineInstanceKey)
    var proxyKeys = helpers.assignEverything(Object.create(null), makeObject.proxyKeys());
    typeBindings(proxyKeys);
    canReflect.assignSymbols(proxyKeys, {
    	"can.defineInstanceKey": function(prop, value) {
    		this[symbols.metaSymbol].definitions[prop] = value;
    	}
    });
    
    var makeFunction = {
    
    	observable: function(object, options) {
    
    		if(options.shouldRecordObservation === undefined) {
    			options.shouldRecordObservation = makeObject.shouldRecordObservationOnOwnAndMissingKeys;
    		}
    		var proxyKeys = Object.create(makeFunction.proxyKeys());
    
    		var meta = {
    			target: object,
    			proxyKeys: proxyKeys,
    			options: options,
    			definitions: {},
    			isClass: helpers.isClass(object),
    			preventSideEffects: 0
    		};
    
    		proxyKeys[symbols.metaSymbol] = meta;
    		meta.proxy = new Proxy(object, {
    			get: makeObject.get.bind(meta),
    			set: makeObject.set.bind(meta),
    			ownKeys: makeObject.ownKeys.bind(meta),
    			deleteProperty: makeObject.deleteProperty.bind(meta),
    			construct: makeFunction.construct.bind(meta),
    			apply: makeFunction.apply.bind(meta),
    			meta: meta
    		});
    
    		mapBindings.addHandlers(meta.proxy, meta);
    		typeBindings.addHandlers(meta.proxy, meta);
  • ¶

    Store the function and its proxy now, before we convert the prototype and its constructor to proxies.

    		observableStore.proxiedObjects.set(object, meta.proxy);
    		observableStore.proxies.add(meta.proxy);
  • ¶

    Change prototype and its constructor

    		if (meta.target.prototype && meta.target.prototype.constructor === meta.target) {
    			var prototype = meta.proxy.prototype;
    			prototype.constructor = meta.proxy;
    		}
    
    		return meta.proxy;
    	},
  • ¶

    construct is called when the new operator is used. It needs to return an observable instance.

    	construct: function(target, argumentsList, newTarget) {
    
    		var instanceTarget, key;
    		if (this.isClass) {
  • ¶

    If the target a class, we can’t call the function without new. We can create the instance with Reflect.construct.

    			instanceTarget = Reflect.construct(target, argumentsList, newTarget);
  • ¶

    Support can.defineInstanceKey.

    			for (key in this.definitions) {
    				Object.defineProperty(instanceTarget, key, this.definitions[key]);
    			}
    			return this.options.observe(instanceTarget);
    		} else {
  • ¶

    Create an empty object that inherits from the constructor function.

    			instanceTarget = Object.create(this.proxy.prototype);
  • ¶

    Support can.defineInstanceKey.

    			for (key in this.definitions) {
    				Object.defineProperty(instanceTarget, key, this.definitions[key]);
    			}
    			var instance = this.options.observe(instanceTarget);
    			instance[symbols.metaSymbol].preventSideEffects++;
    			var res = target.apply(instance, argumentsList);
    			instance[symbols.metaSymbol].preventSideEffects--;
    			if (res) {
    				return res;
    			} else {
    				return instance;
    			}
    		}
    	},
  • ¶

    apply makes sure the function returns an observable.

    	apply: function(target, thisArg, argumentsList) {
    		var ret = this.target.apply(thisArg, argumentsList);
    		return this.options.observe(ret);
    	},
    	proxyKeys: function() {
    		return proxyKeys;
    	}
    };
    
    module.exports = makeFunction;