为什么不变性在JavaScript中如此重要(或需要)?

2020/10/15 13:41 · javascript ·  · 0评论

我目前正在研究React JSReact Native框架。阅读关于Facebook的Flux和Redux实现的文章时,我遇到了Immutability或Immutable-JS库

问题是,为什么不变性如此重要?更改对象有什么问题?它不是使事情变得简单吗?

举个例子,让我们考虑一个简单的新闻阅读器应用程序,其打开屏幕是新闻标题的列表视图。

如果我设置说一个带有初始的对象数组,我将无法对其进行操作。这就是不变性原则的意思,对不对?(如果我错了,请纠正我。)但是,如果我有一个新的News对象必须更新怎么办?在通常情况下,我可以将对象添加到数组中。在这种情况下我该如何实现?删除商店并重新创建?是不是将对象添加到数组中的开销较小?

我最近一直在研究同一主题。我会尽力回答您的问题,并尝试分享到目前为止我学到的知识。

问题是,为什么不变性如此重要?更改对象有什么问题?它不是使事情变得简单吗?

基本上可以归结为不变性增加了(间接)可预测性,性能和允许进行突变跟踪的事实。

可预测性

突变会隐藏变化,从而产生(意外的)副作用,这可能会导致讨厌的错误。当您强制执行不变性时,可以使您的应用程序体系结构和思维模型保持简单,这使得对应用程序进行推理变得更加容易。

性能

即使向不可变对象添加值意味着需要在需要复制现有值的情况下创建新实例,并且需要向新对象添加新值,这会消耗内存,但不可变对象可以利用结构共享来减少内存高架。

所有更新都返回新值,但是内部结构被共享以大大减少内存使用(和GC崩溃)。这意味着,如果将一个向量附加到具有1000个元素的向量上,则实际上不会创建长度为1001个元素的新向量。内部很可能只分配了几个小对象。

您可以在此处了解更多信息

变异追踪

除了减少内存使用量之外,不变性还允许您通过使用引用和值相等来优化应用程序。这使得查看任何更改是否真的很容易。例如,反应组件的状态变化。您可以shouldComponentUpdate通过比较状态对象来检查状态是否相同,并防止不必要的渲染。您可以在此处了解更多信息

其他资源:

如果我设置,说一个对象数组最初带有一个值。我无法操纵它。这就是不变性原则的意思,对吗?(如果我错了,请纠正我)。但是,如果我有一个新的News对象必须更新怎么办?在通常情况下,我可以将对象添加到数组中。在这种情况下我该如何实现?删除商店并重新创建?是不是将对象添加到数组中的开销较小?

是的,这是正确的。如果您对如何在应用程序中实现此方法感到困惑,我建议您看看redux如何做到这一点以熟悉核心概念,这对我有很大帮助。

我喜欢以Redux为例,因为它具有不变性。它具有一个不变的状态树(称为store),其中所有状态更改都是通过调度动作来显式显示的,该动作由化简器处理,该化简器接受前一个状态以及所述动作(一次一个)并返回应用程序的下一个状态。您可以在此处阅读有关其核心原理的更多信息

egghead.io有一个很棒的redux课程,redux的作者Dan Abramov解释了以下原理(我对代码进行了一些修改以更好地适应这种情况):

import React from 'react';
import ReactDOM from 'react-dom';

// Reducer.
const news = (state=[], action) => {
  switch(action.type) {
    case 'ADD_NEWS_ITEM': {
      return [ ...state, action.newsItem ];
    }
    default: {
        return state;
    }
  }
};

// Store.
const createStore = (reducer) => {
  let state;
  let listeners = [];

  const subscribe = (listener) => {
    listeners.push(listener);

    return () => {
      listeners = listeners.filter(cb => cb !== listener);
    };
  };

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach( cb => cb() );
  };

  dispatch({});

  return { subscribe, getState, dispatch };
};

// Initialize store with reducer.
const store = createStore(news);

// Component.
const News = React.createClass({
  onAddNewsItem() {
    const { newsTitle } = this.refs;

    store.dispatch({
      type: 'ADD_NEWS_ITEM',
      newsItem: { title: newsTitle.value }
    });
  },

  render() {
    const { news } = this.props;

    return (
      <div>
        <input ref="newsTitle" />
        <button onClick={ this.onAddNewsItem }>add</button>
        <ul>
          { news.map( ({ title }) => <li>{ title }</li>) }
        </ul>
      </div>
    );
  }
});

