Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【译】 javascript 函数参数设计

【译】 javascript 函数参数设计

作者头像
IMWeb前端团队
发布于 2019-12-04 05:09:02
发布于 2019-12-04 05:09:02
51400
代码可运行
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队
运行总次数:0
代码可运行

本文作者:IMWeb jerytang 原文出处:IMWeb社区 未经同意,禁止转载 本文 编译https://gcanti.github.io/2014/09/25/six-reasons-to-define-constructors-with-only-one-argument.html 原文标题为:构造器只使用一个参数的 6 个原因 (Six reasons to define constructors with only one argument)

简介

Reddit 上 有关于本文的评论。

JavaScript中,通常像下面这样来定义一个 "class" :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function VanillaPerson(name, surname) { // multiple arguments
  this.name = name;
  this.surname = surname;
}

var person = new VanillaPerson('Giulio', 'Canti');
person.name; // => 'Giulio'

如果构造器只用一个参数,改写上面的代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Person(obj) { // only one argument
  this.name = obj.name;
  this.surname = obj.surname;
}

var person = new Person({name: 'Giulio', surname: 'Canti'});
person.name; // => 'Giulio'

下面,我将列出 6 个方面谈谈,在排除对性能有极端要求的情况下,为什么后者是更好的方案。

1. 维护性

如果 Person 添加了一个参数,先看第一种方案要多少处修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 ...
 * @param {String} email // change
 ...
 */
function VanillaPerson(name, surname, email) { // change

  // make `new` optional
  if (!(this instanceof VanillaPerson)) {
    return new VanillaPerson(name, surname, email); // change
  }

  this.name = name;
  this.surname = surname;
  this.email = email; // change
}

再看,使用一个 object 参数,仅需要改写一处:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Person(obj) {

  if (!(this instanceof Person)) {
    return new Person(obj);
  }

  this.name = obj.name;
  this.surname = obj.surname;
  this.email = obj.email; // change
}

许多人认为,构造器中使用 new 是一种 "反模式",但是我感觉还 OK. 通常用 new 能更明确的表示实例化一个实例,但是我依然会像下面这样写, $('.myclass').show() ,而不是,new $('.myclass').show() ,尽管前者是一种 "反模式"。

2. 命名参数 (Named parameters)

JavaScript是不支持命名参数【1】,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// first argument is name or surname? I don't remember
var person = new VanillaPerson('Canti', 'Giulio'); // wrong!

使用一个 object 参数能很好的模拟命名参数,虽然要多些点代码,但是更加易读

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// order doesn't matter and it's more readable
var person = new Person({surname: 'Canti', name: 'Giulio'});

3. 控制可选参数

采用多参数方案,当有可选参数的时候,将会非常丑陋:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function VanillaPerson(name, surname, email, vat, address) {
  this.name = name;
  this.surname = surname;
  this.email = email;
  this.vat = vat;
  this.address = address;
}

// I must count the arguments to know where to put 'myaddress'
var person = new VanillaPerson('Giulio', 'Canti', null, 'myaddress'); // wrong!

而使用单个 object 参数,将非常方便:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var person = new Person({surname: 'Canti', name: 'Giulio', address: 'myaddress'});

4. 使用 json 数据的方便性

假设你从某个 API 通过 ajax 获取的 JSON 数据,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "name": "Giulio",
  "surname": "Canti"
}

使用多参数方案的话,你需要定义一个函数来处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function deserialize(x) {
  return new VanillaPerson(x.name, x.surname);
}

var person = deserialize(json);

使用单个 object 参数的方案,直接就可以使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var person = new Person(json);

更多关于这个点的讨论,参见另一篇 blog,JSON Deserialization Into An Object Model.

5. 幂等性

当函数 f 满足 f(f(x)) = f(x) ,称 f 具有幂等性。

上面的多参数函数不是幂等的,但是可以很容易的让 object 参数的函数变为幂等的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Person(obj) {

  if (obj instanceof Person) {
    return obj;
  }

  ...
}

var person = new Person({name: 'Giulio', surname: 'Canti'});
new Person(person) === person; // => true

6. 避免重复

