i'm reading the book 'Secrets of the JavaScript Ninja' (http://jsninja.com/) and wonder why a certain code block from one of the examples is coded like it is.
The example (http://jsfiddle.net/3s5bopqe/3/) builds a custom array type (MyArray) and reuses a number of functions from the Array.prototype without actually inheriting from the 'Array' type.
In the example from the book a function with a certain name is reused in the prototype of the custom type using this code
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
I believe i understand what happens here. But it seems unnecessary to me to actually use a function literal and explicitly 'apply' a function from the Array.prototype. The following code works just as well in the test setting :
MyArray.prototype[ name ] = Array.prototype[ name ];
My question is whether there are any advantages of using the code from the book over the last code block ?
By using
MyArray.prototype[ name ] = Array.prototype[ name ];
you are actually copying method from Array into your object (MyArray).
In future, if Array method changes, your MyArray method wont be updated.
By using
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
you referred to Array method. Changes made in Array object methods are reflected in your MyArray methods. Why? Because you never copied that method, you just borrowed it (referred to its current implementation).
There's one major difference I can see. If any of the targeted methods on Array.prototype gets altered after you copied them (e.g. applying a polyfill that fixes behavior) the changes will not be reflected using MyArray.prototype[ name ] = Array.prototype[ name ]; while it would using the jsninja approach.
Depending on your point of view, you may find one or the other behavior more fitting.
Related
Attempting to assign a scope object to a JavaScript variable to do minor manipulation before sending to my API. However, any changes made to the JavaScript variable change the scope object.
var recruitingCallListOutput = $scope.RecrutingCallingList.Recruit;
// manipulation of recruitingCallListOutput
The manipulation actually still updates the scope object which is not desired. Feel I am not understanding something in AngularJS correctly. Is there a way to grab the data and detach it from the scope?
In your example, recruitingCallListOutput is a reference to $scope.RecrutingCallingList.Recruit (see https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 for more detail.) You will want to make a copy of $scope.RecrutingCallingList.Recruit.
If Recruit is a shallow object, meaning no nested objects (property values are primitives only), you can simply do
var recruitingCallListOutput = Object.assign({}, $scope.RecrutingCallingList.Recruit);
If you have nested objects/arrays as property values, you'll need to deep copy. It's been a while since I have been in the angular world, but
var recruitingCallListOutput = angular.copy($scope.RecrutingCallingList.Recruit)
you could actually use angular.copy in both examples.
This is nothing to do with AngularJS. It's Javascript, and it's expected behaviour.
For example, if you open the browser console (F12->Console) right now and run this:
var foo = {x:1};
var copy=foo;
copy.x=2;
console.log(foo.x);
you will see {x:2} printed out.
This is the same behaviour you would expected for any object reference in Javascript, C#, Java, etc. Because you are making a reference and not a copy, any changes to the reference are actually changes to the original.
The simplest way to solve this problem in your case is to copy the values you are interested in from the item in question into a totally separate object and modify that copy.
e.g.
var recruitingCallListOutput = {
name: $scope.RecrutingCallingList.Recruit.name,
age:$scope.RecrutingCallingList.Recruit.age,
modifiedSomething: $scope.RecrutingCallingList.Recruit.something + 42 //or whatever modifications you need to make
...and so on.
};
There are ways to "clone" an object in Javascript but unless your object is really really complex I would be careful. And consider if you really need all of the properties of the original object anyway, perhaps you only need to send some of them to your backend.
I am reading about getters and setters in javascript. I would like to know if there is a difference between this two ways of coding with and without setters
first way, without setters.
>obj1 = {
arr: [];
}
>obj1.arr.push('first')
>obj1.arr
[ 'first' ]
Second way, with setters.
>obj2 = {
set add(data) {
this.arr.push(data);
},
arr: []
}
>obj2.add = 'first'
>obj2.arr
[ 'first' ]
The setter syntax in your example does not really prevent the client code to still add a value using the direct push call as in the first code block. So the difference is that you just added another way to do the same thing.
To make a fair comparison, you would have to define the same method in both alternatives: once as a normal method and once as a setter method, and then the difference is just the syntax how the argument is passed to the method, either with obj.add('first') or obj.add = 'first'.
In this actual case I would vote against the setter, because it gives the false impression that if you "assign" another value, the first assigned value is overwritten:
obj.add = 'first';
obj.add = 'second';
... but obviously this is not the case: both values exist in the object now.
First, The set Syntax bind an object property to a defined function. In this particular example, there is no difference between two codes, but let's say for example you want to check if the value is negative before adding it to the array, so you can use set to Encapsulate that behavior.
So basically, using setter is only to add additional encapsulated behavior to the functions of the objects.
The way of accessing the array index called bracket notation. it is equal to dot notation, except the bracket notation allows you to set new properties to objects or arrays dynamically.
Hope this help you.
I think difference only about "how it looks like". Using setters it's closest way for understanding for people who came to js from object oriented languages.
The getter/setter property is not the same as "normal" instance property, one is called "named data property", the other is called "named accessor property".
Please let met quote below part of documents from the ECMAScript 5.1.
https://www.ecma-international.org/ecma-262/5.1/#sec-8.10.1
An Object is a collection of properties. Each property is either a
named data property, a named accessor property, or an internal
property:
A named data property associates a name with an ECMAScript language
value and a set of Boolean attributes.
A named accessor property associates a name with one or two accessor
functions, and a set of Boolean attributes. The accessor functions are
used to store or retrieve an ECMAScript language value that is
associated with the property.
An internal property has no name and is not directly accessible via
ECMAScript language operators. Internal properties exist purely for
specification purposes.
There are two kinds of access for named (non-internal) properties: get
and put, corresponding to retrieval and assignment, respectively.
And
If the value of an attribute is not explicitly specified by this
specification for a named property, the default value defined in Table
7 is used.
I'm struggling a lot with Javascript prototyping; I don't seem to understand when I should be declaring an attribute in the prototype vs instantiation. Particularly, I don't understand, why in my code attached (a basic exercise in building a little Router class to treat a document more like an app than a page), attributes set at instantiation are persisting and thus accumulating in what I intend to be separate objects entirely.
Mostly this is a learning exercise, but I've scaled back some of the original code to help with contextual obfuscation*.
Code is here: http://codepen.io/anon/pen/xGNmWM
Basically, each time you click a link, the data-route attribute on the element should be picked up an event listener, a new Route object should be instantiated (and passed information about the intended route); finally the Router class should actually "launch" the Route, ie. make an ajax request or do some in-document stuff, whatever.
Right now, the Route.url.url attribute should be, in my obviously wrong understanding, created anew each time and then informed by passed data. Somehow, this attribute is persisting and thus accumulating passed information from each click.
I truly don't understand why.
**I have not removed anything that would effect my question; really it could be trimmed back even more but I realize the integrity of the question relies on a reasonable facsimile of the original code.
You have two problems.
By Value vs By Reference
In Javascript primitive types, as numbers, booleans and strings, are passed to another functions or set to another variable by value. That means that its value is copyed (cloned).
The object types, as objects, arrays and functions, are passed to another functions or set to another variable by reference. That means that variables of this type are just references to a content to memory. Then when you set an object to a variable, just its memory address is being copied.
When you pass the "route_data" its reference is copied. Then the Route constructor is working on same variable hold by Router.
If you clone your object before pass it the problem is solved.
...
var route_data = this.route_data[ route_name ];
route_data = $.extend(true, {}, route_data); // Clone object using jQuery
var route = new Route( route_name, route_data, request_obj);
...
Prototype
The Javascript has a prototypal inheritance, that means that each object points to its prototype that is another object.
var obj = { name: 'John' };
console.log(obj.__proto__);
All objects root prototype is Javascript Object by default. As noted by example above.
function Person(name) {
this.name = name;
}
Person.prototype = {
getName: function() {
return this.name;
}
}
var obj = new Person('John');
console(obj.getName());
console(obj.__proto__);
console(obj.__proto__.__proto__);
When you use new a new empty object is created and binded as this to specified function. Additionally its object prototype will point to the object specified on prototype of called function.
On get operations the Javascript engine will search on entire prototype chain until specified field is found.
But on set operations if the specified field does not exist on current object, a new field will be created.
When you define the url field on Route this should be static, because when you try to change it a new field is created.
If you verify your Route instance you will note that you have created duplicated url fields. One on object itself and another on prototype.
I would have really appreciated a minimal code example posted here on SO rather than codepen. It would have saved me some time reading your code (you're not paying me for this after all).
Anyway, this is problematic:
Route.prototype = {
// etc..
url : {url: false, type: "get", defer: false}
// etc..
}
Basically, what you're doing is this:
var global_shared_object = {url: false, type: "get", defer: false};
Route.prototype.url = global_shared_object;
Do you see the problem? Now when you do:
var route1 = new Route();
var route2 = new Route();
Both the .url property of route1 and route2 point to the same object. So modifying route1.url.url will also modify route2.url.url.
It should be noted that route1.url and route2.url are different variables. Or rather different pointers. Modifying route1.url will not modify route2.url. However, the way they've been initialized makes them both point to the same object so modifying that object can be done from either pointer.
The key to making this work is to create a new object for .url every time you create a new object. That can be done either in the constructor or in your case your .init() method:
Route = function (name, route_data, request_obj) {
this.url = {url: false, type: "get", defer: false};
this.init(name, route_data, request_obj);
}
Implied Lesson:
The lesson to take from this is that the object literal syntax is actually an object constructor. Not just a syntax.
// this:
{};
// is the same as this:
new Object();
So every time you see an object literal, in your mind you should be thinking new Object() then see if it makes sense to call it just once or do you need a new instance of it every time.
I think the Function object is the only instance in the JavaScript. All the other function(){...} or var xx=function(){...} are the objects inherited from this Function object. That is the prototype based language feature. And in prototype based language there is no Class, and One of the advantages of it is the object can changed anytimes.such as add a method or add a property into it. So, I think we can treat object like a key-values collection. You can add items into it anytimes after you created it. (If I was wrong, Please correct me.)
Say you have the code:
Function.test=function(){
alert(Function.name);//"Function"
alert(Function.myName);//"fun"
};
Function.name="fun";
Function.myName="fun";
Function.test();
In the above code, all I want to do is add a test method to the Function object.
There is no error found in this code. But why Function.name can not be changed?
First of all this property is not standard. This means that different browsers may treat it as they want. The second thing is that according to MDN it is read only.
A page from MDN states that the name of the Function cannot be changed because it's read only.
Lets say you have this object:
mainObj = {
foo1: 'bar1',
foo2: 'bar2',
foo3: 'bar3'
}
Now I want to make a close of this object by doing cloneObj = mainOb. Now we have two identical objects.
When I change the value of mainObj.foo1 = 'lolcats' after I made the clone then for some reason cloneObj.foo1 = 'lolcats'
I tested this in Chrome's console on a much more complex object. I know for certain that there's nothing in my script that would keep making the two objects sync up. I even made sure of this by creating random names for the cloneObj.
Is this behavior done on purpose or am I experiencing some sort of bug? Or am I just missing something very fundamental here?
You didn't clone the initial object when you just did cloneObj = mainOb, you are actually passing a reference to mainOb that can be accessed via the cloneObj variable name. You therefore have two variable names referencing the SAME object.
when you assign/clone object such as cloneObj = mainOb you just create another reference to the same object. Both mainOb and cloneOb points to the same object thus, a change in one is reflected on another.