// Handler that will execute when the store dispatches.
const render = () => {
  ReactDOM.render(
    <News news={ store.getState() } />,
    document.getElementById('news')
  );
};

// Entry point.
store.subscribe(render);
render();

此外,这些视频还详细演示了如何实现以下方面的不变性:

不变性的相反观点

TL / DR:不变性是一种时尚趋势,而不是JavaScript中的必要性。如果您使用的是React,它确实为状态管理中一些令人困惑的设计选择提供了一种简洁的解决方法但是,在大多数其他情况下,它并不能为其引入的复杂性增加足够的价值,更多的是用来填补简历,而不是满足客户的实际需求。

长答案:请阅读以下内容。

为什么不变性在javascript中如此重要(或需要)?

好吧,我很高兴你问!

前段时间,一个很有才华的人Dan Abramov编写了一个名为Redux的javascript状态管理库,该使用纯函数和不变性。他还制作了一些非常酷的视频,使这个想法非常容易理解(和出售)。

时机是完美的。Angular的新颖性正在逐渐消失,JavaScript世界已准备好专注于具有适当程度的最新功能,并且该库不仅具有创新性,而且与React完美地结合在一起,React由另一个硅谷强者兜售

不幸的是,时尚在JavaScript世界中占统治地位。现在,阿布拉莫夫被誉为半神半兽,而我们所有人凡人都必须服从于不变性……无论它是否有意义。

更改对象有什么问题?

没有!

实际上,程序员已经将对象突变了……只要存在可以突变的对象。换句话说,超过50年的应用程序开发。

为什么使事情复杂化?当您发现对象cat并死亡时,您是否真的需要一秒钟cat来跟踪更改?大多数人只会说cat.isDead = true并完成它。

(变异对象)不是使事情简单吗?

是!..当然可以!

特别是在JavaScript中,实际上,JavaScript最有用用于呈现在其他位置(例如数据库中)维护的某些状态的视图。

如果我有一个新的News对象必须更新怎么办?...在这种情况下如何实现?删除商店并重新创建?是不是将对象添加到数组中的开销较小?

好吧,您可以采用传统方法并更新News对象,因此对象的内存表示形式会发生变化(以及向用户显示的视图,或者这样,人们希望如此)...

或者...

您可以尝试使用性感的FP / Immutability方法,并将对News对象的更改添加到跟踪每个历史更改的数组中,以便随后遍历数组并找出正确的状态表示形式(phe!)。

我正在尝试了解这里的内容。请赐教给我:)

时尚来来去去哥们。有很多方法可以给猫皮。

对不起,您不得不承受一套不断变化的编程范例的困惑。但是,嘿,欢迎来到俱乐部!

现在要牢记关于不变性的几个重要要点,并且您会以只有天真才能激发的狂热强度向您抛出这些要点。

1)不可变性对于避免多线程环境中的竞争条件非常有用

当多个线程想要更​​改对象时,多线程环境(例如C ++,Java和C#)会犯锁定对象的做法。这对性能不利,但比替代数据损坏更好。但是还不如使所有东西都变得不可变(上帝赞美Haskell!)。

可惜!在JavaScript中,您总是在单个线程上操作甚至是网络工作者(每个人都在单独的上下文中运行)。因此,由于您不能在执行上下文(所有可爱的全局变量和闭包)中包含与线程相关的竞争条件,因此支持不可变性的要点不在窗外。

(话虽如此,有一个优势,在网络工作者,这是,你有没有关于与主线程上的对象摆弄的预期使用纯函数。)

2)不变性可以(以某种方式)避免应用状态下的竞争状况。

这才是真正的症结所在,大多数(反应)开发人员都会告诉您,不变性和FP可以某种方式发挥作用,从而使应用程序的状态变得可预测。

当然,这并不意味着您可以避免数据库中出现争用情况,要想摆脱这种情况,就必须协调所有浏览器中的所有用户,为此,您需要使用诸如WebSockets(详情请参见下文),它将向运行该应用程序的每个人广播更改。

这也不意味着JavaScript中存在一些固有的问题,即您的应用程序状态需要不可变性才能变得可预测,任何在React之前编写前端应用程序的开发人员都会告诉您这一点。

