没有办法在 JavaScript 中拥有基于类的对象吗?

2023-11-27

基于 javascript 原型的面向对象编程风格很有趣,但是在很多情况下您需要能够从类创建对象。

例如,在矢量绘图应用程序中,工作区在绘图开始时通常是空的:我无法从现有的“线”创建新的“线”。更一般地说,动态创建对象的每种情况都需要使用类。

我读过很多教程和《Javascript:好的部分》一书,但在我看来,没有办法定义尊重 1) 封装和 2) 高效成员方法声明的类(我的意思是:成员方法被定义一次,并在每个类实例之间共享)。

为了定义私有变量,使用了闭包:

function ClassA()
{
    var value = 1;
    this.getValue = function()
    {
        return value;
    }
}

这里的问题是“ClassA”的每个实例都会有自己的成员函数“getValue”的副本,这是低效的。

为了有效地定义成员函数,使用原型:

function ClassB()
{
    this.value = 1;
}

ClassB.prototype.getValue = function()
{
    return this.value;
}

这里的问题是成员变量“value”是公共的。

我认为这个问题不容易解决,因为需要在对象创建期间定义“私有”变量(以便对象可以访问其创建上下文,而不暴露这些值),而基于原型的成员函数定义必须在对象创建之后完成,这样原型才有意义(“this.prototype”不存在,我已经检查过)。

或者我错过了什么?


EDIT :

首先,感谢您的有趣回答。

我只是想在我最初的信息中添加一点精确性:

我真正想做的是拥有1)私有变量(封装很好,因为人们只能访问他们需要的东西)和2)有效的成员方法声明(避免复制)。

看起来简单的私有变量声明实际上只能通过 JavaScript 中的闭包来实现,这就是我专注于基于类的方法的本质原因。如果有一种方法可以使用基于原型的方法来实现简单的私有变量声明,那对我来说没问题,我不是基于类的方法的强烈支持者。

阅读完答案后,似乎简单的解决方案就是忘记私有变量,并使用特殊的编码约定来阻止其他程序员直接访问“私有”变量......

我同意,我的标题/第一句话对于我想在这里讨论的问题具有误导性。


嘘,过来!想听秘密吗?

经典继承是一种经过考验和尝试的方法。

It is通常用 JavaScript 实现它很有用。类是一个很好的概念,并且拥有用于在对象之后建模我们的世界的模板非常棒。

经典的传承只是一种模式。如果您的用例需要这种模式,那么在 JavaScript 中实现经典继承是完全可以的。

原型继承注重共享功能性那就是awesome (恐龙鼓槌很棒),但在某些情况下您想要共享数据方案而不是功能。这是原型继承根本无法解决的问题。

那么,你是在告诉我,类并不像大家一直告诉我的那样是邪恶的?

不,他们不是。 JS 社区不赞成的不是类的概念,而是将自己限制在代码重用的类中。就像该语言不强制执行强类型或静态类型一样,它也不强制对象结构上的方案。

事实上,在幕后巧妙地实现了该语言can turn您通常会反对类似于经典继承类的东西。

那么,类在 JavaScript 中是如何工作的

好吧,你真的只需要一个构造函数:

function getVehicle(engine){
    return { engine : engine };
}

var v = getVehicle("V6");
v.engine;//v6

我们现在有一个车辆类别。我们不需要使用特殊关键字显式定义 Vehicle 类。现在,有些人不喜欢用这种方式做事,而是习惯了更经典的方式。为此 JS 提供了(愚蠢的恕我直言)句法糖通过做:

function Vehicle(engine){
     this.engine = engine;
}
var v = new Vehicle("V6");
v.engine;//v6

这在很大程度上与上面的示例相同。

那么,我们还缺少什么呢?

继承和私有成员。

如果我告诉你 JavaScript 中的基本子类型非常简单怎么办?

JavaScript 的类型概念与我们在其他语言中习惯的不同。 JS 中某种类型的子类型是什么意思?

var a = {x:5};
var b = {x:3,y:3};

类型是b类型的子类型a?假设是的话根据(强)行为子类型(LSP):

  • 逆变子类型中的方法参数 - 完全保留在这种继承中。
  • 协方差子类型中的返回类型的数量 - 完全保留在这种继承中。
  • 子类型的方法不应引发新的异常,除非这些异常本身是超类型的方法引发的异常的子类型。 ——在这种传承中得到了完整的保存。

Also,

  • 前提条件无法在子类型中得到强化。
  • 后置条件在子类型中不能被削弱。
  • 不变量超类型的属性必须保留在子类型中。
  • 历史规律

