在 JavaScript 中显示 OOP 和过程编程之间区别的一个很好的例子是什么?

What is a good example that shows the difference between OOP and procedural programming in JavaScript?

我无法理解 JavaScript 中的 OOP。

很多年前,我曾经涉足过BASIC编程,在学校学过一点COBOL和FORTRAN,所以我对过程式编程有些熟悉,但我从来没有把这些学得非常熟练或复杂。我也用 JS 做了一些骇人听闻的事情,但我想它本质上主要是过程性的,而不是 OOP。

最近,我决定训练自己成为一名 Web 开发人员并正确学习 JavaScript,基于好评如潮,我决定将 Head First JavaScript Programming 作为我的教科书。

我遇到的问题是我无法弄清楚使用对象与使用函数和变量有何不同或更好。我看了很多教程和视频,但它们都说的和这本书完全一样。 "Objects are useful because they are analogous to everyday items in real life such as a car. A car has a model year, a make, a weight, a color, etc... A car can also do things such as start, stop, go, etc. They are functions inside the object and called 'methods'." 然后是 "this is how to create an object:"

var myCar = {
    make: "Chevy";
    year: 2004;
    kilograms: 2000;
    color: "blue";
    go: function() {
        alert("go!");
    }
};

太棒了。现在我知道对象在某种程度上类似于现实生活中的事物,并且我知道如何使用属性和方法来制作对象。我明白了。

我可以通过调用这些 属性 值来访问它们,如下所示:

a = myCar.make;
b = myCar.year;
c = myCar.color;

我也可以调用函数(或方法):

myCar.go();

太棒了。但我仍然无法弄清楚为什么我要这样做。为什么这比下面的更好?

myCarMake = "Chevy";
myCarYear = 2004;
myCarKgs = 2000;
myCarColor = "blue";

function go() {
    alert("go!");
};

除了代码的组织方式外,我不明白它的程序性如何。毕竟,语句是按顺序执行的。而且我不明白做所有这些有什么好处。

我一直在想,为了理解这一点,我真正需要的是看两个做同样事情的程序,一个是用普通变量和函数按程序编码的,另一个是用 OO 编程的,以便看到差异以及这些对象如何以有利的方式相互交互。

我发现我找到的所有教科书和网站都从未解释过相似的事物有何不同或为什么一个比另一个更好,并且很少有关于如何将它们很好地结合在一起的例子。大多数书籍和教程只告诉您可以做什么,但没有告诉您为什么要这样做或选择一种方式而不是另一种方式。 (与这个问题无关,但我想知道的另一件事是,我知道我可以将一个函数分配给一个变量,但我为什么要这样做?)

为了弄清楚我在寻找什么,我的问题是,有人可以给我展示一个以两种方式编程的程序(两者都做同样的事情并且对于初学者来说足够简单但又足够复杂以说明为什么 OOP可能需要或有利)来突出差异并解释为什么它更好?

老实说,我总是发现适当范围的变量的想法是 javascript 的 OOP 方法的一个很好的论据。

以你为例:

var myCar = {
make: "Chevy",
year: 2004,

};

var make = "Ford";
console.log(make);
console.log(myCar.make);

生成 "Ford",然后生成 "Chevy"。范围界定在 JS 中可能是一个问题的原因是可以引入的库数量之多。是的,小程序往往更容易使用过程编写。但是,当您引入十几个其他库时,您不知道它们使用哪种方法来声明变量——这可能会导致模糊错误。

是的,您可以只在函数定义中声明变量,然后它们的范围将仅限于该函数(下面的示例),但是它们不能轻易被重用!

function go(){
var make = "chevy";
console.log(make);
};
go();
console.log(make) ///throws undefined 'error'

OOP 提供了一种范围安全且易于重用的方法(这对外部库非常有用)。

他们说 OOP 是关于继承、多态和封装。

我们将封装放在一边,因为它与 OOP 无关,而是与模块有关。 (终于,modules是语言的一部分了!)

Inheritance 是一个强大的概念,可以重用逻辑和数据。扩展您的汽车示例,我们可以执行以下操作。

var AbstractCar = {
    go: function () {
        alert('go ' + this.make+ '!');
    }
};
var MyCar = Object.create(AbstractCar, {
    make: { value: 'Chevy', writable: true }
});

var YourCar = Object.create(AbstractCar, {
    make: { value: 'Mustang', writable: true }
});

MyCar.go(); // go Chevy!
YourCar.go() // go Mustang!

另一方面,多态性允许您将不同种类的对象视为一个对象,只要它们符合接口即可。或者换句话说,只要他们能做我们想让他们做的。

例如,我们需要 out 对象的字符串表示。任何具有 toString 方法的东西都可以与字符串连接。

var myCar = {
    make: "Chevy",
    year: 2004,
    toString: function () {
        return this.make + ' ' + this.year;
    }
};

var statement = 'I want to sell ' + myCar;

console.log(statement); // I want to sell Chevy 2004

就是这样。

OOP 不是什么值得掌握的高级技术。虽然它可以很方便。 :)

