What are the benefits / drawbacks of functional object creation in JavaScript?

I just watched Douglas Crockford talk about how prototypical inheritance is "not a good idea either"

YouTube 35m55s

I don't really care about his views on Prototypical inheritance in conjunction with JavaScript since it is such an essential part of the language that it will always be there.

But I would like to know what benefits I am reaping by using the functional object creation that he is showing in the link:

// Class Free Object Oriented Programming
function constructior(init) {
    var that = other_constructor(init),
        member,
        method = function () {
            // init, member, method
        };
    that.method = method;
    return that;
}

After the video I re-read the part about Functional Object Creation in his book "JavaScript The Good Parts" Chapter 5: Inheritance.

But I can't really see the big difference.. I can get private members just fine with the constructor pattern:

function Constructor (value) {
    var private = value;
    this.getPrivate = function () {
        return private;
    }
}
var OBJ1 = new Constructor(5);
var OBJ2 = new Constructor('bacon');

console.log( OBJ1.getPrivate() ); // 5
console.log( OBJ2.getPrivate() ); // bacon

The only difference I can spot between a Constructor Pattern and the Functional Pattern is the omission of the new keyword. By avoiding the use of the new keyword we can avoid the error of forgetting the new keyword.

Writing this:

var panda = createBear();

Instead of this:

var panda = new Bear();

Makes me think it is mainly down to personal preference. I can see how avoiding the new keyword can be useful, and I might adopt it the functional pattern. But this is the only reason I can see as to why you would do it. Can I please get some more information why one would be better or worse than the other?


Alright so I am gonna try and answer my own question here with the information that I have received and additional stuff I have gathered on the internet after asking the question.

TL;DR:

They are both useful and can achieve mostly the same things. Constructors have access their prototype which can be very useful because it means they have "global" values across all the children created with the Constructor. It is both useful and potentially dangerous because you can override the constructor property OR give the child a property of the same name - making it harder to access the prototypes value.

There is some danger of forgetting new keyword when calling the Constructor but it is easily remedied by adding "use strict"; inside the Constructor function which will then throw an error if you forget the new keyword.

If you want to avoid the Prototype and its features/dangers you can use a Factory Function. The really useful Feature of the Functional approach is that you can return anything you like. Rather than always Constructing a "child" of a predefined object.

What I have learned from all this is that it is stupid to pick one over the other when you could be using both. They both have their strengths and weaknesses and people need to remember that Douglas Crockford is just a human being, not the JavaScript God. (That would be Brandon Eich, lol jk!)


The accepted answer by @Domenic on What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor? Gave me some insights on the differences and similarities between the two methods of object creation.

Constructor

Using the new keyword creates a link between the new object and the Constructor Object it is derived from. The Constructor is the Prototype of the new Object and the new Object is an instance of the Prototype Object.

var Constructor = function () {
    this.x = 0;
    this.y = 0;
};
var A = new Constructor();
console.log(A instanceof Constructor ); // true

Being linked to the prototype object means that our new object has access to the prototypes properties without having to store them inside the object itself. This is both more memory efficient than creating the properties on each child object and it comes with the added bonus of the power of Prototyping.

Adding a property or method to the object prototype is simple:

Constructor.prototype.color = 'yellow';

Now every object created with the Constructor object has access to the .color property without storing it inside themselves.

var A = new Constructor();
console.log(A.color); // yellow
console.log(A.hasOwnProperty('color')); // false

Since the objects in JavaScript are dynamic it means that you can "retroactively" add new properties to the prototype and objects created before the change will still "inherit" the new properties.

var A = new Constructor();
Constructor.prototype.food = 'bacon';
console.log(A.food); // bacon;

One reason that Crockford might advocate against the Constructor patters is to avoid overriding the prototype property OR overriding the namespace of the prototype inside the child object accidentally.

Constructor.prototype.number = 5;
A.calculate = function () {
    return A.number * 5;
}
console.log(A.calculate()); // 25

Constructor.prototype.number = 'fishsticks';
console.log(A.calculate()); // NaN

From what I can understand adding properties after creation will also make the code run slower inside the V8 engine because the objects no longer share the same "hidden classes" But I am not knowledgeable enough to get into that. Breaking the JavaScript Speed Limit with V8

The prototype can still be accessed. Either via the now deprecated .__proto__. or the new Object.getPrototypeOf() method.

console.log(Object.getPrototypeOf(A.color)); // yellow

The other reason why Crockford is advocating against the use of a Constructor Function is that you might forget to type new . If you forget to write new in front of the Constructor it will run the Constructor Function instead of creating a new object.

var A = Constructor();
console.log(A); // undefined

This is easily fixed by adding strict typing to your function which will throw an error if you forget the new keyword.

var Constructor = function () {
    "use strict";
    this.x = 0;
    this.y = 0;
}
var A = Constructor();

console.log(A);
// Uncaught TypeError: Cannot set property 'x' of undefined

Factory Function

I found this pretty straight forward. If you don't want to have deal with the new keyword, and some of the "dangers" of the Constructor function, you can create objects that don't use their prototype with this approach.

function factory () {
    var obj = {
        x: 0,
        y: 0
    }
    return obj;
}
var A = factory(); // {x: 0, y: 0}

This can be very handy for when you want to do something with the data other than just creating an Object.

function factory () {
    if ( new Date().getHours() < 8 ) { 
        return "can't create object. Need Coffe!" 
    };
    var obj = {
        x: 0,
        y: 0
    }
    return obj;
}
var A = factory(); // Before 8 am: "can't create object. Need Coffe!"
var A = factory(); // After 8 am: {x: 0, y: 0};

Doing this you lose the power / danger of the prototype. Because the object is not bound to one.

factory.prototype.foo = "bar";
A = factory();
console.log(A.foo); // undefined

This means you can't use it. But it also means you can't mess it up.

In conclusion.

See TL;DR

I learned a lot searching and writing this, hopefully someone else will learn a thing or two too.

References:

What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor?

Constructor function vs Factory functions

It's time to start using JavaScript strict mode

链接地址: http://www.djcxy.com/p/40848.html

上一篇: 带有函数返回自我的Javascript'new'

下一篇: 在JavaScript中创建函数对象有什么好处/缺点?