如果你需要建立各种模型,并且需要对模型的字段进行验证,使用单个 object 参数,实现一个如下的函数,可以节省每次实例化时的验证:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function struct(props) {

  function Struct(obj) {

    // make Struct idempotent
    if (obj instanceof Struct) return obj;

    // make `new` optional, decomment if you agree
    // if (!(this instanceof Struct)) return new Struct(obj);

    // add props
    for (var name in props) {
      if (props.hasOwnProperty(name)) {
        // here you could implement type checking exploiting props[name]
        this[name] = obj[name];
      }
    }

    // make the instance immutable, decomment if you agree
    // Object.freeze(this);

  }

  // keep a reference to meta infos for further processing,
  // documentation tools and IDEs support
  Struct.meta = {
    props: props
  };

  return Struct;

}

// defines a 1-arity Person class
var Person = struct({
  name: String,
  surname: String
});

var person = new Person({surname: 'Canti', name: 'Giulio'});

上面实现了一个 Person 类,要求 surname 和 name 字段都必须为字符串,struct 是一个通用的实现,里面完成了对传入的 object 的所有验证逻辑。

延伸阅读

(译注,第 6 点,作者专门写了一篇 blog ,实现了一个非常有意思的验证库【2】)

JavaScript, Types and Sets - Part I

github

使用单个 object 作为参数的特性实现 tcomb .

tcomb可以用于浏览器和 Node.js ,用于 javascript的类型检查,适合 Domain Driven Design ,增加代码内部安全性。

(译注,但是封装是有代价的【3】)

备注

【1】译注:对于支持 Named parameters 的语言,你可以写成下面这样,函数内部是根据名字而不是位置来引用参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var person = new VanillaPerson(surname='Canti', name='Giulio');

【2】译注:https://github.com/gcanti

【3】译注:参见 https://gcanti.github.io/2014/09/25/six-reasons-to-define-constructors-with-only-one-argument.html 的评论

I don't like this for a performance reasons. You are creating extra object just to pass values and it is garbage collected later. It doesn't matter for a few instances, but doing hundreds or thousands instances this way and you would feel small hiccups, especially in performance sensitive application like games.

每次函数调用都传入了一个额外的 object ,增加了垃圾回收的负担,实例少的时候还可以接受,但是,实例数量一多,必然会带来无问题。

【4】原文下评论中也指出 ExtJs 早就这样干了,ExtJS 的参数不就是一个大大的 json config.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-12-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript 的简洁之道
如果你关注代码本身和代码的编写方式,而不是只关心它是否能工作,那么你写代码是有一定的水准。专业开发人员将为未来的自己和“其他人”编写代码,而不仅仅只编写当前能工作就行的代码。
前端小智@大迁世界
2019/06/15
4470
盘点JavaScript中getter()和setter()函数的使用
第一种是 数据属性。已经知道如何使用它们了。到目前为止,使用过的所有属性都是数据属性。
前端进阶者
2021/09/10
1.9K0
盘点JavaScript中getter()和setter()函数的使用
JavaScript 私有类字段和 TypeScript 私有修饰符
在本文中,我们将对 JavaScript 私有类字段进行一些说明,并了解它们与 TypeScript 私有修饰符的区别。
疯狂的技术宅
2020/02/18
2.2K0
orm2 中文文档 3. 定义模型
在[连接](1. Connecting to Database.md)之后,你可以使用连接对象(db)来定义你的模型。你需要指定模型的名称,一个用于描述的属性和一些(可选的)选项。下面是一个简短的例子:
ApacheCN_飞龙
2022/11/27
2380
JS面向对象笔记二
注意点:当构造函数里面有return关键字时,如果返回的是非对象,new命令会忽略返回的信息,最后返回时构造之后的this对象;   如果return返回的是与this无关的新对象,则最后new命令会返回新对象,而不是this对象。示例代码:
tandaxia
2018/09/27
5.8K0
JS面向对象笔记二
JS中的面向对象、原型、原型链、继承总结大全
补充: js中说一切都是对象,是不完全的,在js中6种数据类型(Undefined,Null,Number,Boolean,String,Object)中,前五种是基本数据类型,是原始值类型,这些值是在底层实现的,他们不是object,所以没有原型,没有构造函数,所以并不是像创建对象那样通过构造函数创建的实例。关于对象属性类型的介绍就不介绍了。
疯狂的技术宅
2019/03/27
1.5K0
JS中的面向对象、原型、原型链、继承总结大全
深入理解JavaScript面向对象的程序设计(一)——对象的创建
类似Java等面向对象语言中创建对象的语法,在 JavaScript中可以通过执行 new操作符后跟要创建的对象类型的名称来创建。JavaScript中通过如下方式可以创建一个Object对象:
CherishTheYouth
2020/11/12
4580
深入理解JavaScript面向对象的程序设计(一)——对象的创建
Frontend 入门笔记
仔细想了想,还是不要待在 comfort zone 里,干脆直接学 js 全栈,也可以提高 js 的熟练度。
Clouder0
2022/09/23
5690
Frontend 入门笔记
深入浅出js实现继承的7种方式
  有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inheritance里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承