这个相当令人困惑的说法只是意味着使用React,您的应用程序状态将更容易出现竞争状况,但是不变性可以使您减轻痛苦。为什么?由于React是特殊的..它被设计为高度优化的渲染库,状态管理几乎是事后才想到的,因此,组件状态通过您无法控制异步事件链(也称为“单向数据绑定”)进行管理结束并依靠你记住不要直接改变状态...

在这种情况下,很容易看出对不变性的需求与JavaScript无关,而与React中的竞争条件有很大关系:如果您的应用程序中有许多相互依赖的更改,并且没有简单的方法来找出原因您目前处于状态,您会感到困惑,因此使用不变性来跟踪每一个历史变化都是很有意义的

3)比赛条件绝对不好。

好吧,如果您使用的是React,那么可能就是这样。但是,如果您选择其他框架,它们将很少见。

此外,您通常要处理更大的问题……诸如依赖地狱之类的问题。就像a肿的代码库。就像您的CSS无法加载一样。就像缓慢的构建过程或卡在整体后端上,使得几乎不可能进行迭代。就像没有经验的开发人员一样,他们也不了解发生了什么并弄乱了事情。

你懂。现实。但是,嘿,谁在乎呢?

4)不变性利用引用类型来减少跟踪每个状态变化对性能的影响。

因为认真的说,如果您每次状态更改时都打算复制内容,那么最好确保您对此有所了解。

5)不变性使您可以撤消东西

因为er ..这是您的项目经理想要的第一功能,对吧?

6)不可变状态与WebSocket结合具有很大的潜力

最后但并非最不重要的一点是,与WebSocket结合使用时,状态增量的累积成为了一个令人信服的案例,它允许将状态作为不可变事件流轻松消耗...

一旦一分钱都落在了这个概念上(状态是事件的流转,而不是代表最近观点的原始记录集),不可变的世界将成为一个神奇的居住地。一个源于事件的奇迹和可能性,超越了时间本身并在完成后右这个绝对可以让实时应用程式EASI来完成,你只播事件给大家感兴趣,所以他们的流动建立自己表示本和自己的变化写回社区流动。

但是到了某个时刻,您醒了,意识到所有的奇观和魔力都不是免费的。与您渴望的同事不同,您的利益相关者(是的,付钱给您的人)很少关心哲学或时尚,而关心他们为打造可销售产品而付出的金钱。最重要的是,编写不可变代码更困难,更容易破坏代码,而且如果没有后端来支持不可变前端,那么毫无意义。当(如果!!)最终使您的利益相关者相信您应该通过WebSockets之类推送技术发布和使用事件时,您会发现扩展生产痛苦


现在寻求一些建议,您是否应该选择接受它。

使用FP / Immutability编写JavaScript的选择也是使应用程序代码库更大,更复杂且更难管理的选择。我强烈建议将这种方法限制在您的Redux reducer上,除非您知道自己在做什么...而且如果您要继续使用不可变性,无论如何,然后将不可变状态应用于整个应用程序堆栈,而不仅限于客户端,否则您将失去它的真正价值。

现在,如果您有幸能够在自己的工作中做出选择,那么请尝试(或不使用)您的智慧,并付钱给您的人做对的事情您可以根据自己的经验,内心或周围发生的事情(当然,如果每个人都在使用React / Redux,那么会有一个有效的论据,那就是找到资源来继续您的工作会更容易)。您可以尝试“恢复驱动开发”或“炒作驱动开发”方法。它们可能更像您的事情。

总之,可以说对于不变性的事情是,它令你的时尚与您同行,至少直到下一个热潮来临时,由此时你会很高兴地前进。


现在,在本次自我治疗之后,我想指出的是,我已将其作为文章添加到我的博客=> JavaScript中的不变性:反向视图如果您有强烈的感觉,也可以在这里回复;)。

问题是,为什么不变性如此重要?更改对象有什么问题?它不是使事情变得简单吗?

实际上,事实恰恰相反:可变性使事情变得更复杂,至少从长远来看。是的,它使您的初始编码更加容易,因为您可以随心所欲地进行更改,但是当程序变大时,这将成为一个问题–如果更改了值,则更改了什么?

当您使所有内容不变时,这意味着数据再也不会因意外而更改。您肯定知道,如果将值传递给函数,则无法在该函数中对其进行更改。

