I've just found the following line in Knockout's source code:
target.subscribe = target['subscribe'] = function …
Why are they assigning the function to the same property twice? The only difference is the way they access it. As far as I know this shouldn't make a difference with the given property name (JavaScript property access: dot notation vs. brackets?).
It's possible that this is done to prevent things breaking when code is minified.
target.subscribe can be minified to something like target.a, however there may be code that relies on target.subscribe still being there. For instance, you might have:
var x = 'subscribe';
target[x](something);
Assigning to both will allow the minifier to do its work, without breaking support for expression access.
Related
Example use case:
I have an object with an attribute "myProperty", having getter and setter ("Property Getters and Setters" are supported since EcmaScript 5: https://www.w3schools.com/js/js_es5.asp):
var obj = {
myProperty:'myPropertyValue',
get myProperty(){
return this.property;
},
set myProperty(value){
this.property=value;
}
};
I would like to bind that attribute to a view, which is the task of a custom function that is called bindProperty.
In order to pass the property myProperty to the function, I could do something like
bindProperty(obj, 'myProperty');
However, I would like to avoid to pass the property name as a hard coded String. Strings have the disadvantage, that they are not updated when the attribute name changes during refactoring.
If I would use
bindProperty(obj, obj.myProperty);
the function would only know the property value 'myPropertyValue' and not where the value comes from.
=>How can I pass/identify the property itself, without using a String?
A. Using reflection?
I could imagine something like
bindProperty(obj, ()=>obj.myProperty);
The function bindProperty would then have to do some reflection magic to find out the name of the attribute in the body of the lambda expression (pseudo code):
let attributeName = getNameofArgumentFromBodyOfLambdaExpression(lambda);
obj[attributeName] = 'newValue';
=>Is it possible in JavaScript to evaluate the body of the lambda expression using reflection to get the name of the property?
(I know that this can be done in .NET languages, e.g.
Private Sub BindProperty(Of T)(propertyExpression As Expression(Of Func(Of T)))
Dim propertyName As String = GetPropertyName(propertyExpression)
'...
)
B. Using complex attributes
An alternative whould be that I use wrapping property objects, having their own getters and setters. Howerver, then I would have to use the property like
obj.myProperty.set('newValue')
or
obj.myProperty('newValue') //similar to knockout observables
I still want to be able to use the great Getter/Setter feature. With other words: I want to use my properties like plain attributes:
obj.myProperty = 'newValue'
Therefore, this is not an option for me and I would prefer to use Strings instead of B.
C. Any other alternatives?
An object in javascript is more or less just a mapping of strings or symbols to values. There is no real reflection that you can call upon in the runtime environment that would enable you to move backward from the value to the property name.
If all you need is refactoring, the one way to do this would be to just configure your IDE to recognize string accessors by providing some sort of type information either via Flow or Typescript or something of that sort (the type information is likely what allows reflection to work in languages like .NET). Or you could just settle for a unique prefix like "viewable_propName" and just do simple find and replace if you need to rename.
If you are really focused on getting this to work without type information and in current ES6 syntax, you could do the following:
function getNameofPropFromVal(obj, val){
for(let prop in obj){
if(obj[prop] === val) return prop;
}
}
obj[getNameofPropFromVal(obj, obj.myProp)] = 'newVal';
Though this has shortcomings:
(1) There is no guarantee that two properties won't share the same value.
(2) It introduces unnecessary runtime overhead.
Finally, if you're willing to be cutting edge and use a transformer like babel you could use decorators for your bindProperty method. That way you can just do the binding in the object definition itself. Here is an article explaining the gist and here is the more formal ECMAScript proposal.
I just found following simple work around that might fullfill my needs:
function bindProperty(obj, lambdaExpression){
let expression = lambdaExpression.toString(); // "()=> obj.myProperty"
let subStrings = expression.split(".");
let propertyName = subStrings[1];
console.info(propertyName );
//...
}
blah('A');
function blah(letter){
arrayA.push('something');
}
I want to push something to an array where the name of the array is 'array' plus a letter being passed to it.
I can console out 'arrayA' fine:
console.log('array'+${letter})
But if I try to build the array name, the same logic doesn't work:
array${letter}.push('something')
In the browser (where the global objects, functions, and variables become members of the window object) you can create and access dynamically named objects using the bracket notation.
Were you looking for something like this?
function blah(letter){
window['array' + letter] = [];
window['array' + letter].push('something');
}
blah('A');
After this you can access and use the newly created array (arrayA) as usual.
arrayA.push('something else');
In node you can probably achieve this using global instead of window.
Try it and forget it (or replace the 3 occurrences of window with global for testing with node.js):
function test(name,value){
if(!window["array"+name])
window["array"+name]=[];
window["array"+name].push(value);
}
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
test("A",10);
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
test("A",20);
try{console.log(arrayA);}catch(e){console.log("arrayA missing: "+e);}
window is the global scope in a browser, and generally you should not rely on global variables without a good reason. They lack context (that is why they are 'global'), making it hard to tell where they belong, what they are and where they come from. That is something what most programming paradigms advise against.
The thing also works with node.js, just it has global as global context, you can paste this snippet into https://www.tutorialspoint.com/execute_nodejs_online.php as a test, replace the 3 window-s, and it will work (you can of course wrap it into a proper module too, just that is more work). What is written above against the usage of global variables stays true for node.js too. Do not use the global context especially if you are developing modules.
However, instead of window, the syntax works with any object too, and that would be considered okay:
var obj={};
console.log(obj.something);
obj['some'+'thing']=10;
console.log(obj.something);
So you can freely have your own 'context' object (if you write the var obj={}; line in the top-level of a module, it will be available everywhere in that module, and it will not interfere with the outside world), and create/access its members using this array-like syntax (obj['something']), constructing the names on the fly when necessary.
I'm wondering if there's a Angular2 equivalent to the bracket notation in Javascript that lets you use variables to select an object property. Or a way of doing the same thing.
Consider this code:
let selector = "foo";
let someObject = {foo: "some content", guu: "some other content"};
console.log(someObject[selector]); //bracket notation, logs "some content"
I've tried the following in Angular2:
{{someObject[selector]}}
{{someObject['selector']}}
Neither worked. Is there another way of doing it without Javascript?
This appears to work just fine in Angular: https://stackblitz.com/edit/angular-mrky5n.
Some reasons that it may not be working in your current application:
1) If selector is a variable with a string selector in it, perhaps that selector variable is not available to the scope of the template (e.g. it's private on the component class, defined within a method, or lifecycle hook, etc.
2) If selector is the property you actually want to access, perhaps there is a typo in the name of selector -- maybe the real property is selectro or selecter. This is a reason to not use this approach, since you lose Typescript's type checking. someObject.selectorr throws an error while someObject['selectorr'] goes through fine.
3) That property has not received a value yet -- maybe the safe navigation operator would help.
Angular template syntax supports many JS features, including bracket notation.
{{someObject[selector]}} expression assumes that both someObject and selector are component properties. This won't work if they are local variables that are defined with let.
In Javascript, local variables do not live on any object that I'm aware of. That is,
function foo() {
const x = 2;
self.x; // undefined
this.x; // undefined
window.x; // undefined
x; // 2, obviously
eval('x'); // 2
}
The last option eval('x') shows that it is possible to refer to these variables by name. I'm looking to extend this and access a variable using a name pattern:
function foo() {
// Code not under my direct control
function foobar_abc() {}
function other_functions() {}
// Code under my control
const matchingFunction = // find the function with name matching 'foobar_*'
}
If this lived on an object, I would use something like myObject[Object.keys(myObject).find((key) => key.startsWith('foobar_'))]. If it were in the global scope, myObject would be window and everything works.
The fact that eval is able to access the variable by name implies that the value is available somewhere. Is there any way to find this variable? Or must I resort to techniques which re-write the (potentially very complex) code which is not under my direct control?
I'm targeting modern browsers, and in this use case I don't mind using eval or similarly hacky solutions. Arbitrary code is already being executed, because the execution of user-provided code is the purpose.
Another option is to use code parsing to deduce the function names using a javascript AST (abstract syntax tree) library. The "esprima" package will probably be good place to look:
https://www.npmjs.com/package/esprima
So you can do
import esprima from 'esprima'
const userCodeStructure = esprima.parseScript( userProvidedJavascriptString );
const allTopLevelFunctionDeclarations = userCodeStructure.body.filter( declaration => declaration.type === "FunctionDeclaration" );
const allTopLevelFunctionNames = allTopLevelFunctionDeclarations.map( d => d.id );
I haven't tried this myself by the documentation suggests it should work (or something like it):
http://esprima.readthedocs.io/en/latest/
One possible approach that might help you here is to evaluate at global scope rather than in a function, and that might put the functions on the window object instead of in local scope.
Easiest way to do this is probably to write a new tag into the document and inject the user-provided code.
Relying on variable names is the wrong approach.
eval is evil. It may not be available under CSP. Considering that the code is supposed to run in browser, the biggest problem is that variables don't have expected names in minified code. They are a, b, c...
In order to maintain their names, they should be object properties - and so they will be available on the object.
Or must I resort to techniques which re-write the (potentially very complex) code
This is refactoring and that's what should be done to avoid bad code that smells and creates major problems.
I'm looking at JavaScript that produces:
The related code is
My question is, what does $this refer to? Just the keyword "this" I understand, but $this? There doesn't seem to be any jQuery around.
Thanks for any illumination.
It has to do with the Google "jstemplate" mechanism.
From that page:
$this: $this refers to the JsEvalContext data object used in processing the current node. So in the above example we could substitute $this.end for end without changing the meaning of the jscontent expression. This may not seem like a very useful thing to do in this case, but there are other cases in which $this is necessary. If the JsEvalContext contains a value such as a string or a number rather than an object with named properties, there is no way to retrieve the value using object-property notation, and so we need $this to access the value.
It appears to be prefixed with $ due to using the Google JSTemplate API.
More information here: http://code.google.com/p/google-jstemplate/wiki/HowToUseJsTemplate
That is not javascript, it’s HTML.
What you see is a custom element property called jsdisplay that has the value of $this.something. What it actually does is very hard to give an exact answer to, but as some other pointed out it’s probably used internally in google templating.
Pointy is right for this specific case.
To clear up the confusion about $ in JavaScript:
In JavaScript the dollar sign ($) in variable names is treated like a-z, A-Z and underscore (_).
The variable you are looking at, could have been named anything else. $this is no special JS keyword. The developers of jstemplate could have named it foo if they wanted to. Or like they did, something similar to this, like _this or self.