In this simple example, why do new and Object.create behave differently?
var test=function(name){
this.name=name
};
var test1= new test("AAA");
test1.name;//AAA
var test2=Object.create(test);
test2.name="AAA";
typeof(test2);//Object
test2.name;//"" (empty string).
Why is test2.name empty?
Object.create expects an Object as it's first argument for the prototype chain, not a function (or constructor in your case).
It won't complain if you pass a function, but it means that certain extra things will be inherited by your created Object, for example, the non-writability of function names.
The reason you're getting an empty string is because test is an anonymous function, so test.name is "". As I said above, this is non-writable, so
test.name = 'foo';
test.name; // still ""
If you had used a named function expression for test, this would have been more obvious.
var test = function foobar() {},
ex = Object.create(test);
ex.name; // "foobar"
EDIT a function that behaves like new for test using Object.create would look like this
function myNew() {
var o = Object.create(test.prototype); // set up prototype inheritance
test.apply(o, arguments); // then construct
return o;
}
// and using it
var test3 = myNew('AAA');
test3.name; // "AAA"
test3.name = 'BBB';
test3.name; // "BBB"
This pattern is not guaranteed to work with DOM constructors.
The word "name" is "almost reserved" in JavaScript. If you try a normal attribute name, it should work. For example,
var test=function(name){this.name=name};
var test1= new test("AAA");
test1.name;//AAA
var test2=Object.create(test);
test2.name2="AAA";
typeof(test2);//Object
console.log(test2.name2);//"AAA"
For difference between the two ways of creating objects, this page shows some examples with explanation.
Related
Does anyone know what is test[name] mean?
function test(value){
copy(value||{},this);
}
test[name] = function(){
return "test"
}
This will be easiest to explain with an example:
var name = "foo";
test[name] = function(){
return "test"
};
This would add a property named "foo" to the object test, and the value of that property is a function. It doesn't matter in this case that the object test is actually a function, you can assign properties to functions just like any other object in JavaScript.
You could call this function using any of the following methods:
test[name]()
test["foo"]()
test.foo()
Note that test[name]() will not work if the name variable is assigned to something different, for example name = 'bar'.
Javascript has two sets of notation for accessing objects, dot notation (obj.property) and bracket notation (object[property]). More on that at MDN.
test[name] = function (){} assigns an anonymous function to the name property on the the test object (which itself is a function). In this case (as noted by the comments) the variable name is being used to access the property.
This may seem a little strange at first, but it's helpful to remember that in javascript, functions are objects.
All functions in Javascript are also objects. This adds a property to the test function object with a value which is an anonymous function.
For example:
function test(){
return "foo";
}
// test is a function, so it is also an object and
// it can have properties assigned to it
test.x = function(){
return "bar";
};
test(); // "foo"
test.x(); // "bar"
Of course just like with any object you can also use bracket notation:
var name = 'hello';
test[name] = function(){
return "HELLO!";
};
test.hello(); // "HELLO!"
In JavaScript, functions are objects. They have properties. test[name] sets a property (named whatever the name variable holds) to a function.
when you have a javascript object with defined properties you can access the property either with the dot notation obj.property or with the square brackets notation obj[property]
the property could also be a function so if you have an object:
var test = {
foo : function(arg){ console.log(arg) },
bar : 'hello'
};
you can call test.foo('bar') also by doing test['foo']('bar')
This is especially useful in iterations or when you dont know a priori what the name of the property is going to be. For example:
var fun = 'foo';
test[fun]('hello world');
Naturally it's up to you to do proper checks such as
if ('function'==typeof test['foo']){ test['foo']('bar'); }
Also note that you can do checks on the fly like so:
test[ fun || 'foo']('hello');
Taken from the Mozilla page
One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table). The keys in this array are the names of object members
There are two ways to access object members: dot notation and bracket notation (a.k.a. subscript operator).
So
test[name] = function (
means: there are (if everything is ok) two objects: test and name (and we know that at least test is present, because you defined it one line before: function test(value))
take the test object (if there isn't a test object an error will happen). Then access the key/value pair with the key calculated from the name object and there put a function.
Now, how the key is calculated from the name object? The same page from before tells us:
Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method.
Note that the description is a little wrong... test[null] == test["null"] and test[undefined] == test["undefined"], so perhaps the truth is that under the covers something like String(key).valueOf() is done (the String function will convert null to "null" and undefined to "undefined")
Some examples (where => means "is equivalent to, with this values")
var name = 'foo';
test[name] => test['foo']
var name = 123;
test[name] => test['123']
var name = 123.3;
test[name] => test['123.3']
var name = new Date();
test[name] => test['Wed Aug 14 2013 17:35:35 GMT+0200 (...)']
var name = null;
test[name] => test['null']
var name = undefined;
test[name] => test['undefined']
var name = [];
test[name] => test['']
var name = [1,2,3];
test[name] => test['1,2,3']
var name = {};
test[name] => test['object Object']
and so on...
The brackets are how you reference a property via a key into the hash that javascript objects are.
I'm trying to understand the fundamentals of object literals
window.Test = ({
attribute1:'This is',
attribute2:'a test',
function1: function(){
alert(this.attribute1 + ' ' + this.attribute2);
}
});
//TEST 1
Test.function1(); // "This is a test"
//TEST 2
var tester = new Test(); //ERROR: Object is not a function
tester.function1();
If I understand correctly window.Test = creates a global variable in the window object.
It would be the same as var Test = . Therefore it's logical that TEST1 works.
In TEST2 I try to instantiate the literal but this is not possible because it's a variable. I got the idea from Backbone.js where the following code does work:
window.Product = Backbone.Model.extend({});
var p = new Product();
I would expect Product to be a variable here as well, so no way to instantiate it.
Can anyone explain the difference between the Backbone example and my example? It seems I'm missing out on some fundamental understanding of literals.
First of all, this has nothing to do with JSON. That is a text format for representing objects. What you have is an object literal in Javascript. The JSON format is based on the Javascript syntax, but it's not the same thing.
You can't instantiate an object literal, because it is already an instance.
What the backbone extend method does is that it creates a function that will create an object. The extend method will return a function that you can use to create an instance because it will copy everything from the Model object into its own object.
yes it is not a function, it is a js object and to clone it use this :
window.Test = ({
attribute1:'This is',
attribute2:'a test',
function1: function(){
alert(this.attribute1 + ' ' + this.attribute2);
}
});
//TEST 1
Test.function1(); // "This is a test"
//TEST 2
var tester = $.extend(true,{}, Test);
tester.function1();
Reference : here
Fiddle : here
object literals are already an instantiated object
var a = {}
is in this regard equivalent to:
var a = new Object()
so new {} is equivalent to new (new Object())
the new-keyword instantiates object from constructors. You can't instantiate something from an object that already is an instance.
Kind of strange thing is happening with my code. What I observe is something like this:
var prototype = {
property : "",
array : []
}
var objectArray = [];
function myFunction {
objectArray[0] = Object.create(prototype);
objectArray[1] = Object.create(prototype);
objectArray[0].property = "Hello 1";
objectArray[0].array.push("Hello 1");
objectArray[1].property = "Hello 2";
objectArray[1].array.push("Hello 2");
// At this point when running the program I get:
// objectArray[0].property == "Hello 1"
// objectArray[1].property == "Hello 2";
// objectArray[1].array == objectArray[1].array == prototype.array
// == ["Hello 1", "Hello 2"]
}
What I want, and expected, was two separate arrays for the two objects. What am I missing here?
In JavaScript, objects are copied by reference, so both objectArray objects are simply references to the same object ( prototype ). You need to clone the object or create instances using the new keyword and a constructor to create separate objects.
Example on how to do it using the new keyword:
var prototype = function() {
this.property = "";
this.array = [];
};
objectArray[0] = new prototype();
objectArray[1] = new prototype();
You can also do:
var prototypeFactory = function() {
return {
property: "",
array: []
};
};
objectArray[0] = prototypeFactory();
objectArray[1] = prototypeFactory();
The prototype object exists the same as the [[Prototype]] for each object. They don't get a fresh copy when you use Object.create().
You'd need to use assignment, which never walks the prototype chain.
I wonder if you aren't asking yourself "why does it work for property, but not for array?". The fact is, it doesn't work for property either, JavaScript is fooling you.
When objects share the same prototype, one must consider that:
All properties of the prototype are shared by the objects that inherit from that prototype.
You cannot assign to properties of the prototype, only read them (unless it's an accessor property, but let's keep that aside).
So what's actually happening here:
objectArray[0].property = "Hello 1";
objectArray[1].property = "Hello 2";
is that a new own property called "property" is being created on each object, while prototype.property remains untouched. The array property is behaving differently because you're not assigning to it, you're accessing prototype.array and calling the push method on it.
Don't forget that Object.create() isn't yet standardized, and won't work in IE8 or FF3.6. A simple work-around to clone an object is to use the JSON object:
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
I have a list of arguments such as var args = ['blah', 1, 3.9] and I want to apply it to something that needs to be newed like new bleh.Thinggy(a, b, c).
I want to do the following var m = {}; bleh.Thinggy.apply(m, args);
I am worried there is something I am not thinking of does anyone know if this is safe?
Your current method is flawed, because prototype inheritance will not work as expected.
The equivalent of method.apply(context, args) for constructors is:
// Given a list of arguments `args`:
var bindArgs = [Constructor.prototype].concat(args);
new (Function.prototype.bind.apply(Constructor, bindArgs));
The roles of Function.prototype.bind and Function.prototype.apply are explained at the corresponding documentation. Remember: .bind returns a function!
To keep it simple, I'll explain how to use .bind for a fixed number of arguments, say two. Then, the following have the same effect:
Math.max.apply(Math, 2, 3);
Math.max.bind(Math, 2, 3)();
And
Math.max.apply(Math, 2, 3, 4, 5);
Math.max.bind(Math, 2, 3)(4, 5);
If you've even glanced at the documentation, you'll certainly understand the first form. The second form is trickier though. It works in this case, because the position of an argument in Math.max is not relevant. The maximum value of all arguments is considered, where all arguments are treated identically.
Now, here follows an example with a custom function:
function echoMe(name, age) {
console.log('Hello ' + name + '. Your age is ' + age);
console.log('this is ', this);
}
echoMe('Rob', '19');
// "Hello Rob. Your age is 19"
// "this is [object DOMWindow]" (non-strict mode)
var echoYou = echoMe.bind(null, "Oops");
echoYou("Peter", "19");
// "Hello Oops. Your age is Peter"
// "this is null"
Because the position of arguments is significant in this case, the last example showed something weird. Indeed, the first argument is bound to "Oops" by the .bind method. The arguments passed to the bound function echoYou are appended to the arguments list. Additionally, you notice that the context this was changed to null.
Interesting.. Let's try to change the context using .apply:
function printThisFood() {
console.log("this.food is " + this.food);
}
printThisFood.apply({food: "Fish"});
// "this.food is fish"
var locked = printThisFood.bind({food: "Strong"});
locked.apply({food: "Weak"});
// "This.food is Strong"
As you can see, this.food still points to method from the context as defined through .bind!
So, we know how to lock the context of a function, as well as passing an arbitrary number of fixed arguments to a function. This can be applied to constructors, resulting in the function which I presented on top of the answer. To verify that it works as expected:
function Constructor() {
console.log(this instanceof Constructor); // true
console.log(this, arguments); // Convince yourself via console
}
var bindArgs = [Constructor.prototype].concat([1, 2]);
// is equal to [Constructor.prototype, 1, 2]
var BoundConstructor = Function.prototype.bind.apply(Constructor, bindArgs);
var instance = new BoundConstructor();
// Eliminated intermediate variable, and put on one line
var instance = new (Function.prototype.bind.apply(Constructor, bindArgs));
Note: I omitted parentheses () in the one-liner, because constructors can be initialized without these. new Image and new Image() are behaving identically.
To immediately read a property (or invoke a method) from the constructed method, you can either wrap the whole expression in parentheses or append () to remove ambiguity:
(new (Function.prototype.bind.apply(Constructor, bindArgs))).method()
new (Function.prototype.bind.apply(Constructor, bindArgs))().method();
Note 2: It still holds that additional arguments are appended to the argument list. This property can also be used to "preset" the first arguments of a given constructor:
function Stupid(obvious1, obvious2, foo) { this.interesting = foo; }
Stupid.prototype.onlymethod = function() { return this.interesting};
var saveKeyStroke = Function.prototype.bind.call(Stupid, Stupid.prototype, 1, 2);
// Or, equivalent:
//var saveKeyStroke=Function.prototype.bind.apply(Stupid,[Stupid.prototype,1,2]);
new saveKeyStroke('Fourth argument').onlymethod(); // "Fourth argument"
new saveKeyStroke().onlymethod(); // undefined
(new saveKeyStroke).onlymethod(); // undefined
Apply is safe,but it would not return any instance of bleh.Thinggy.
If you want instance of bleh.Thinggy then you have to create it before using bleh.Thinggy.apply.
code:
var m = new bleh.Thinggy; // m is instance of bleh.Thinggy.
bleh.Thinggy.apply(m, args);
... use m as instance of bleh.Thinggy.apply
When I do:
var person = new Object();
person.name = "alex";
console.log(person)
output is:
Object { name="alex"}
However, say I drop the "new" word and do:
var person = Object();
person.name = "alex";
console.log(person)
Output is also:
Object { name="alex"}
Why?
Because some built-in functions are just defined to act this way. For example see ES5 15.2.1.1 for Object:
15.2.1.1 Object ( [ value ] )
When the Object function is called with no arguments or with one argument value, the following steps are taken:
If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments (15.2.2.1).
Return ToObject(value).
They test whether they have been called with new or not and if not act like they'd have been called with new.
Not all constructors work like this. For example Date will return a string when called without new.
You can implement this yourself:
function Foo() {
if(!(this instanceof Foo)) {
return new Foo();
}
// do other init stuff
}
Foo() and new Foo() will act the same way (it gets more tricky with variable arguments though).
Since your example is an Object type of built-in function, as it is answered above it is the same for this type, it does not work the same way for most of the other built-in functions such as Number(). You should be very careful when invoking them with the 'new' keyword or not. Because by default the 'new' keyword with a function constructor returns an object, not a primitive type directly. So you can not, for example, check strict equality on two variables that one of them is declared and assigned using new Number() , and the other is with Number()
An example would be:
var num1 = Number(26);
var num2 = new Number(26);
num1 == num2; // returns true
num1 === num2; // returns false
You may checkout the difference at the console log:
console.log(num1);
> 26
console.log(num2);
> NumberĀ {26}
> __proto__: Number
> [[PrimitiveValue]]: 26