简而言之:如果您使用不可变的值,那么就很容易就可以对代码进行推理:每个人都会获得数据的唯一*副本,因此它无法处理并破坏代码的其他部分。想象一下,这使在多线程环境中工作变得容易得多!

注意1:根据您所做的工作,不变性可能会带来性能损失,但是Immutable.js之类的东西会尽可能地优化。

注2:在不太确定的情况下,您不确定Immutable.js和ES6的const含义完全不同。

在通常情况下,我可以将对象添加到数组中。在这种情况下我该如何实现?删除商店并重新创建?是不是将对象添加到数组中的开销较小?PS:如果该示例不是解释不变性的正确方法,请让我知道什么是正确的实际示例。

是的,您的新闻示例非常好,您的推理也完全正确:您不能只修改现有列表,因此需要创建一个新列表:

var originalItems = Immutable.List.of(1, 2, 3);
var newItems = originalItems.push(4, 5, 6);

尽管其他答案都不错,但要解决有关实际用例的问题(来自其他答案的注释),请让您离开正在运行的代码一分钟,然后看看您鼻子下无处不在的答案:git如果每次您推送一次提交都覆盖了存储库中的数据,将会发生什么

现在,我们讨论不可变集合面临的问题之一:内存膨胀。Git足够聪明,它不仅可以在每次更改时简单地创建文件的新副本,还可以跟踪差异

虽然我对git的内部运作了解不多,但我只能假设它使用了与您所引用的库类似的策略:结构共享。在后台,库使用try或其他树来仅跟踪不同的节点。

该策略对于内存中的数据结构也相当合理,因为有众所周知的对数时间运算的树运算算法。

另一个用例:假设您要在Web应用程序上使用撤消按钮。使用数据的不可变表示形式,实现这种过程相对简单。但是,如果您依赖突变,则意味着您必须担心缓存世界状况并进行原子更新。

简而言之,要为运行时性能和学习曲线的不变性付出代价。但是任何有经验的程序员都会告诉您,调试时间比代码编写时间大一个数量级。用户不必忍受与状态相关的错误,对运行时性能的轻微影响可能会胜过这些错误。

问题是,为什么不变性如此重要?更改对象有什么问题?它不是使事情变得简单吗?

关于可变性

从技术的角度来看,可变性没有错。速度很快,正在重新使用内存。开发人员从一开始就习惯了它(我记得它)。可变性的使用存在问题,并且这种使用可能带来麻烦。

如果对象不共享任何东西,例如存在于函数范围内并且没有暴露于外部,那么很难看到不变性的好处。实际上,在这种情况下,保持不变是没有意义的。当共享某些东西时,就开始具有不变性。

变异性头痛

可变的共享结构很容易造成很多陷阱。可以访问参考的代码任何部分的任何更改都会影响具有此参考可见性的其他部分。这种影响将所有部分连接在一起,即使他们不应该知道不同的模块也是如此。一个功能的变更可能会使应用程序的不同部分崩溃。这样的事情是不好的副作用。

接下来的突变问题通常是损坏的状态。当中间的突变过程失败时,可能会发生损坏状态,并且某些字段已被修改,而有些字段未被修改。

而且,由于突变,很难跟踪变化。简单的参考检查不会显示出差异,要知道需要进行一些深度检查会更改什么。另外,为了监视更改,需要引入一些可观察的模式。

最后,变异是信任缺失的原因。如果可以突变,如何确定某些结构具有想要的值。

const car = { brand: 'Ferrari' };
doSomething(car);
console.log(car); // { brand: 'Fiat' }

如上面的示例所示,通过可变结构总是可以通过具有不同的结构来完成。doSomething函数正在改变外部提供的属性。对代码不信任,您真的不知道拥有什么以及将拥有什么。发生所有这些问题的原因是:可变结构表示指向内存的指针。

不变性与价值有关

不变性意味着更改不会在相同的对象,结构上进行,而是以新的形式表示。这是因为引用不仅代表内存指针,还代表值。每次更改都会创造新的价值,并且不会触及旧的价值。这样清晰的规则可以使信任度和代码可预测性恢复。使用函数是安全的,因为它们可以使用自己的值而不是突变来代替突变。

使用值代替内存容器可以确定每个对象都代表特定的不可更改的值,并且可以安全地使用它。

