在 JavaScript 中创建函数对象的优点/缺点是什么?

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

本文关键字:缺点 是什么 JavaScript 创建 函数 对象      更新时间:2023-09-26

>我刚刚看了道格拉斯·克罗克福德(Douglas Crockford(谈论原型遗传"也不是一个好主意">

优酷 35分55秒

我真的不在乎他对原型继承与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 关键字是多么有用,我可能会采用它的功能模式。但这是我能看到你为什么要这样做的唯一原因。我能得到更多的信息,为什么一个会比另一个更好或更差吗?

好的,

所以我将尝试在这里用我收到的信息和我在提出问题后在互联网上收集的其他东西来回答我自己的问题。

博士:

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

调用构造函数时有一些忘记关键字的危险new但是通过在构造函数中添加"use strict";很容易补救,如果您忘记了new关键字,则会抛出错误。

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

我从这一切中学到的是,当你可以同时使用两者时,选择一个而不是另一个是愚蠢的。他们都有自己的优点和缺点,人们需要记住,Douglas Crockford只是一个人,而不是JavaScript之神。(那将是布兰登·艾希,哈哈!


@Domenic接受的答案 在 JavaScript 中,构造函数和作为构造函数调用的函数返回对象之间有什么区别?让我对两种对象创建方法之间的异同有一些见解。

构造 函数

使用 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 引擎中运行得更慢,因为这些对象不再共享相同的"隐藏类"但我没有足够的知识来进入这一点。使用 V8 打破 JavaScript 速度限制

仍然可以访问原型。通过现已弃用的.__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

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

引用:

在 JavaScript 中,构造函数和作为构造函数调用的函数返回对象之间有什么区别?

构造函数与工厂函数

是时候开始使用 JavaScript 严格模式