没有办法在 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 中拥有基于类的对象吗? 的相关文章

  • Javascript:将 JSON 字符串转换为 ES6 映射或其他形式以保留键的顺序

    ES6 或后续版本 Javascript 或 TypeScript 中是否有原生 内置 方法将 JSON 字符串转换为 ES6 映射 或者可以选择要实现的自制解析器 目标是保留 JSON 字符串编码对象的键顺序 Note 我故意不使用 解析
  • 如何在chartjs中绘制多个时间序列,其中每个时间序列都有不同的时间

    例如 我有两个时间序列 s1 2017 01 06 18 39 30 100 2017 01 07 18 39 28 101 and s2 2017 01 07 18 00 00 90 2017 01 08 18 00 00 105 我想在
  • CSS - 制作“步进”文本的好方法?

    有没有一种好的方法可以实现以下目标 而无需任何额外的标记 不过使用 JavaScript 就很好了 任何想法 Thanks Edit 我的标记将是这样的 div style width 400px p Text text text Text
  • HTML:您可以隐藏/忽略浏览器查找中的文本元素 (CTRL+F)

    我有一个具有相当复杂的 UI 的 Web 应用程序 并且屏幕的一部分保留用于内容 如果可能的话 我想这样做 以便当用户使用浏览器的内置文本搜索 CTRL F 时 UI 中的任何文本都将被忽略 并且仅搜索实际内容 这可行吗 CSS 和 Jav
  • webpack 加载器并包含

    我是 webpack 的新手 我正在尝试了解加载器及其属性 例如测试 加载器 包含等 这是我在 google 中找到的 webpack config js 的示例片段 module loaders test js loader babel
  • 如何在给定目标索引数组的情况下对数组进行就地排序?

    你如何对给定的数组进行排序arr in place给定目标索引数组ind 例如 var arr A B C D E F var ind 4 0 5 2 1 3 rearrange arr ind console log arr gt B E
  • 如何处理 React JSX 中的长类名?

    假设我在 React JSX 中渲染这个组件 render return h1 Some text h1 这些类触发我的 JS linter 的行太长 而且很难阅读 怎样才能分开长className将 React 组件中的属性分成多行而不破
  • JavaScript 中的凯撒密码

    我正在尝试编写一个程序来解决javascript中的以下问题 写在本段下面 我不知道为什么我的代码不起作用 有人可以帮助我吗 我是 JavaScript 新手 这是一个免费的代码训练营问题 现代常见的用法是 ROT13 密码 其中字母的值移
  • 如何用方向键移动div

    我想使用 jQuery 用箭头键移动 div 所以右 左 下 上 找到了我想要完成的演示here http atomicrobotdesign com blog htmlcss move objects around the canvas
  • Javascript 3d 绘图实用程序? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有谁知道有什么好的 javascript 3d 绘图实用程序吗 我知道每个网站都推荐过画布 3d 图
  • 如何禁用向左滚动?

    I got a div 元素 parent 包含多个子元素 item 我想启用滚动父元素一个方向 left OR正确的 否则什么都不会发生 看我的代码 parent scroll function gt gt gt scroll event
  • 根据复选框显示/隐藏输入字段[重复]

    这个问题在这里已经有答案了 如果单击该复选框 它将显示一个输入字段 到目前为止它正在工作 但如果未选中该复选框 它应该隐藏它 我该怎么做 div class checkbox div
  • 查找 JavaScript 中函数参数的数量[重复]

    这个问题在这里已经有答案了 可能的重复 获取函数的元数 https stackoverflow com questions 4848149 get a functions arity 假设我有 function a x function b
  • 如何从 WinRT StreamSocket 读取所有可用数据并清空 inputStream?

    我想在向套接字写入新数据之前读取当前正在等待套接字的所有数据 WinRT中的读取方法都是异步的 所以我不能简单地while直到套接字为空 由于我确实想丢弃套接字上的数据 因此我不想使用读取器 而是直接从套接字读取数据IInputStream
  • Javascript 警报/消息框中的欧元符号或其他实体

    有谁知道我如何在 javascript 警报窗口中显示欧元或其他 html 实体 alert u20AC HTML 实体字符查找 http leftlogic com lounge articles entity lookup
  • AngularJS 中的嵌套模块

    我有 2 个不同的 AngularJs 模块 一个 widgetContainer 和一个 widget 小部件可以显示为独立的应用程序 也可以包含在小部件容器中 一个 widgetContainer 包含 0 N 个 widget 如果我
  • 出于安全目的,您是否有理由不执行自己的算法来打乱 ID?

    我计划实现我自己的非常简单的 哈希 公式 为具有多个用户的应用程序添加一层安全性 我目前的计划如下 用户创建一个帐户 此时后端会生成一个 ID ID 通过公式运行 假设 ID 57 8926 36 7 或同样随机的东西 然后 我将新的用户
  • jQuery 倒计时插件 - 只显示非零周期

    我正在使用 jQuery 倒计时插件编写倒计时 我只希望它显示活动 非零 周期 例如代替 剩余时间 0 天 0 小时 13 分 20 秒 它应该只显示 13 分 20 秒 我的代码是 countdown countdown expiryUr
  • 仅使用 javascript 获取网站的正文元素

    我想检索以下网站的正文内容http sports espn go com nhl bottomline scores nhl s left1 http sports espn go com nhl bottomline scores nhl
  • 在哪里放置资源特定逻辑

    您能帮我考虑在 AngularJS 中将资源 服务 特定的业务逻辑放置在哪里吗 我觉得在我的资源上创建一些类似模型的抽象应该很棒 但我不确定如何做 API调用 gt GET customers 1 lt first name John la

随机推荐