Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to create JavaScript objects that behave like C++ RValues?

Tags:

I'm a C++ programmer who recently landed on the JavaScript world; now I'm trying to apply some of the dessign patterns of C++ to JavaScript for the sake of my understanding and mental health.

AFAIK, the following codes are kind of equivalent in C++ and Javascript:

C++

// Class definition
template <typename T> class foo
{
public:
    // Constructor
    foo(T value) { this->value = value; }
    // Public function
    T twice() { return this->value + this->value; }

private:
    // Private function
    void bar() { }
    // Private member
    T value;
};

JavaScript

// "Class" definition and constructor
function foo(value)
{
    // "Private" member
    this.value = value;

    // "Private" function
    this.bar = function() { };
}

// Public function
foo.prototype.twice = function() { return this.value + this.value; };

The usage of both classes are similar too:

C++ live demo

foo<int> f1(1);
foo<std::string> f2("1");

std::cout << f1.twice() << '\n'; // output: 2
std::cout << f2.twice() << '\n'; // output: 11

JavaScript live demo

var f1 = new foo(1);
var f2 = new foo('1');

print(f1.twice()); // output: 2
print(f2.twice()); // output: 11

But there's a thing that cannot be done with a JavaScript class and is possible to do with a C++ class: the use of a temporary RValue to do a task:

C++

std::cout << foo<float>(3.14f).twice() << '\n'; // output: 6.28

JavaScript

print(foo(3.14).twice()); // Uncaught TypeError: undefined is not a function

I think that the error on the JavaScript version is due to the fact that foo is a function and it returns nothing (undefined), so at first I was thinking of change the constructor with the code below:

JavaScript

// "Class" definition and constructor
function foo(value)
{
    // "Private" member
    this.value = value;

    // "Private" function
    this.bar = function() { };

    return this; // <----- new code!
}

But this doesn't work at all; the object returned by the return this; instruction isnt of type foo (foo(3.14) instanceof foo is false).

While debugging in Chrome 35.0.1916.114 the type of this in the instruction return this; is foo but the type changes to window in this situation:

var x = foo(3.14); // x is typeof window

Once made the introduction, here comes the questions:

  • Why the type of this is foo inside the constructor and window when captured outside?
    • Is because the new operator isn't used?
  • Is there a way to create JavaScript objects that behave like C++ RValues?
like image 234
PaperBirdMaster Avatar asked Jun 04 '14 10:06

PaperBirdMaster


1 Answers

In JavaScript, when you use the new keyword with a function, the function behaves differently to when called without the new keyword. With it, the function acts like a class, and a new object is instantiated from it, just like in traditional OOP languages. Additionally, the this context is set to itself and the return value of the function is disregarded.

By contrast, without the new keyword, the this context is set to the global scope, which for browsers is the window object, and the return value can be captured by whatever called it.

It is possible to create something like your C++ example, whereby you don't need the new keyword, and it still returns a new object.

Fiddle

function foo(value) {
    function foo(value) {
        this.value = value;

        this.bar = function () {};
        this.twice = function() { return this.value + this.value; }
    }

    return new foo(value);
}

console.log( foo(3.14).twice() ); // 6.28

Explanation:

the outer function foo behaves like a normal function, and is intended to be called without the new keyword. Inside lives the inner foo, which is intended to be like a class - called with the new keyword. The outer foo instantiates a new instance of the inner foo and returns it. Thus it can be used like the C++ example. It is not necessary to declare the class-like function within the outer function, it just depends if you want to encapsulate it within the outer.


Visibility

The two examples in the question are not exactly equivalent because the JavaScript example uses all public properties and methods, whereas the C++ example has bar and value as private.

Below is a version that is closer to the C++ version:

Fiddle

function foo(value) {
    function foo(value) {
        var value = value;

        function bar(){}
        this.twice = function() { return value + value; }
    }

    return new foo(value);
}

console.log( foo(3.14).twice() ); // 6.28 
console.log( foo(3.14).value ); // undefined because it's private
console.log( foo(3.14).bar() ); // undefined because it's private

As you can see from the test cases, value and bar are not publicly accessible/visible. This is achieved by not using the this prefix and declaring value using the var keyword (which defines a local variable). The function is declared as a declaration instead of an expression. In JavaScript, there is no formal way of declaring/differentiating public and private properties or methods like in OOP languages.

You may have noticed the above examples don't use prototype to declare any methods. The reason for this is that the prototype way would always declare methods with public visibility, and prototype methods cannot see any variables from inside the "class" or function. The code comments in the question suggest that using this.bar inside the function body makes it private, but that isn't the case.

like image 149
MrCode Avatar answered Sep 19 '22 00:09

MrCode