所有这些又都由我们来保留。我们可以根据需要将它们保持得紧密或松散,我们不是必须这样做,但我们肯定可以。

事实上,只要我们在实现继承时遵守上述规则,我们就完全实现了强行为子类型,这是一种非常强大的子类型形式(参见注释*)。

>>>>>> 技术部分结束

简单地说,我们还可以看到结构子类型成立。

这将如何应用到我们的Car例子?

function getCar(typeOfCar){
    var v = getVehicle("CarEngine");
    v.typeOfCar = typeOfCar;
    return v;
}
v = getCar("Honda");
v.typeOfCar;//Honda;
v.engine;//CarEngine

不太难,不是吗?那么私人会员呢?

function getVehicle(engine){
    var secret = "Hello"
    return {
        engine : engine,
        getSecret : function() {
            return secret;
        }
    };
}

See, secret is a closure多变的。它是完全“私有”的,它的工作方式与Java等语言中的私有不同,但不可能从外部访问。

在函数中拥有私有变量怎么样?

啊!这是一个很好的问题。

如果我们想在原型上共享的函数中使用私有变量,我们需要首先了解 JS 闭包和函数是如何工作的。

在 JavaScript 中,函数是一流的。这意味着您可以传递函数。

function getPerson(name){
    var greeting = "Hello " + name;
    return {
        greet : function() {
            return greeting;
        }
    };
}

var a = getPerson("thomasc");
a.greet(); //Hello thomasc

到目前为止一切顺利,但是我们可以将绑定到 a around 的函数传递给其他对象!这可以让你做很宽松解耦这太棒了。

var b = a.greet;
b(); //Hello thomasc

等待!如何做b知道这个人的名字叫 thomasc?这就是闭包的魔力。非常棒吧?

您可能担心性能。让我告诉你我是如何学会停止担忧并开始喜欢优化 JIT 的。

实际上,拥有这样的函数副本并不是什么大问题。 javascript 中的函数都是关于功能!闭包是一个很棒的概念,一旦你掌握并掌握了它们,你就会发现它是非常值得的,而且对性能的影响实际上并没有那么有意义。 JS 每天都在变得越来越快,不用担心这类性能问题。

如果你觉得复杂的话,下面的也很合理。与其他开发人员的共同合同只是说“如果我的变量以_不要碰它,我们都是同意的成年人”。这看起来像:

function getPerson(name){
    var greeter = {
        greet : function() {
            return "Hello" +greeter._name;
        }
    };
    greeter._name = name;
    return greeter;
}

或者古典风格

function Person(name){
    this._name = name;
    this.greet = function(){
       return "Hello "+this._name;
    }
}

或者,如果您想在原型上缓存函数而不是实例化副本:

function Person(name){
    this._name = name;
}
Person.prototype.greet =  function(){
       return "Hello "+this._name;
}

所以,总结一下:

  • 您可以使用经典的继承模式,它们对于共享数据类型很有用

  • 您还应该使用原型继承,它同样有效,并且在您想要共享功能的情况下更有效。

  • 盗贼大师几乎做到了。私有化实际上并不像人们在 JavaScript 中想象的那样是一件大事,只要你的代码定义了一个清晰的接口,这根本就不应该有问题。我们都是专注于这里的成年人:)

*The clever reader might think: Huh? Weren't you tricking me there with the history rule? I mean, property access isn't encapsulated.

I say no, I was not. Even if you don't explicitly encapsulate the fields as private, you can simply define your contract in a way that does not access them. Often like TheifMaster suggested with _. Also, I think the history rule is not that big of a deal in a lot of such scenarios as long as we're not changing the way property access treats properties of the parent object. Again, it's up to us.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

