《JavaScript应用程序设计》一一1.15 减少隐式副作用

1.15 减少隐式副作用

在JavaScript中有两类十分常见却可以轻易规避的错误。第一类是语法错误,第二类是无意识的隐式副作用。
隐式副作用是代码复用的大敌,因为它导致函数被其作用域外的状态所劫持。隐式副作用的产生是由于多个函数间共享变量或属性所致,举例来说,应用中有一个购物车功能,用户在会话期间可以对购物车的内容进行保存。
用户想要更改当前会话中购物车的内容顺序,只需:

test('Order WITH unintentional side effect.', function () {
      var cartProto = {
          items: [],

          addItem: function addItem(item) {
            this.items.push(item);
          }
        },

        createCart = function (items) {
          var cart = Object.create(cartProto);
          cart.items = items;
          return cart;
        },

        // Load cart with stored items.
        savedCart = createCart(["apple", "pear", "orange"]),

        session = {
          get: function get() {
            return this.cart;
          },

          // Grab the saved cart.
          cart: createCart(savedCart.items)
        };

      // addItem gets triggered by an event handler somewhere:
      session.cart.addItem('grapefruit');

      ok(session.cart.items.indexOf('grapefruit')
        !== -1, 'Passes: Session cart has grapefruit.');

      ok(savedCart.items.indexOf('grapefruit') === -1,
        'Fails: The stored cart is unchanged.');
    });

很不幸,当用户在当前会话中添加或删除购物车内容时,之后的购物车设置信息也被连带删除了,导致这个问题的代码片段是:

createCart = function (items) {
  var cart = Object.create(cartProto);
  cart.items = items;
  return cart;
},

请留意此处,cart.items是对原型对象上items属性的直接引用,现在对代码做略微修改。
cart.items = Object.create(items);
新的购物车拥有自己的内容副本,不会再对storedCart对象带来直接影响。
若想减少隐式副作用在程序中的出现概率,最好的方法就是在函数内部对它进行规避。所有外部变量传入函数之前,最好是先经过一轮复制,不要将原始值直接传入。
纯函数没有隐式副作用,因为它在调用时不会更改任何外部变量,决定它返回值的因素仅有一个,即它的入参。
尽可能地确保你的函数在执行过程中不会影响外界的状态,在执行结尾处返回修改后的变量副本而不是原始引用,请注意,在整个执行过程中你仍然有更改外部变量的机会。就像REST架构下客户端与服务器的数据传输一样:客户端首先从服务器获取一份数据资源的副本,修改其内容,再将处理过的副本发送回至服务器。建议归建议,大部分情况下开发者为了兼顾到应用的性能不会这么去做,不过使用纯函数或许可以起到一定效果。
在每个函数内部对隐式副作用做规避,一来可以减少代码冗余,二来可以帮助你提升程序分层设计的意识。举例来说,你有一个开工已达数月之久的项目,现在你需要对其新增数据校验功能,当项目中仅有一个函数能够对数据进行提交操作时,你只需把校验功能安插在此函数调用之前即可。但如果这样的函数在项目中有百来个,势必会影响校验功能的加入。
将不同的功能逻辑相互隔离,可以让你更好地管理应用状态。如果函数在执行过程中不会被外界的状态变更所干扰,它只需少量代码就可以完成手头的工作,因为眼前的任务只有一个。
同理,操作DOM的函数一定只专注于DOM树操作,比如视图的render()方法,又如一些DOM树插件。

时间: 2024-09-14 23:31:34

《JavaScript应用程序设计》一一1.15 减少隐式副作用的相关文章

《JavaScript应用程序设计》导读

前言 在本书中我们先后提到了不少Web开发类书籍,但对于那些想从头开始学习构建完整JavaScript应用的读者来说,市面上真正值得推荐的JavaScript教程又显得十分稀缺.与此同时,现在几乎每一个互联网创业公司的人员配备中都少不了资深JavaScript应用程序开发者的角色.本书旨在帮助人们了解如何构建易于扩展和维护的完整JavaScript应用. 本书并不打算向读者详细讲解JavaScript语言基础,它基于你现有的JavaScript知识结构,向你展示那些会让你的编码工作长久受益的Ja

JavaScript运算符规则与隐式类型转换详解

