I'm not sure if the title is correctly conveying the feature i'd like clarity on. But let me lay it out.
In javascript we can instantiate a variable to an empty object:
var person = {};
We can easily set properties to it
person.name = "Bob"
We can't, however, assign objects by using properties:
person.pet.name = "Bob"
> Uncaught TypeError: Cannot set property 'name' of undefined
In angular you can. This modest code for example. I have a controller:
angular.module('someModule', [])
.controller('someController', [
Callback
]
function Callback(){
this.person = {}
}
Then the view contains:
<input type="text" data-ng-model='vm.person.pet.name'>
Where vm is the viewmodel representing the controller. When I log the results grabbed from the input you get a person object with a pet child object with a property of name
What gives? Why did angular add this feature?(i assume feature) Is it strictly for flexibility?
From the docs:
Forgiving
Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating a.b.c throws an exception if a is not an object. While this makes sense for a general purpose language, the expression evaluations are primarily used for data binding, which often look like this:
{{a.b.c}}
It makes more sense to show nothing than to throw an exception if a is undefined (perhaps we are waiting for the server response, and it will become defined soon). If expression evaluation wasn't forgiving we'd have to write bindings that clutter the code, for example: {{((a||{}).b||{}).c}}
Similarly, invoking a function a.b.c() on undefined or null simply returns undefined.
In short, expression in html <input type="text" data-ng-model='vm.person.pet.name'> doesn't change in time, however an actual value of vm.person.pet.name does.
I think this is a feature. Imagine that you have an array of objects of different types e.g. people and animal.
people object:
{
name: "someName",
lastname: "someLastName
}
and
animal object:
{
name: "someName"
//Without lastname
}
If you put an array of objects, which contains these 2 types of objects, to the ng-repeat you could easily avoid code crash if you try to access some property which is not part of the every object.
Related
I couldn't find any clear answer, so what's the difference between Objecttype and extend type in JavaScript?
In AngularJS, when I consoled $state:
console.log($state)
console.log($state.$current.parent)
I got the output as:
As you can see, the $state is interpreted as an Object where the latter is an extend?
I needed the parent name of current state, so I was planning to take it from the self.name object of $state.$current.parent. Is that okay? Or should I keep out of extend?
They both are objects. Chrome's console additionally tells you their type. The first one it's a generic object whereas the second one is still an object, but named instead.
Here's how you can create named objects very easily:
function NamedObject() { this.aProperty = 'something'; }
var namedObject = new NamedObject();
Generic objects are created like this:
var genericObject = { aProperty: 'something' };
In your specific case, uiRouter uses angular.extend internally. If you were to assign a name to this anonymous function here you'd see how extend will be renamed to the function's name.
I have an odd situation in my Javascript code which I cannot explain. So far it has been observed only on Safari/Mac.
I have a line like this:
dict[id].sliceHovered = true;
And sometimes it throws an error:
Attempted to assign to readonly property.
Also:
dict is a bare blank object which I create myself with dict={}.
id is supplied by outside data, so it can be anything (I don't yet know which particular value causes this).
sliceHovered is obviously not a name of something that Javascript has built
in.
The objects in the dict are of my own type. They have a sliceHovered member, but it's not a Javascript defined property (as in Object.defineProperty()), just a regular property (the constructor executes this.sliceHovered=false).
"use strict" is on.
Object.freeze(), Object.seal(), Object.preventExtensions() and const are not used anywhere in the entire codebase.
Thus it's extremely puzzling as to how such an error could be thrown here. If I had an indexing error and dict[id] would be undefined or null, the error would be different. My only idea is that since the dict is created as dict={} then it inherits from Object and maybe id maps to some inherited property. But that means that the object returned from dict[id] would have to be read-only itself, because sliceHovered is definitely not a name of an existing Javascript property.
However I cannot think of any Javascript objects that would be intrinsically read-only like that.
Any ideas what could be wrong here?
You can check this situation
My only idea is that since the dict is created as dict={} then it inherits from Object
with: var dict = Object.create(null);
Also try to use Object.getOwnPropertyDescriptor(dict, id) to make sure descriptors have right values.
I have slightly modified the example from the following URL (http://docs.angularjs.org/cookbook/helloworld) as follows, placing the name value within an attrs object property:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
<script>
function HelloCntl($scope) {
$scope.attrs = {
name : 'World'
}
}
</script>
</head>
<body>
<div ng-controller="HelloCntl">
Your name: <input type="text" ng-model="attrs.name"/>
<hr/>
Hello {{attrs.name || "World"}}!
</div>
</body>
</html>
One benefit I can see is that the HTML source code can be searched for /attrs\.\w+/ (e.g.) if there is ever a need to easily find all such attributes within the view rather than the controller (e.g. a search for name could collide with form element names). Also within the controller I can only imagine that partitioning attributes necessary for the front end might lend itself to better organization.
Is anybody else using such a level of abstraction. Are there any possible specific further benefits to it's usage? And most importantly, might there be any specific drawbacks to it.
It's recommended that you always use a dot in your ngModels in order to avoid potential issues with prototypal inheritance that are discussed in Angular's Guide to Understanding Scopes:
This issue with primitives can be easily avoided by following the
"best practice" of always have a '.' in your ng-models – watch 3
minutes worth. Misko demonstrates the primitive binding issue with
ng-switch.
Prototypal inheritance and primitives
In javascripts' approach to inheritance reading and writing to a primitive act differently. When reading, if the primitive doesn't exist on the current scope it tries to find it on any parent scope. However, if you write to a primitive that doesn't exist on the current scope it immediately creates one on that scope.
You can see the problem this can cause in this fiddle that has 3 scopes- one parent and two children that are siblings. First type something in the "parent" and you'll see that both children are updated. Then type something different in one of the children. Now. only that child is updated, because the write caused the child to creates it's own copy of the variable. If you now update the parent again, only the other child will track it. And if you type something into the sibling child all three scopes will now have their own copies.
This can obviously cause lots of issues.
Prototypal inheritance and objects
Try the same experiment with this fiddle in which each ngModel uses a property of an object instead of a primitive. Now both reading and writing act consistently.
When you write to a property of an object it acts just like reading does (and the opposite of how writing to a primitive does). If the object you're writing to does not exist on the current scope it looks up it's parent chain trying to find that object. If it finds one with that name then it writes to the property on that found object.
So, while in the primitive example we started with 1 variable and then after writing to the children ended up with 3 copies of the variable- when we use an object we only ever have the one property on the one object.
Since we almost always, perhaps just always, want this consistent behavior the recommendation is to only use objects properties, not primitives in an ngModel or, said more commonly, "always use a dot in your ngModel"
I do this as well. I also put all action functions (button clicks etc) into a $scope.actions object. And since i use socket.io i put those callbacks into a $scope.events object it usually keeps my controllers nice and organized and easily able to find the function i need to if i need to do any editing.
app.controller('Ctrl',['$scope', function ($scope) {
$scope.data = {
//contains data like arrays,strings,numbers etc
};
$scope.actions = {
//contains callback functions for actions like button clicks, select boxes changed etc
};
$scope.events = {
//contains callback functions for socket.io events
}
]);
Then in like my templates I can do like
<input ng-click="actions.doSomething()">
I also do a partial of this for services. I use a private and public data object
app.factory('$sysMsgService',['$rootScope',function($rootScope){
//data that the outside scope does not need to see.
var privateData = {};
var service = {
data:{
//contains the public data the service needs to keep track of
},
//service functions defined after this
};
return service;
}]);
At one popular blog, the author asked his audience what was their “Ah ha!” moment for JavaScript and most of the people said that it was realizing that everything in JavaScript is an object.
But being new to JS and programming in general I don't quite get what that means. It doesn't seam like it's related to actual JS Object - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
Or it does?
If it doesn't could you please explain what do they mean by "everything in JavaScript is an object".
Or it's all about OO Programming in general and reading something on this subject will help to understand it? Can you recommend what to read on this topic?
Go back to first principles.
What's an object? It's a software component that encapsulates state and behavior together into a single entity in memory.
By that definition, you can see where everything can be thought of as an object. Functional programmers make functions first class objects. Data folks would say that data, even that without behavior, can be thought of as an object (albeit not a very smart one).
I don't see what this changes.
JavaScript treats functions as objects.
I'm not sure what effect this insight will have on your programming.
What they mean, most likely, is that any data that can be assigned to a variable has properties (and therefore methods) that can be accessed in an object like fashion.
// strings
"asdf".length; // 4
"asdf".replace('f', 'zxc'); // "azxc"
// numbers
(10).toFixed(2); // "10.00"
// booleans
true.someProp; // undefined (point is it doesn't crash)
They even have prototypes they inherit from.
"omg".constructor; // function String() { [native code] }
String.prototype.snazzify = function() {
return "*!*!*" + this + "*!*!*";
};
"omg".snazzify(); // "*!*!*omg*!*!*"
However, these are primitives, and while they behave object like in a lot of ways, they are different from other "real" JS objects in a few ways. The biggest of which is that they are immutable.
var s = "qwerty";
s.foo; // undefined, but does not crash
s.foo = 'some val'; // try to add a property to the string
s.foo; // still undefined, you cannot modify a primitive
Do note though that functions are real mutable objects.
var fn = function(){};
fn.foo; // undefined
fn.foo = 'some val'; // try to add a property to the function
fn.foo; // "some val"
So while it's not technically true that "everything in JS is an object", under most circumstances you can treat them mostly like objects in that they have properties and methods, and can potentially be extended. Just be sure you understand the caveats.
NOT everything in JavaScript is an object.
All data in JavaScript must fall into one of six primitive types, or the object type. Primitive types includes boolean, null, undefined, string, number and symbol; everything that is not a primitive is an object.
This means functions are objects, arrays are objects, ES6 classes de-sugars into a function which are objects.
The confusion arises because primitive values have object wrappers. When you try to access the length property on the string literal, JavaScript creates a temporary object wrapper around the primitive and access the length property of that object wrapper. After the property has been retrieved, the object wrapper is discarded. This is known as autoboxing.
Essentially, it is implemented similar to the following:
const foo = "bar";
// When you run `foo.length`, it's similar to
tmp = String(foo);
tmp.length;
delete tmp;
Or
const foo = "bar";
(new String(foo)).length;
The string, number and boolean primitives have object wrappers, but null and undefined do not. So trying to access a property or method from those primitives would throw an error.
null.length; // Uncaught TypeError: Cannot read property 'length' of null
undefined.length; // Uncaught TypeError: Cannot read property 'length' of undefined
Not everything is an object in JavaScript. For example a number is not an object.
Different languages have different definition of "object". For JavaScript, the official definitions can be found in the ECMAScript Language Specification, which states:
In ECMAScript, an object is a collection of zero or more properties
each with attributes that determine how each property can be used (...) Properties are containers that hold other objects,
primitive values, or functions. A primitive value is a member of one
of the following built-in types: Undefined, Null, Boolean, Number,
BigInt, String, and Symbol; an object is a member of the built-in type
Object; and a function is a callable object.
So by this definition it is clear that primitive values (like numbers) are not objects. Even strings are not objects, which is different from most other OO languages.
So why do some people say "everything is an object"? You have to ask them! But I suspect it is because they are getting confused by the built-in objects Number, Boolean, String etc. which can be used as wrappers around the corresponding primitive values. This wrapping sometimes happen automatically, which can make primitive values look like object and for example allow you to (seemingly) access properties on primitives. But in reality the properties are on the wrapper object.
'ALMOST everything is an object' because the MAIN code-units are JS-objects. On primitives you can NOT add members for example as on all objects. My answer why JS-functions are JS-objects here: https://stackoverflow.com/a/24811539
Why are arrays in a Backbone.js Model essentially static variables?
class exports.Content extends Backbone.Model
tags: []
then if i make a few models:
contentA = new Content()
contentB = new Content()
and add one string to each models' array:
contentA.tags.push('hello')
contentB.tags.push('world')
they both end up with the same array:
contentB.tags // ['hello','world']
but if it's a string, then there is no problem:
contentA.name = "c1"
contentB.name = "c2"
contentA.name // "c1"
The short answer
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
The long answer
In spite of your code being in CoffeeScript, this comes down to a combination of a few things in JavaScript:
There are no classes in JavaScript
Object literals are evaluated immediately
JavaScript objects are passed around by reference
In JavaScript, there are no classes. Period. CoffeeScript gives you the notion of a class, but in reality, it gets compiled down to JavaScript which has no classes.
You can have types and type definitions (constructor functions) but not classes. Backbone provides a class-like definition, that looks similar to Java's "extend" class-based inheritance. It's still just JavaScript, though, which has no classes.
What we have, instead, is an object literal being passed in to the extends method. It's as if you write this code:
var config = {
tags: []
}
var MyModel = Backbone.Model.extends(config);
In this code, config is an object literal, or a hash, or a key/value pair, or an associative array. Whatever name you call it, it's the same basic idea. You end up with a config object that has a tags attribute. The value of config.tags is an empty array, [], which is itself an object.
Which brings us back to the short answer:
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
"tags" is being declared on the Content.prototype - which is shared across all instances of Content. You probably need to use defaults instead: http://backbonejs.org/#Model-defaults. However, it should be noted that you must use a function (instead of a hash) when defining defaults, otherwise you still run into the same problem when using types which are passed by reference (e.g. Array).
var Content = Backbone.Model.extend({
defaults: function() {
return {
tags: []
};
}
});
benpickles is correct when it comes to Backbone models, and the way to do this in coffeescript in general is to initialize instance properties in the constructor:
class Foo
constructor: ->
#bar = []
As Derick Bailey mentioned, pass by reference is the problem. I solved the problem by cloning the Array (passing-by-value essentially):
var _events = window.active_model.get('events').slice(0);
_events.push({ key: "xxx", value: "yyy" });
window.active_field.set('events', _events);