So, I have an element prototype function called hasClass.
Element.prototype.hasClass = function (className)
{
if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}
};
Now, since I have to define multiple functions and the code will become pretty messy then I thought, why not use Object.defineProperties function; same function with the function:
Object.defineProperties(Element.prototype, {
"hasClass": {
get: function (className)
{
if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}
}
}
});
The first function works fine as it should.
However, when defining same function with Object.defineProperties my Firefox console starts spamming: TypeError: $test.hasClass is not a function (Where $test is element choosen with selector). This error does not appear when using normal, first example of function, while it appears when using the lattest one.
So the question is. Why it does throw such error while it should not?
Oh yeah, funny thing is that when I use the second function with console.log like this:
Object.defineProperties(Element.prototype, {
"hasClass": {
get: function (className)
{
/*if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}*/
console.log( "HI");
}
}
});
Then the console says this:
http://i.imgur.com/ULf7Ev6.png
I am confused.
You're mis-using the property get you should be using value otherwise hasClass will be the value returned from the get function.
Replace get with value
Object.defineProperties(Element.prototype, {
"hasClass": {
value: function (className)
{
/*if (className && typeof className === "string")
{
if (this.classList)
{
return this.classList.contains(className);
}
else
{
return new RegExp(className).test("" + this.className + "");
}
}*/
console.log( "HI");
}
}
});
Refer to documentation on defineProperty even though you're using defineProperties
The problem: in your version the value of hasClass will be undefined because you haven't returned anything from the get function the last statement is console.log( "HI"); if you put return {}; after that, hasClass === {}
Object.defineProperty and Object.defineProperties are not meant to define getter functions with params. In your case, just do it as in your first example (or look below, see Edit).
You can understand Object.defineProperty as a way to "intercept" the plain set/get action, but in the code where you get or set the property you can see no difference to "public" properties.
obj.abc = 'value'; // <-- set function called
console.log(obj.abc); // <-- get function called
There are no braces () - the functions are called transparently.
In case you really have properties, be careful not to run into recursive calls when the property you want to hide is actually as well public. See JS defineProperty and prototype
Edit
As I understand your question, you're looking for a less verbose way to add functions to a prototype.
Many JavaScript libraries have a way to copy some properties from one object to another:
jQuery: jQuery.extend
Underscore/Lodash: _.extend
Leaflet: L.extend
sure there are many more (and maybe a modern version of ECMAScript already has such a utility method)
Here you have a very basic extend function, if you don't have one of those libraries at hand:
function extend1(dest, src) {
var i;
for (i in src) {
if (src.hasOwnProperty(i)) {
dest[i] = src[i];
}
}
return dest;
}
Now you could extend your prototypes like this:
extend1(Element.prototype, {
hasClass: function (className) {
/* the body */
},
methodFoo: function (arg1, arg2) {},
methodBar: function () {},
});
I think, for methods this is a better way than using Object.defineProperty. For real property definitions, take Object.defineProperty.
Related
I'm currently writing a library using jQuery, and I'm facing a wall I can't pass alone.
First, here is some code :
(function($) {
// default options
var defaults = {
option1: "what ever",
option2: "what ever"
};
// available methods
var methods = {
method1: function(param1) {
console.log(param1);
/* ... */
},
method2: function(param1, param2) {
console.log(param1);
console.log(param2); // gives undefined
/* ... */
}
};
// Where magic happens
$.fn.pskChart = function(params) {
if (methods[params] != undefined) {
if (this.length > 0) {
return $(this).each(function(i) {
return methods[params].apply($(this), Array.prototype.slice.call(arguments, 1));
});
}
} else {
$.error("Method " + params + " doesn't exist for pskChart");
}
}
})(jQuery);
$("#sample").pskChart("method1", "param1"); // will work
$("#sample").pskChart("method2", "param1", "param2"); // won't work
This code is working in case I provide only two parameters (method and another parameter), but won't work if I have more parameters
I understand that Array.prototype.slice.call(arguments, 1) returns only one object containing all the remaining arguments.
In the second example, method2 will be called with only one parameter that contains ["param1","param2"]
I would like to "split" this object in order to provide as many parameters I have.
I feel like methods[params].apply($(this), Array.prototype.slice.call(arguments, 1)); but I have no clue how to fix it.
Note that I could have an infinite (or at least big) number of parameters (in addition to the method, that will always be first).
Playing with Array.slice would not be a great (nor proper) solution.
I don't see why it would work even with just one parameter, as you're using the wrong arguments object.
Instead (see comments):
// Where magic happens
$.fn.pskChart = function(params) {
var args; // **Change** We'll use this below
if (methods[params] != undefined) {
if (this.length > 0) {
// **Change** Grab the arguments here, in the `pskChart` function
args = Array.prototype.slice.call(arguments, 1);
return $(this).each(function(i) {
// **Change** Use them here; you can't use `arguments` here
// because, it will be the arguments to this anonymouus
// function, not the ones to `pskChart`
return methods[params].apply($(this), args);
});
}
} else {
$.error("Method " + params + " doesn't exist for pskChart");
}
}
Side note: params is a very strange name for an argument you're using as a method name. Perhaps methodName?
Side note 2: Rather than repeatedly looking up methods[params], consider using a variable. Large sets may benefit from the reduced work.
Side note 3: This line inside your each loop is almost certainly a mistake:
return methods[params].apply($(this), args);
That will return the return value of the method to jQuery's each code. jQuery's each code will completely ignore that vallue unless it's === false, in which case it will stop the each loop. Not at all likely to be what you meant that to do.
Side note 4: Your pskChart function, being a jQuery plugin, should return this unless it has a good reason for returning something else. Right now, the return value is chaotic: If the set has no elements, you return undefined (implicitly); if the set has objects, you return a new jQuery object, not this, because of this line:
return $(this).each(function(i) {
There's no reason for using $() there, this is already a jQuery set (which is why the previous line using this.length works).
Side note 5: You're missing a semicolon from the assignment statement (e.g., there should be one after the closing } on the function expression).
Recommendation:
// Where magic happens
$.fn.pskChart = function(methodName) {
var args, method = methods[methodName];
if (!method) {
// Throws
$.error("Method " + params + " doesn't exist for pskChart");
}
if (this.length > 0) {
args = Array.prototype.slice.call(arguments, 1);
this.each(function() {
method.apply($(this), args);
});
}
return this;
};
In javascript using an object parameter is my preferred way of working with functions. To check that a function has the required parameters I either (Solution 1) loop through all the object parameters properties and throw an error or (Solution 2) wait until a required property is needed and throw an error. Solution two seems efficient but I have to throws in multiple places in the function. Solution 1 seems pragmatic but should probably be a reusable piece of code. Is there another solution I should be looking at?
You can actually do this
var propsNeeded = ["prop1", "prop2", "blah", "blah", "blah"],
obj = {
prop1: "Hi"
}
function hasRequiredProperties(props, obj){
return Object.keys(obj).sort().join() == propsNeeded.sort().join();
}
console.log(hasRequiredProperties(propsNeeded, obj)); // false
You can check for single properties like
function hasProperty(propName, obj){
return obj.hasOwnProperty(propName);
}
For consistency I would create require method and use it always when some property is required.
var require = function (key, object) {
if (typeof object[key] === 'undefined') {
throw new Error('Required property ' + key + ' is undefined');
}
};
I would test if required property exists as soon as I'm certain that property is needed. Like this:
var example = function (args) {
require('alwaysRequired', args);
// some code here which uses property alwaysRequired
if (args.something) {
require('sometimesRequired', args);
// some code here which uses property sometimesRequired
}
};
Using #Amit's answer I'd probably add a method to Object itself:
Object.prototype.hasAllProperties = function(props, fire){
var result = Object.keys(this).sort().join() == propsNeeded.sort().join();
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
and in your function:
function someFunction(myObject){
var objComplete = myObject.hasAllProperties(["prop1", "prop2", "prop3"], false);
}
Update:
After noticing the problem with #Amit's original answer, here's what I suggest:
Object.prototype.hasAllProperties = function(props, fire){
var result = true;
$(props).each(function(i, e){
if (!this.hasOwnProperty(e) ) {
result = false;
return false;
}
});
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
This is just a general case of checking for presence of keys on a object, which can be done easily enough with
requiredParams.every(function(prop) { return prop in paramObj; })
It almost reads like natural language. "Taking the required parameters, is EVERY one of them IN the parameter object?".
Just wrap this in function checkParams(paramObj, requiredParams) for easy re-use.
More generally, this is the problem of asking if one list (in this case the list of required parameters) is included in another list (the keys on the params object). So we can write a general routine for list inclusion:
function listIncluded(list1, list2) {
return list1.every(function(e) { return list2.indexOf(e) !== -1; });
}
Then our parameter-checking becomes
function checkParams(paramObj, requiredParams) {
return listIncluded(requiredParams, Object.keys(paramObj));
}
If you want to know if object has at least some properties you can use this function without third parameter:
function hasRequiredProperties(propsNeeded, obj, strict) {
if (strict) return Object.keys(obj).sort().join() == propsNeeded.sort().join();
for (var i in propsNeeded ) {
if (!obj.hasOwnProperty(propsNeeded[i])) return false;
}
return true;
};
Example:
options = {url: {
protocol: 'https:',
hostname: 'encrypted.google.com',
port: '80'
}
};
propsNeeded = ['protocol', 'hostname'];
hasRequiredProperties(propsNeeded, options.url); // true
hasRequiredProperties(propsNeeded, options.url, true); // false
Maybe some of you know about AOP, in some languages using AOP can lead you to be able to inject code after, before, or while a method is executing,etc.
What I want is to apply the same in Javascript, I am currently working on a massive app which has more than 300 ajax calls, and every time I need to do some change on the catch statement on them, I have to modify them one by one which is very tedious.
What I want to do is something like :
functionName.before("try {")
functionName.after("} catch(ex){
//dostuff
}")
Is it possible? I know there are things like .call, or the arguments object inside every function..which seem pretty meta-function (AOP) functionalities.
Not with before and after, but a wrap will work:
Function.prototype.wrapTry = function(handle) {
var fn = this;
return function() {
try {
return fn.apply(this, arguments);
} catch(e) {
return handle(e);
}
};
};
Then use it like
var safeFunction = functionName.wrapTry(doStuff);
In JavaScript, functions are first-class objects. That means you can manipulate or redeclare them.
Assuming that there is a "foo" function:
var originalFoo = foo;
foo = function()
{
// "before" code.
// Call the original function.
originalFoo.apply(this, arguments);
// "after" code.
};
After that, any call to foo() will call the new function: even with parameters.
Old question but you may take a look over this https://github.com/k1r0s/kaop-ts/blob/master/docs/api.md#available-join-points
import { onException } from "kaop-ts"
import handlingException from "./somewhere"
class Something {
#onException(handlingException)
method() {
// stuff that may throw an error
}
}
I also will give a late answer in order to shed some light onto this special case that every then and now pops up as JavaScript and AOP.
Firstly, cases like the very one presented by the OP always ask for modifying already existing functionality, thus targeting closed code that sometimes is not even owned by the party that sees itself challenged from modifying the control flow of such code.
Why then, not just name it like that ... JavaScript method modification or JavaScript method modifiers.
Secondly, because of already riding the horse of terminology, altering closed functionality in JavaScript has nothing to do with Aspect-oriented Programming unless an implementation that claims to be AO provides abstraction and code-reuse levels for at least Aspect, Advice and Pointcut.
Last, for what the OP is going to achieve and what also has been the accepted answer, there does exist a a whole bunch of before, after around / wrap solutions, almost always unfortunately mentioning AO(P), and in far too many cases not taking care of the context or target which is essential to method modification.
The example I do provide uses a prototypal implementation of afterThrowing. Because JavaScript already features a standardized bind, I'm firmly convinced that Function.prototype is the right place as well for some other method-modifiers
like before, after, around, afterThrowing
and afterFinally.
// OP's example pseudo code
//
// functionName.before("try {")
//
// functionName.after("} catch(ex){
// dostuff
// }")
function doStuffAfterThrowing(exception, originalArguments) {
"use strict";
var context = this;
console.log('context : ', context);
console.log('String(exception) : ', String(exception));
console.log('originalArguments : ', originalArguments);
return "safely handled exception";
}
function doFail() {
throw (new ReferenceError);
}
function oneOutOfManyAjaxCallbacks(payload) {
doFail();
}
var jsonData = {
"foo": "foo",
"bar": "bar"
};
var someModifiedAjaxCallback = oneOutOfManyAjaxCallbacks.afterThrowing(doStuffAfterThrowing, { x: 'y' });
// does fail controlled/handled.
console.log('someModifiedAjaxCallback(jsonData) : ', someModifiedAjaxCallback(jsonData));
// does fail "Uncaught".
console.log('oneOutOfManyAjaxCallbacks(jsonData) : ', oneOutOfManyAjaxCallbacks(jsonData));
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
return ((target != null) && target) || null;
}
;
Function.prototype.afterThrowing = function (handler, target) { // afterThrowing
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
var ret, args = arguments;
try {
ret = proceed.apply(target, args);
} catch (exc) {
ret = handler.call(target, exc, args);
//throw exc;
}
return ret;
}) || proceed;
};
}(Function));
</script>
Having come that far one might also consider reading ...
sandwich pattern in javascript code
Can you alter a Javascript function after declaring it?
Question
I'd like to know the best way I can wrap the jQuery function while retaining all functionality. Essentially I want to call $('#someId') but have it operate as $('#' + id + 'someId') by wrapping the function, modifying the arguments, and passing it through to the original jQuery function.
Motivation
I have a section of JS that will reuse the same variable winId which is concatenated and passed to jQuery. Instead of writing
$('#' + winId + 'someId').html();
$('#' + winId + 'someOtherId').css();
...
$('#' + winId + 'someThirdId').text();
throughout the whole file, I want to wrap the jQuery function so I can just call
$('#someId').html();
$('#someOtherId').css();
...
$('#someThirdId').text();
and and have winId added in before passing through to $.
My attempt
Here's what I'm thinking as a wrapper:
(function() {
var fn = $;
return $ = function() {
for ( var i = 0; i < arguments.length; i++ ) {
if ( typeof arguments[i] == 'string') {
arguments[i] = /* code to add in winId, omitted */
}
}
return fn.apply( this, arguments );
}
})();
This works great, except that obviously none of the methods like $.ajax are available:
Uncaught TypeError: Object function () {
for ( var i = 0; i < arguments.length; i++ ) {
if ( typeof arguments[i] == 'string' ) {
arguments[i] = /* code to add in winId, omitted */
}
}
return fn.apply( this, arguments );
} has no method 'ajax'
Note: I know I could copy the object over using jQuery.extend($, jQuery), but I'm interested in a more elegant solution than that if possible.
Here's a different implementation:
DEMO
(jQuery.fn.init = (function (init) {
return function (selector) {
if (typeof selector === 'string' && selector[0] === '#') {
arguments[0] = selector.replace('#', '#prefix_');
}
return init.apply(this, arguments);
};
})(jQuery.fn.init)).prototype = jQuery.fn;
$(function () {
console.log($('#test').length);
console.log($.ajax);
});
EDIT: Followup question: How can I apply this only within a closure? For example, within an object.
Perhaps with functions that allows to add named decorators and remove them, something like:
HTML
<div id="prefix_test"></div>
JS
var decJQ = (function (decJQ, $) {
var decorators = {},
init = $.fn.init;
($.fn.init = function () {
for (var k in decorators) {
if (decorators.hasOwnProperty(k)) {
arguments = decorators[k].apply(this, arguments);
}
}
return init.apply(this, arguments);
}).prototype = $.fn;
return $.extend(decJQ, {
decorate: function (name, fn) {
decorators[name] = fn;
},
undecorate: function (name) {
delete decorators[name];
}
});
})(window.decJQ || {}, jQuery);
decJQ.decorate('idPrefix', function (selector) {
if (typeof selector === 'string' && selector[0] === '#') {
arguments[0] = selector.replace('#', '#prefix_');
}
return arguments;
});
$(function () {
console.log($('#test').length); //1
decJQ.undecorate('idPrefix');
console.log($('#test').length); //0
});
EDIT 2:
You could also go for something extremely simple, such as:
(function ($) {
//use $ which has been wrapped
})(function () {
//do some manipulations
return jQuery.apply(this, arguments);
});
Following the suggestion by Bergi and the post he links to here, this is one way to go:
$.fn.extend({
initCore: $.fn.init,
init: function (selector, context, rootjQuery) {
if (typeof selector === 'string' && selector[0] === '#') {
selector = selector.replace('#', '#' + winId);
}
return $.fn.initCore(selector, context, rootjQuery);
}
});
$.fn.init.prototype = $.fn;
I've tested $('#foo') will find a div that has a winId prefixed to the id value, like this <div id="1foo"></div>.
For example: http://jsfiddle.net/MfdJS/1/
Add class="winID" to your elements.
Use $(".winID").find('#someId").css(...) to access CSS attributes of specific element.
Use $(".winID").css(...) to access CSS attribues to all winID tagged elements.
ok well i just tested
$('.con'+'tainer')
and
$('d'+'iv');
and
var s = 't';
$('.con'+s+'ainer');
and the console is returning the correct values
i belive that you are calling a function jQuery() with a string parameter, so as long as you use the normal syntax for building/appending/constructing a string with the plus signs, i think you're golden. im glad you asked this question because now i know too
That's a pretty strange thing to do. Why don't you just create a CSS selector string for winId and save it as a variable?
var foo = '#' + winId;
Now you can do:
$(foo + ', #bar').html("add some content");
What you're proposing to do will leave any programmer working on this project -- including you six months from now -- completely flummoxed when they use $('#bar') and it's actually selecting #foo and #bar.
Ok, difficult to understand from the title only. Here is an example. I want a function to refer to a variable that is "injected" automagically, ie:
function abc() {
console.log(myVariable);
}
I have tried with:
with({myVariable: "value"}) { abc() }
but this doesn't work unless abc is declared within the with block, ie:
with({myVariable: "value"}) {
function abc() {
console.log(myVariable);
}
abc(); // This will work
}
So the last piece will work, but is it possible to fake the with statement, or do I have to force the developers to declare their function calls in a with statement?
Basically the call I want to do is:
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
Ofcourse, I am aware I could pass this is a one parameter object, but that is not what I am trying to do:
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
Further more, I am trying to avoid using eval:
with({myVariable: "value"}) {
eval(abc.toString())(); // Will also work
}
Is this not supported at at all beyond eval in Javascript?
JavaScript does not provide any straightforward way to achieve the syntax you're looking for. The only way to inject a variable into a Lexical Environment is by using eval (or the very similar Function constructor). Some of the answers to this question suggest this. Some other answers suggest using global variables as a workaround. Each of those solutions have their own caveats, though.
Other than that, your only option is to use a different syntax. The closest you can get to your original syntax is passing a parameter from doSomething to the callback, as Aadit M Shah suggested. Yes, I am aware you said you don't want to do that, but it's either that or an ugly hack...
Original answer (written when I didn't fully understand the question)
Maybe what you're looking for is a closure? Something like this:
var myVariable = "value";
function doSomething() {
console.log(myVariable);
};
doSomething(); // logs "value"
Or maybe this?
function createClosure(myVariable) {
return function() {
console.log(myVariable);
};
}
var closure = createClosure("value");
closure(); // logs "value"
Or even:
var closure = function(myVariable) {
return function() {
console.log(myVariable);
};
}("value");
closure(); // logs "value"
I asked a similar question a long time ago: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
The short answer is no, you can't achieve dynamic scoping without resorting to eval. The long answer is, you don't need to.
JavaScript doesn't support dynamic scoping, but that's not an issue because you can make your free variables parameters of the function that they belong to.
In my humble opinion this is the best solution:
function doSomething(context, callback) {
callback(context);
}
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
However since you don't want to write a formal parameter, the next best thing is to use this instead:
function doSomething(context, callback) {
callback.call(context);
}
doSomething({myVariable: "value"}, function() {
console.log(this.myVariable);
});
Another option would be to manipulate the formal parameter list of the program as follows:
function inject(func, properties) {
var args = [], params = [];
for (var property in properties) {
if (properties.hasOwnProperty(property)) {
args.push(properties[property]);
params.push(property);
}
}
return Function.apply(null, params.concat("return " + func.toString()))
.apply(null, args);
}
Now we can use this inject method to inject properties into a function as follows:
function doSomething(context, callback) {
var func = inject(callback, context);
func();
}
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
See the demo: http://jsfiddle.net/sDKga/1/
Note: The inject function will create an entirely new function which will not have the same lexical scope as the original function. Hence functions with free variables and partially applied functions will not work as expected. Only use inject with normal functions.
The Function constructor is kind of like eval but it's much safer. Of course I would advise you to simply use a formal parameter or this instead. However the design decision is your choice.
Try:
function doSomething(vars, fun) {
for (var key in vars) { // set the variables in vars
window[key] = vars[key];
}
fun.call(); // call function
for (var key in vars) { // remove the variables again. this will allow only the function to use it
delete window[key];
}
}
Set global variables that can then be received inside of fun
The JSFiddle: http://jsfiddle.net/shawn31313/MbAMQ/
Warning: disgusting code ahead
function callWithContext(func, context, args) {
var oldProperties = {};
for(var n in context) {
if(context.hasOwnProperty(n)) {
var oldProperty = Object.getOwnPropertyDescriptor(self, n);
oldProperties[n] = oldProperty;
(function(n) {
Object.defineProperty(self, n, {
get: function() {
if(arguments.callee.caller === func) {
return context[n];
}
if(!oldProperty) {
return;
}
if(oldProperty.get) {
return oldProperty.get.apply(this, arguments);
}
return oldProperty.value;
},
set: function(value) {
if(arguments.callee.caller === func) {
context[n] = value;
}
if(!oldProperty) {
return;
}
if(oldProperty.set) {
return oldProperty.get.apply(this, arguments);
} else if(!oldProperty.writable) {
var fakeObject = {};
Object.defineProperty(fakeObject, n, {value: null, writable: false});
fakeObject[n] = value; // Kind of stupid, but…
return;
}
oldProperty.value = value;
}
});
})(n);
}
}
func.apply(this, args);
for(var n in context) {
if(context.hasOwnProperty(n)) {
if(oldProperties[n]) {
Object.defineProperty(self, n, oldProperties[n]);
} else {
delete self[n];
}
}
}
}
This is vomitously horrendous, by the way; don’t use it. But ew, it actually works.
i don't see why you can't just pass the info in or define a single global, but i think that would be best.
that said, i am working on a Module maker/runner that allows sloppy/dangerous code to execute without interference to the host environment. that provides the opportunity to re-define variables, which can be passed as an object.
this does use eval (Function() technically) but it can run in "use strict", so it's not too crazy/clever.
it doesn't leave behind artifacts.
it also won't let globals get hurt.
it's still a work in progress, and i need to iron out a couple minor details before i vouch for security, so don't use it for fort knox or anything, but it's working and stable enough to perform the operation asked for.
tested in ch28, FF22, IE10:
function Module(strCode, blnPreventExtensions, objWhitelist, objExtend) {
var __proto__=self.__proto__, pbu=self.__proto__, str=strCode, om=[].map, wasFN=false,
params = {Object:1}, fnScrubber, natives= [ Object, Array, RegExp, String, Boolean, Date] ,
nativeSlots = [],
preamble = "'use strict';" ,
inherited="__defineGetter__,__defineSetter__,__proto__,valueOf,constructor,__lookupGetter__,__lookupSetter__",
late = inherited +
Object.getOwnPropertyNames(__proto__||{}) + Object.getOwnPropertyNames(window);
late.split(",").sort().map(function(a) {
this[a] = 1;
}, params);
preamble+=";var "+inherited+";";
//turn functions into strings, but note that a function was passed
if(str.call){wasFN=true; str=String(str); delete params.Object; }
objExtend=objExtend||{};
var vals=Object.keys(objExtend).map(function(k){ return objExtend[k]; })
// build a usable clone of Object for all the new OOP methods it provides:
var fakeOb=Object.bind();
(Object.getOwnPropertyNames(Object)||Object.keys(Object)).map(function(a){
if(Object[a] && Object[a].bind){this[a]=Object[a].bind(Object); } return this;
},fakeOb)[0];
//allow "eval" and "arguments" since strict throws if you formalize them and eval is now presumed safe.
delete params.eval;
delete params.arguments;
params.hasOwnProperty=undefined;
params.toString=undefined;
params['__proto__']={};
__proto__=null;
Object.keys(objWhitelist||{}).map(function ripper(a,b){
b=this[a];
if(typeof b!=='object'){
delete this[a];
}
}, params);
// var ok=Object.keys.bind(Object);
// prevent new prototype methods from being added to native constructors:
if (blnPreventExtensions) {
natives.forEach(function(con, i) {
var proto=con.prototype;
Object.getOwnPropertyNames(proto).map(function(prop){
if(proto[prop] && proto[prop].bind ){ this[prop]=proto[prop];}
}, nativeSlots[i] = {});
delete con.constructor;
delete con.prototype.constructor;
}); //end con map()
} /* end if(blnPreventExtensions) */
//white-list harmless math utils and prevent hijacking:
delete params.Math;
if(blnPreventExtensions){Object.freeze(Math);}
//prevent literal constructors from getting Function ref (eg: [].constructor.constructor, /./.constructor.constructor, etc...):
Function.prototype.constructor = null;
try {
//generate a private wrapper function to evaluate code:
var response = Function(
Object.keys(objExtend) + (vals.length?",":"") +
Object.keys(params).filter(/./.test, /^[\w\$]+$/), // localize most globals
preamble + " return " + str.trim() // cram code into a function body with global-blocking formal parameters
);
// call it with a blank this object and only user-supplied arguments:
if (blnPreventExtensions) { //( user-land code must run inside here to be secure)
response = response.apply({}, vals.concat(fakeOb)).apply({}, [].slice.call(arguments,4) );
}else{
response = response.apply({}, vals.concat(fakeOb));
}
} catch (y) {
response = y + "!!";
} /* end try/catch */
if (blnPreventExtensions) {
om.call(natives, function(con, i) {
var pro=con.prototype;
//remove all proto methods for this con to censor any additions made by unsafe code:
Object.getOwnPropertyNames(pro).map(function(a){ try{delete pro[a];}catch(y){}});
//restore all original props from the backup:
var bu = nativeSlots[i];
om.call(Object.keys(bu), function(prop){ con.prototype[prop]=bu[prop]; }, bu);
}); //end con map()
} /* end if(blnPreventExtensions) */
//restore hidden Function constructor property:
Function.prototype.constructor = Function;
return response;
} /* end Module() */
/////////////////////////////////////////////////////////////
function doSomething(context, fn){
console.log(myVariable);
return myVariable;
}
//use 1:
alert( Module(doSomething, true, {console:1}, {myVariable: "value123"} ) );// immed
//use2:
var fn=Module(doSomething, false, {console:1}, {myVariable: "value123"} );// as function
alert(fn);
alert(fn());
again, i think OP would be best off not doing things later than need be, but for the sake of comprehensiveness and inspiration i'm putting this out there in good faith.
You need to use call() to construct a context, as in:
var f=function(){
console.log(this.foo);
};
f.call({foo:'bar'})
will print "bar"
You can avoid using eval() in calling the function, if you are willing to use it in doSomething():
function abc() {
console.log(myVariable);
}
// Prints "value"
callWith({ myVariable: "value" }, abc);
function callWith(context, func) {
for(var i in context) eval('var ' + i + ' = context[i];');
eval('(' + func.toString() + ')')();
}
Have a look at this post.
Have a look at goog.partial, scroll a little bit up to see the description of what it does:
Here is an implementation of it:
var b = goog.partial(alert, 'Hello world!');
b();//alerts "Hello world!"
In the example it passes the function alert with parameter "Hello world!" but you can pass it your own function with multiple parameters.
This allows you to create a variable that points to a function that is always called with a certain paramater. To use parameters in a function that are not named you can use arguments:
function test(){
console.log(arguments);//["hello","world"]
}
test("hello","world");