草庐IT

javascript - 做一个对外只读的属性,但是我的方法还是可以设置的

coder 2024-05-12 原文

在 JavaScript (ES5+) 中,我试图实现以下场景:

  1. 一个对象(其中将有许多单独的实例)每个都具有只读属性 .size 可以通过直接属性读取从外部读取,但不能从外部设置.
  2. .size 属性必须通过原型(prototype)上的某些方法进行维护/更新(并且应该保留在原型(prototype)上)。
  3. 我的 API 已经由规范定义,所以我不能修改它(我正在为一个已经定义的 ES6 对象开发一个 polyfill)。
  4. 我主要是想防止人们不小心开枪打自己的脚,而且真的不需要防弹只读(尽管越防弹越好),所以我愿意只要直接设置 obj.size = 3; 是不允许的,就可以妥协一些侧门访问属性。

我知道我可以使用在构造函数中声明的私有(private)变量并设置一个 getter 来读取它,但是我必须将需要维护该变量的方法从原型(prototype)中移出并在构造函数中声明它们也(因此他们可以访问包含变量的闭包)。对于这种特殊情况,我不想将我的方法从原型(prototype)中移除,所以我正在寻找其他可能的选择。

还有什么其他的想法(即使有一些妥协)?

最佳答案

好的,所以对于一个解决方案,您需要两个部分:

  • 一个size不可分配的属性,即 writable:true或者没有 setter属性
  • 一种改变size的值的方法反射(reflect),这不是.size = …它是公共(public)的,以便原型(prototype)方法可以调用它。

@plalx 已经提出了第二个“半私有(private)”的明显方式 _sizesize 的 setter/getter 反射(reflect)的属性.这可能是最简单、最直接的解决方案:

// declare
Object.defineProperty(MyObj.prototype, "size", {
    get: function() { return this._size; }
});
// assign
instance._size = …;

另一种方法是制作 size属性不可写,但可配置,因此您必须使用 Object.defineProperty 的“长路” (尽管恕我直言,对于辅助函数来说甚至太短了)在其中设置一个值:

function MyObj() { // Constructor
    // declare
    Object.defineProperty(this, "size", {
        writable: false, enumerable: true, configurable: true
    });
}
// assign
Object.defineProperty(instance, "size", {value:…});

这两种方法绝对足以防止“搬起石头砸自己的脚”size = …作业。对于更复杂的方法,我们可能会构建一个公共(public)的、特定于实例的(闭包)setter 方法,该方法只能从原型(prototype)模块范围方法中调用。

(function() { // module IEFE
    // with privileged access to this helper function:
    var settable = false;
    function setSize(o, v) {
        settable = true;
        o.size = v;
        settable = false;
    }

    function MyObj() { // Constructor
        // declare
        var size;
        Object.defineProperty(this, "size", {
            enumerable: true,
            get: function() { return size; },
            set: function(v) {
                if (!settable) throw new Error("You're not allowed.");
                size = v;
            }
        });
        …
    }

    // assign
    setSize(instance, …);

    …
}());

只要不关闭对 settable 的访问,这确实是故障安全的被泄露。还有一种类似的、流行的、更短的方法是使用对象的身份作为访问 token ,大致如下:

// module IEFE with privileged access to this token:
var token = {};

// in the declaration (similar to the setter above)
this._setSize = function(key, v) {
    if (key !== token) throw new Error("You're not allowed.");
        size = v;
};

// assign
instance._setSize(token, …);

但是,此模式并不安全,因为有可能窃取 token通过将带有分配的代码应用到带有恶意 _setSize 的自定义对象方法。

关于javascript - 做一个对外只读的属性,但是我的方法还是可以设置的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23819730/

有关javascript - 做一个对外只读的属性,但是我的方法还是可以设置的的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  7. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  8. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  9. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  10. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

随机推荐