本文中涉及的参考资料全部声明在了JavaScript 数据结构学习与实践资料索引 . 隐式类型转换 在 JavaScript 中,当我们进行比较操作或者加减乘除四则运算操作时,常常会触发 JavaScript 的隐式类型转换机制;而这部分也往往是令人迷惑的地方.譬如浏览器中的 console.log 操作常常会将任何值都转化为字符串然后展示,而数学运算则会首先将值转化为数值类型(除了 Date 类型对象)然后进行操作. 我们首先来看几组典型的 JavaScript 中运算符操作结果,希望阅读完本

《JavaScript应用程序设计》一一2.2 函数声明

2.2 函数声明 在JavaScript中有多种定义函数的方法,不同方法各有优缺点. function foo() { /* Warning: arguments.callee is deprecated. Use with caution. Used here strictly for illustration. */ return arguments.callee; } foo(); //=> [Function: foo] 在这段代码中,foo()是一个函数声明.正如在"变量提升&q

《JavaScript应用程序设计》一一第3章 对象

第3章 对象JavaScript拥有原型继承.动态对象扩展.闭包等特性,在现今市面上流行的所有编程语言中,基于对象编程的JavaScript最具灵活性与表现力.在JavaScript中,你可以将诸如函数.数组.键/值对以及一些基础数据结构都视为对象,甚至一些原始数据类型在用点语法做属性操作时,也会被JavaScript隐式当作对象处理.为了能够调用原型链上的方法,原始数据类型在使用时会被临时包裹为对象,例如:'tonya@example.com'.split('@')[1]; // => exa

JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型_基础知识

ECMAScript是一种动态类型的语言,构建于5种简单数据类型(Undefined.Null.Boolean.Number.String)和一种复杂数据类型(Object)的基础之上.这篇文章就来复习一下简单数据类型,我会尽量从编程实践的角度来描述,下面代码运行环境为FireFox 14.0.1. 简单数据类型 简单数据类型 取值 Undefined undefined(只有一个值) Null null(只有一个值) Boolean true|false(只有两个值) Number 数值 St

《JavaScript高级程序设计》学习笔记

系统学习JS, 从<JavaScript高级程序设计>入门,通过学习jQuery或者angularJS源码来进阶. 第1章 JavaScript简介 1.JS问世的目的是处理以前由服务器端语言(如Perl)负责的一些输入验证操作, 由Netscape主导开发,那时候微软的IE还不能独领风骚.最初叫做LiveScript,当时媒体热炒Java,于是改名为JavaScript. JavaScript和ECMAScript通常指同一门语言,但是,JavaScript是ECMA-262标准的实现和扩展

JavaScript的隐式转换

JavaScript的数据类型分为六种,分别为null,undefined,boolean,string,number,object.object是引用类型,其它的五种是基本类型或者是原始类型.我们可以用typeof方法打印来某个是属于哪个类型的.不同类型的变量比较要先转类型,叫做类型转换,类型转换也叫隐式转换.隐式转换通常发生在运算符加减乘除,等于,还有小于,大于等.. typeof '11'  //string       typeof(11) //number '11' < 4     /

浅析JavaScript中的隐式类型转换

        这篇文章主要是对JavaScript中的隐式类型转换进行了详细分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助 如果把通过函数或方法调用,明确的将某种类型转换成另一种类型称为显示转换 ,相反则称为隐式类型转换 .google和维基百科中没有找到"显示类型转换","隐式类型转换"的字眼.暂且这么称呼.   一. 运算中存在的隐式类型转换    1, "+"运算符     代码如下: var a = 11, b = '22'; 

JavaScript高级程序设计(第3版)学习笔记9 js函数(下)_基础知识

再接着看函数--具有魔幻色彩的对象. 9.作为值的函数 在一般的编程语言中,如果要将函数作为值来使用,需要使用类似函数指针或者代理的方式来实现,但是在ECMAScript中,函数是一种对象,拥有一般对象具有的所有特征,除了函数可以有自己的属性和方法外,还可以做为一个引用类型的值去使用,实际上我们前面的例子中已经有过将函数作为一个对象属性的值,又比如函数也可以作为另一个函数的参数或者返回值,异步处理中的回调函数就是一个典型的用法. 复制代码 代码如下: var name = 'linjisong'