【问题标题】:What are the benefits / drawbacks of functional object creation in JavaScript?在 JavaScript 中创建函数式对象有哪些优点/缺点?
【发布时间】:2014-04-18 17:07:55
【问题描述】:

我刚刚看了 Douglas Crockford 谈论原型继承如何“也不是一个好主意”

YouTube 35m55s

我并不真正关心他对结合 JavaScript 的原型继承的看法,因为它是语言的重要组成部分,它将永远存在。

但我想知道我通过使用他在链接中显示的功能对象创建获得了什么好处:

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

看完视频后,我重新阅读了他在“JavaScript The Good Parts”一书中关于功能对象创建的部分第 5 章:继承。

但我真的看不出 不同.. 我可以使用构造函数模式获得私有成员:

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

我可以发现构造函数模式和函数模式之间的唯一区别是省略了new 关键字。通过避免使用new 关键字,我们可以避免忘记new 关键字的错误。

这样写:

var panda = createBear();

而不是这个:

var panda = new Bear();

让我觉得这主要取决于个人喜好。我可以看到避免使用new 关键字是多么有用,我可能会采用函数模式。 但这是我能理解你为什么要这样做的唯一原因。我能否获得更多信息,为什么一个会比另一个更好或更差?

【问题讨论】:

标签: javascript functional-programming scope private-members


【解决方案1】:

好的,所以我将尝试在这里回答我自己的问题,其中包含我收到的信息以及我在提出问题后在互联网上收集的其他内容。

TL;DR:

它们都很有用,并且可以实现几乎相同的目标。构造函数可以访问他们的原型,这非常有用,因为这意味着它们在使用构造函数创建的所有实例中都具有“全局”值。它既有用又有潜在危险。有用,因为 Constructor 的所有实例都可以访问相同的原型属性,从而避免重复。很危险,因为您可以覆盖构造函数属性给实例一个同名的属性 - 使得访问原型值变得更加困难。

在调用构造函数时忘记new 关键字存在一定的危险,但可以通过在构造函数中添加"use strict"; 轻松解决,如果忘记new 关键字则会抛出错误。

如果您想避免原型及其功能/危险,您可以使用工厂 功能。 函数式方法真正有用的特性是你可以返回任何你喜欢的东西。而不是总是构造一个预定义对象的“子”

我从这一切中学到的是,当你可以同时使用两者时,选择一个而不是另一个是愚蠢的。他们都有自己的长处和短处,人们需要记住,Douglas Crockford 只是一个人,而不是 JavaScript 之神。 (那就是 Brandon Eich,哈哈!)


@Domenic 在What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor? 上接受的答案 让我了解了这两种对象创建方法的异同。

构造函数

使用new 关键字在新对象和派生它的构造函数对象之间创建一个链接。构造函数是新对象的原型,新对象是原型对象的一个​​实例。

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

链接到原型对象意味着我们的新对象可以访问原型属性,而不必将它们存储在对象本身中。这不仅比在每个子对象上创建属性更节省内存,而且还带来了原型设计功能的额外好处。

向对象原型添加属性或方法很简单:

Constructor.prototype.color = 'yellow';

现在,使用 Constructor 对象创建的每个对象都可以访问.color 属性,而无需将其存储在自身内部。

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

由于 JavaScript 中的对象是动态的,这意味着您可以“追溯”向原型添加新属性,并且在更改之前创建的对象仍将“继承”新属性。

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

Crockford 可能反对构造函数模式的一个原因是避免覆盖原型属性或意外覆盖子对象内原型的命名空间。

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

据我所知,在创建后添加属性也会使代码在 V8 引擎中运行得更慢,因为对象不再共享相同的 “隐藏类” 但我没有足够的知识来了解进入那个。 Breaking the JavaScript Speed Limit with V8

原型仍然可以访问。通过现在已弃用的 .__proto__. 或新的Object.getPrototypeOf() 方法。

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

Crockford 反对使用构造函数的另一个原因是您可能忘记输入new。如果您忘记在构造函数前面写new,它将运行构造函数而不是创建新对象。

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

这很容易通过在你的函数中添加严格的类型来解决,如果你忘记了new 关键字,将会抛出一个错误。

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

工厂函数

我发现这很简单。如果您不想处理 new 关键字以及 Constructor 函数的一些“危险”,您可以使用这种方法创建不使用其原型的对象。

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

当您想要对数据做一些事情而不只是创建一个对象时,这会非常方便。

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};

这样做你会失去原型的力量/危险。因为对象没有绑定到一个。

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

这意味着你不能使用它。但这也意味着你不能把事情搞砸。

总结。

参见 TL;DR

我在搜索和写这篇文章时学到了很多东西,希望其他人也能学到一两件事。

参考资料:

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

【讨论】:

    猜你喜欢
    • 2016-11-23
    • 2021-01-02
    • 2013-10-11
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    • 2023-04-09
    相关资源
    最近更新 更多