没有办法在 JavaScript 中拥有基于类的对象吗? 的相关文章

  • 主干视图 DOM 元素已删除

    我一直在阅读有关 Backbone js 僵尸 或内存泄漏 问题的信息 基本上 当您不再需要该元素时 您必须从 DOM 中解除绑定并删除该元素 以确保所有事件也被删除 现在 我有一个包含几个容器的单页应用程序 div div div div
  • 关闭选项卡时要求确认[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 当我在某些浏览器上关闭页面时 我希望出现一个消息框 并询问我是否真的要关闭页面 有两个按钮 如果我单击No那么这个标签就不会被关闭 我怎样
  • 如何测试 javascript 闭包内的函数

    这似乎是不可能的 也可能是 但我正在尝试更多的 TDD 但我总是在闭包方面碰壁 假设我有以下内容 function createSomething init function privateMethod param return init
  • 了解设置 JQuery 变量

    了解设置 JQuery 变量 最近 我通过在 StackOverflow 上遇到的另一个问题寻找帮助 了解到如何设置 JQuery 变量 如下所示 您可以通过简单地调用变量来创建输入字段 并且锚变量似乎也定义了样式 var clicked
  • 从数据 URI 解码 QR 码

    我尝试从数据 uri 中解码二维码 var dataUri data image gif base64 R0lGODdh9gD2AIAAAAAAAP ywAAAAA9gD2AAAC decodeQrCode dataUri cb 我已经尝试
  • 使用 useReducers 调度函数发送多个操作?

    使用时是否可以通过调度函数发送多个动作useReducer挂钩反应 我尝试向它传递一组操作 但这会引发未处理的运行时异常 明确地说 通常会有一个初始状态对象和一个减速器 如下所示 const initialState message1 nu
  • 使用 jQuery/JS 打开时使
    标签的内容具有动画效果

    我只想要 HTML5 的内容details标记为 滑行 动画打开 而不是仅仅弹出打开 立即出现 这可以用 jQuery Javascript 实现吗 Fiddle http jsfiddle net 9h4Hq HTML
  • 如何防止 Iframe 在与浏览器交互后弄乱浏览器的历史记录?

    因此 就我而言 我使用 Iframe 将 Grafana 附加到我的页面 这为我提供了漂亮且易于使用的图表 可以注意到 每次在图表上进行放大或缩小 使用鼠标单击 交互后 Grafana 的 Iframe 都会在我的 Angular 页面上触
  • 如何监听 jQuery AJAX 请求?

    以下两种实现 ajaxRequest 1 2 的方法应该是等效的 话说回来 为什么验证回调已执行的单元测试 3 在 1 中成功而在 2 中失败 我应该如何重写测试 3 来监视 2 中的成功回调 如果我尝试stub jQuery ajax使用
  • 从未用 @flow 标记的导入文件中获取类型定义

    TL DR我怎么告诉flow从未声明的导入模块导入类型定义 flow 加长版 流接缝能够从不使用流语法的文件中派生类型 请参阅示例 示例文件 flow js if Math random lt 0 5 var y hello else va
  • Jquery/Javascript 上传和下载文件,无需后端

    是否可以在没有后端服务器的情况下在 JavaScript 函数中下载和上传文件 我需要导出和导入由 JavaScript 函数生成的 XML 我想创建按钮 保存 xml 来保存文件 但我不知道是否可行 另一方面 我希望将 XML 文件直接上
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • Python 3:将字符串转换为变量[重复]

    这个问题在这里已经有答案了 我正在从 txt 文件读取文本 并且需要使用我读取的数据之一作为类实例的变量 class Sports def init self players 0 location name self players pla
  • Meteor - 从客户端取消服务器方法

    我正在通过服务器方法执行数据库计数 用户可以选择他们希望如何执行计数 然后调用该方法 我的问题是 计数可能需要一些时间 并且用户可能会在方法运行时改变主意并请求不同的计数 有什么方法可以取消调用的方法并运行新的计数吗 我认为 this un
  • 通过 CDN 使用 Dojo 时如何加载自定义 AMD 模块?

    我正在使用 google 的 CDN 并尝试使用他们的加载程序加载我自己的 AMD 模块 我知道我做错了什么 但我被困住了 有任何想法吗
  • Angular 2+ 安全性;保护服务器上的延迟加载模块

    我有一个 Angular 2 应用程序 用户可以在其中输入个人数据 该数据在应用程序的另一部分进行分析 该部分仅适用于具有特定权限的人员 问题是我们不想让未经授权的人知道how我们正在分析这些数据 因此 如果他们能够在应用程序中查看模板 那
  • 条件在反应本机生产中失败,但在开发中有效

    我创建了一个反应本机应用程序 我需要通过它进行比较 如果属实 就会执行死刑 问题是 该条件适用于 React Native 开发模式 而不适用于 React Native 生产版本 我使用 firebase 作为数据库 也使用 redux
  • 如何更改此 jquery 插件的时区/时间戳?

    我正在使用这个名为 timeago 的插件 在这里找到 timeago yarp com 它工作得很好 只是它在似乎不同的时区运行 我住在美国东部 费城时区 当我将准确的 EST 时间放入 timeago 插件时 比如 2011 05 28
  • JQuery 图像上传不适用于未来的活动

    我希望我的用户可以通过帖子上传图像 因此 每个回复表单都有一个上传表单 用户可以通过单击上传按钮上传图像 然后单击提交来提交帖子 现在我的上传表单可以上传第一个回复的图像 但第二个回复的上传不起作用 我的提交过程 Ajax 在 php 提交
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成

随机推荐