在实践中,如果您有一个小脚本或应用程序,您将看不出差异。 但是一旦您转向更大的应用程序和更大的代码库,您就会发现 OOP 比 PP 工作得更好。

以下是 OOP 方法相对于 PP 的一些高级优势:

1) 模块化和可维护性

代码模块化,相关数据和方法封装成对象

这样可以更轻松地控制代码。 在几行代码上看到这一点很复杂,但随着代码的增长,过程代码通常会变得一团糟。

使用 OOP,您的代码会自然地分成更小的部分(对象),即使代码库在增长,保持代码的顺序仍然很容易。 模块化导致更好的可维护性——更容易修改和维护代码。

2) 灵活性

用一些不同的功能替换部分代码要容易得多。

例如,您可以拥有将日志写入文件的 Logger 对象。但是后来您决定改为将日志写入数据库。

使用 PP 方法,您需要遍历整个代码库以搜索 log_to_file(message, file_name) 之类的内容并替换为 log_to_db(message, db, table_name) 之类的内容。

使用 OOP,您只需在系统中创建新的 DbLogger 和 "plug it" 而不是以前的文件记录器,因此记录数据的代码看起来仍然相同,例如 logger->log(message).更好的是,您可以决定记录器的类型 运行-time,例如,从配置文件中读取设置并创建文件记录器或数据库记录器。

3) 可测试性

使用 OOP 可以更容易地获取我们的代码(对象)部分并单独测试它。如果它依赖于其他对象,这些可以替换为假实现(测试模拟),因此您可以真正单独测试这段代码。

只是为了证明差异,让我们想象一下,您现在拥有三辆汽车,而不是一辆汽车(go 方法确实应该对变量做一些事情,否则它没有多大意义):

var myCarMake = "Chevy";
var myCarYear = 2004;
var myCarKgs = 2000;
var myCarColor = "blue";

var yourCarMake = "Ford";
var yourCarYear = 2001;
var yourCarKgs = 1990;
var yourCarColor = "white";

var hisCarMake = "Ferrari";
var hisCarYear = 2011;
var hisCarKgs = 2990;
var hisCarColor = "red";

function go(make, year, kgs, color) {
    alert(make + " " + kgs + " " + year + " " color);
};

go(myCarMake, myCarYear, myCarKgs, myCarColor);
go(yourCarMake, yourCarYear, yourCarKgs, myCarColor);
go(hisCarMake, hisCarKgs, hisCarKgs, hisCarColor);

注意这段代码的一些特性:

  • busness-logic代码(定义汽车属性和go方法)和client代码(我们调用go的部分)混合在一起,我们不能将它们分开,因为客户端代码引用我们创建的全局变量(如myCarMake
  • 很多重复(比如CarMake写了6次)看起来很乱
  • 容易出错(前两次调用都有错误)
  • 难以维护 - 如果我们为汽车添加新参数,我们将不得不遍历所有代码库

现在是带对象的版本(为了简化代码,我把它作为一个简单的对象和单独的构造函数,其他定义对象的方法见this):

var CarPrototype = { // empty object-prototype
    make: "",
    year: 0,
    kilograms: 0,
    color: "",
    go: function() {
        alert(this.make + " " + this.kgs + " " + this.year + " " this.color);
    }
};

// function that constructs the new object
function createCar(make, kgs, year, color) {
    var car = Object.create(CarPrototype);
    car.make = make;
    car.kgs = kgs;
    car.year = year;
    car.color = color;
    return car;
}

var myCar = createCar("Chevy", 2004, 2000, "blue");
var yourCar = createCar("Ford", 2001, 1990, "white");
var hisCar = createCar("Ferrari", 2011, 2990, "red");

myCar.go();
yourCar.go();
hisCar.go();

以及这段代码的一些属性:

  • 业务逻辑明确定义并与客户端代码分离,客户端代码不知道car对象的内部结构
  • 减少重复,总体上代码看起来更清晰
  • 一旦定义了对象,就很复杂了(你只是做myCar->go(),没有参数传递,没有机会混合它们或以错误的顺序传递
  • 更容易修改(如果我们为汽车添加一个新的属性,客户端代码中的myCar.go()调用不需要更改)

当然,我没有提到使用 OOP 而不是 PP 的所有原因,但我从实践中知道这一点 - 尝试花一些时间学习 OOP 原则并开始使用它,你会看到巨大的改进代码结构。一开始你会犯错误,做错事,但结果无论如何都会比程序代码好。

此处的一般方法比特定的语言结构更重要,您可以在 C(不是 OOP 语言)和 javascript(具有特定的 OOP 支持)中使用相同的原则,以及在其他语言中。

我不知道并排比较 PP 和 OOP 的资源,但我认为您并不真正需要它。看看 OOP 应用程序的例子,想象一下如果以过程式风格编写这些应用程序会是什么样子。

我最喜欢的关于 OOP 的书是 Design Patterns,它展示了对象之间的交互是多么优雅和强大。 可能,您还需要找到 something where basic OOP concepts are explained (already mentioned encapsulation, polymorphism, inheritance) and principles (most notably SOLID).