不变的结构代表着价值。

我正在中篇文章中进一步探讨该主题-https: //medium.com/@macsikora/the-state-of-immutability-169d2cd11310

为什么不变性在JavaScript中如此重要(或需要)?

可以在不同的上下文中跟踪不变性,但最重要的是针对应用程序状态和应用程序UI进行跟踪。

我将JavaScript Redux模式视为一种非常时尚和现代的方法,因为您提到了这一点。

对于UI,我们需要使其可预测如果可以预测的话UI = f(application state)

应用程序(在JavaScript中)确实通过使用reducer函数实现的动作来更改状态

reducer函数仅获取动作和旧状态并返回新状态,从而保持旧状态不变。

new state  = r(current state, action)

在此处输入图片说明

好处是:由于所有状态对象均已保存,因此您可以遍历状态,并且自 UI = f(state)

因此,您可以轻松地撤消/重做。


碰巧创建所有这些状态仍然可以提高内存效率,与Git的类比很棒,并且在Linux OS中,我们使用符号链接(基于inode)也有类似的类比。

Java不变性的另一个好处是,它减少了时间耦合,这通常对设计具有实质性的好处。考虑对象的接口有两种方法:

class Foo {

      baz() {
          // .... 
      }

      bar() {
          // ....
      }

}

const f = new Foo();

在某些情况下,可能需要进行调用baz()才能使对象处于有效状态,才能使调用bar()正常工作。但是你怎么知道的呢?

f.baz();
f.bar(); // this is ok

f.bar();
f.baz(); // this blows up

要弄清楚这一点,您需要仔细检查类的内部,因为从检查公共接口并不能立即看出来。在具有大量可变状态和类的大型代码库中,此问题可能会爆炸。

如果Foo是不变的,那么这不再是问题。可以安全地假定我们可以调用bazbar以任何顺序调用,因为类的内部状态无法更改。

曾几何时,线程之间的数据同步存在问题。这个问题让人非常痛苦,有10多种解决方案。有些人试图从根本上解决它。这是函数式编程诞生的地方。就像马克思主义一样。我不明白Dan Abramov是如何将这个想法出售给JS的,因为它是单线程的。他是个天才。

我可以举一个小例子。__attribute__((pure))gcc中有一个属性如果不特别清除函数,编译器将尝试解决您的函数是否纯净。即使状态是可变的,您的函数也可以是纯函数。不变性只是保证您的功能纯净的100多种方法之一。实际上,您的函数中有95%是纯函数。

如果您实际上没有认真的理由,则不应使用任何限制(例如不变性)。如果要“撤消”某些状态,则可以创建事务。如果您想简化通信,则可以发送不可变数据的事件。它是由你决定。

我正在写后马克思主义共和国的这封信。我敢肯定,任何想法的激进都是错误的方法。

一个不同的...

我的其他答案从非常实际的角度解决了这个问题,我仍然喜欢它。我决定将其添加为另一个答案,而不是对该附录进行补充,因为这是一种无聊的哲学语,希望也能回答该问题,但实际上与我现有的答案不符。

TL; DR

即使在小型项目中,不变性也很有用,但不要因为它的存在而假定它对您有用。

答案长得多

注意:出于这个答案的目的,我使用“纪律”一词来表示自我克制,以获得一些好处。

这在形式上类似于另一个问题:“我应该使用Typescript吗?为什么类型在JavaScript中如此重要?”。它也有类似的答案。请考虑以下情形:

您是大约5000行的JavaScript / CSS / HTML代码库的唯一作者和维护者。您的半技术老板会读一些有关Typescript的新知识,并建议我们可能要改用它,但由您决定。因此,您可以阅读,玩耍等等。

因此,现在您可以选择了,您要使用Typescript吗?

Typescript具有一些引人注目的优势:智能感知,及早发现错误,预先指定您的API,在重构破坏它们时易于修复,减少测试。Typescript也要付出一些代价:在不是特别强大的类型系统中,某些非常自然且正确的JavaScript习惯很难建模,注释会增加LoC,重写现有代码库的时间和精力,以及构建管道中的额外步骤等。从根本上讲,它会分割出可能的正确JavaScript程序子集,以换取您的代码更可能正确的承诺它是任意限制的。这就是重点:您强加了一些限制自己的纪律(希望能避免用脚射击)。