TimothyJia
2019/11/12
2.1K0
原来 js 跟 ts 也有相识之处
闭包是许多类似私有模式的基础,比如流行的模块模式。但在ECMAScript 2015 classes近年来接管之后,开发人员感到有必要对classes成员的隐私进行更多的控制。
公众号---人生代码
2021/04/22
1.6K0
JavaScript 面向对象
创建函数 Foo 的时候,就会有一个内置的 Foo.prototype 属性,并且这个属性是对象。
零式的天空
2022/03/02
2870
orm2 中文文档
0.10.x,0.12.x 和 iojs-1.5 版本的测试在 Travis CI 上运行。如果你想要的话,可以在本地运行测试:
ApacheCN_飞龙
2022/11/27
6130
javascript 基础_JavaScript高级编程
1.分类: -基本类型 -String:任意字符串 -Number:任意的数字 -boolean: true/false -undefined:未定义 -null:空
全栈程序员站长
2022/09/24
1.6K0
javascript 基础_JavaScript高级编程
JavaScript学习总结(四)——this、原型链、javascript面向对象
根据题目要求,对给定的文章进行摘要总结。
张果
2018/01/04
1.5K0
JavaScript学习总结(四)——this、原型链、javascript面向对象
JavaScript面向对象精要(二)
构造函数就是用new创建对象时调用的函数。使用构造函数的好处在于所有用同一个构造函数创建的对象都具有同样的属性和方法。
奋飛
2019/08/15
4560
[JavaScript进阶]从JavaScript原型到面向对象
首先给出结论,JavaScript 的本身是支持面向对象的,它本身具备着强大灵活的 OOP 语言能力。但是对于使用过基于类的语言 (如 Java 或 C++) 的开发人员来说,JavaScript 确实有点令人困惑,因为它是动态的,并且本身不提供一个 class 实现。虽然在 ES6 中引入了 class 关键字,但它只是一个语法糖,本质还是基于JavaScript 的原型来实现的。
用户1462769
2019/08/12
6420
[JavaScript进阶]从JavaScript原型到面向对象
重读《JavaScript高级程序设计》
ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数类型和数量)不同即可[p66]。ECMAScript的类型是松散形的,没有签名,所以是没有重载的。
Jimmy_is_jimmy
2019/07/31
1.1K0
JavaScript常见手写题熬夜整理
题目描述:有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
helloworld1024
2022/09/22
9090
《JavaScript高级程序设计》读书笔记
数据传送率的单位.意思是每秒钟多少千字节.比如20Kbit/s就是每秒钟20000个字节.一般上网、下载的速度用这个单位.adsl宽带上网下载速度大概为30-50Kbit/s.
用户3880999
2023/04/13
6790
《JavaScript高级程序设计》读书笔记
Javascript构造函数
     构造函数注意事项: 1.默认函数首字母大写 2.构造函数并没有显示返回任何东西。new 操作符会自动创建给定的类型并返回他们,当调用构造函数时,new会自动创建this对象,且类型就是构造函数类型。 3.也可以在构造函数中显示调用return.如果返回的值是一个对象,它会代替新创建的对象实例返回。如果返回的值是一个原始类型,它会被忽略,新创建的实例会被返回。 function Person( name){                 this.name =name;            
hbbliyong
2018/03/06
1.5K0
Javascript构造函数
相关推荐
JavaScript 的简洁之道
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验