回到上面段落中改写的问题:值得吗?

在描述的场景中,我认为如果您对中小型JS代码库非常熟悉,那么选择使用Typescript会比实际更美观。这就是罚款,没有什么与美学,他们只是不一定引人注目。

方案B:

您现在换工作,现在是Foo Corp.的业务部门程序员。您正在与10个人组成的团队合作,在90000 LoC(并在不断增加)的JavaScript / HTML / CSS代码库中,构建流程涉及babel,webpack ,一组polyfill,与各种插件,状态管理系统,约20个第三方库,约10个内部库,编辑器插件(如带有内部样式指南规则的linter)等互动。

当您还是5k LoC男/女时,这没什么大不了的。即使文档是不是那个什么大不了的事,即使回来6个月后的代码的特定部分,你可以算出它很轻松了。但是现在纪律不仅很好而且很有必要这门学科可能不涉及打字稿,但可能涉及某种形式的静态分析,以及所有其他形式的编码规则(文档,风格指南,构建脚本,回归测试,CI)的。纪律不再是一种奢侈,而是必需品

所有这一切都适用GOTO于1978年:您在C中笨拙的小酒杯游戏可以使用GOTOs和意大利面条逻辑,选择自己的冒险方式并没有什么大不了的,但是随着程序的发展,更雄心勃勃的,善于的,无纪律的使用GOTO无法持续。所有这些今天都适用于不变性。

就像静态类型一样,如果您不是在大型的代码库上工作,而没有一组工程师来维护/扩展它,那么使用不变性的选择比实际更美观:它的好处仍然存在,但可能还不超过成本。

但是与所有有用的学科一样,它不再是可选的。如果我想保持健康的体重,那么可以选择不加冰淇淋。但是,如果我想成为一名竞技运动员,那么我是否选择吃冰淇淋取决于我选择的目标。如果您想用软件来改变世界,那么不变性可能就是避免它因自身重量而崩溃的必要条件。

举个例子:

const userMessage  = { 
   user: "userId",
   topic: "topicId"
   content: {}
}

validateMessage(userMessage)
saveMessage(userMessage) 
sendMessageViaEmail(userMessage)
**sendMessageViaMobilePush(userMessage)** 

现在回答一些问题:

  1. 可变代码中sendMessageViaMobilePush(userMessage))行上的userMessage下是什么?
  1. 不变代码中同一行的userMessage下是什么?

  2. 您能猜出在可变代码中通过哪种方法更新“ id”吗?

  3. 在可变代码中,人们没有收到推送消息(sendMessageViaMobilePush)。你能猜出为什么吗?

希望这可以帮助您了解在javascript中使用可变对象的主要问题。

请注意,当复杂性增加时,很难检查设置的内容和位置,尤其是在与他人一起工作时。

我为可变(或不可变)状态创建了一个框架不可知的开源(MIT)库,它可以替换所有不可变的存储,如库(redux,vuex等)。

不可变状态对我来说很丑陋,因为要做的工作太多(用于简单的读/写操作的动作很多),代码的可读性差,并且无法接受大数据集的性能(重新渲染整个组件:/)。

使用deep-state-observer,我只能使用点表示法更新一个节点并使用通配符。我还可以创建状态历史记录(撤消/重做/时间旅行),仅保留已更改的具体值{path:value}=减少内存使用量。

使用深度状态观察器,我可以微调事物,并且可以精确控制组件的行为,因此可以大大提高性能。代码更具可读性,并且重构更加容易-只需搜索并替换路径字符串(无需更改代码/逻辑)。

我认为赞成不可变对象的主要原因是保持对象的状态有效。

假设我们有一个名为的对象arr当所有项目均为相同字母时,此对象有效。

// this function will change the letter in all the array
function fillWithZ(arr) {
    for (var i = 0; i < arr.length; ++i) {
        if (i === 4) // rare condition
            return arr; // some error here

        arr[i] = "Z";
    }

    return arr;
}

console.log(fillWithZ(["A","A","A"])) // ok, valid state
console.log(fillWithZ(["A","A","A","A","A","A"])) // bad, invalid state

如果arr成为一个不变的对象,那么我们将确保arr始终处于有效状态。

本文地址:http://javascript.askforanswer.com/weishenmebubianxingzaijavascriptzhongrucizhongyaohuoxuyao.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!