编写一个方法同样意味着开发一个API。不论是给你自己,你团队中的其他程序员还是其他使用你项目的开发者来说都是一样。根据你编写函数的大小,复杂程度以及目的,你需要考虑函数的默认行为以及函数输入/输出的接口。
函数的默认参数和属性简写是ES6中可以帮助你编写API的两个实用特性。
让我们快速的帮你梳理一下知识并再次回顾一下语法。默认参数允许我们在初始化函数时声明默认值。当一个参数被忽略或值是 undefined
的时候就会使用默认值;这表示 null
是一个有效的参数值。默认参数可以是从数字到另一个函数的任意值。
// 基本语法
function multiply (a, b = 2) {
return a * b;
}
multiply(5); // 10
// 后面出现的默认参数可以使用之前声明过的默认参数
function foo (num = 1, multi = multiply(num)) {
return [num, multi];
}
foo(); // [1, 2]
foo(6); // [6, 12]
让我们通过一个简单的例子来演示默认参数是如何提高你的开发速度并帮你更好的组织代码的。
我们的示例方法名为 createElement()
。它接受一些配置参数并返回一个HTML元素。它的API是这样的:
// 我们想要一个包含一段文本并有两个CSS类的<p>元素,
// 返回 <p class="very-special-text super-big">Such unique text</p>
createElement('p', {
content: 'Such unique text',
classNames: ['very-special-text', 'super-big']
});
// 为了让这个方法更实用,当缺少某些参数或没有传入任何参数的时候它始终应该返回一个默认元素
// createElement();
// <div class="module-text default">Very default</div>
这个函数的实现逻辑并不复杂,但由于要处理默认参数,它的函数体很长:
// 不使用默认参数时它的函数体很长
function createElement (tag, config) {
tag = tag || 'div';
config = config || {};
const element = document.createElement(tag);
const content = config.content || 'Very default';
const text = document.createTextNode(content);
let classNames = config.classNames;
if (classNames === undefined) {
classNames = ['module-text', 'default'];
}
element.classList.add(...classNames);
element.appendChild(text);
return element;
}
目前为止还算顺利。这里我们都做了什么呢?我们:
tag
和 config
设置了默认值,以防调用时没有传入(注意某些语法检查器不喜欢对参数重新赋值)
classNames
,如果没有则使用默认值
现在让我们把这个方法优化的更简洁,容易开发并且更清晰的展现它的意图。
// 为所有参数设置默认值
function createElement (tag = 'div', {
content = 'Very default',
classNames = ['module-text', 'special']
} = {}) {
const element = document.createElement(tag);
const text = document.createTextNode(content);
element.classList.add(...classNames);
element.appendChild(text);
return element;
}
我们没有修改函数的逻辑,只是把所有关于默认值的处理从函数体中移除了。现在的函数签名包含了所有的默认值。
让我再进一步的解释这一部分可能看起来有些迷惑的代码:
// 这里到底发生了什么?
function createElement ({
content = 'Very default',
classNames = ['module-text', 'special']
} = {}) {
// 函数体
}
我们不仅声明了一个默认的 对象
参数,还声明了默认的对象属性。这比单单的声明一个默认对象(比如 config={}
)并在之后设置默认属性能更清楚的表明默认值是什么样的。这可能需要一段时间来适应,但它最终能改善你的工作流。
当然,我们也可以争辩说更大的配置项会带来更大的开销,还不如把默认值的处理保留在函数体里简单。
如果函数接受一个巨大的配置对象作为参数,你的代码可能会很长。事先准备好一些变量并添加到上述配置对象中是一种常见的方式。属性简写是一种可以简化这个步骤并增加代码可读性的语法糖。
const a = 'foo', b = 42, c = function () {};
// 之前我们会像下面这样使用这些常量
const alphabet = {
a: a,
b: b,
c: c
};
// 但是通过使用简写,我们可以像下面这样
// 这和上边的代码是等价的
const alphabet = { a, b, c };
好吧,让我们再看一个更常见的例子。下面的函数接收一些数据,对它们进行操作并调用另一个方法:
function updateSomething (data = {}) {
const target = data.target;
const veryLongProperty = data.veryLongProperty;
let willChange = data.willChange;
if (willChange === 'unwantedValue') {
willChange = 'wayBetter';
}
// Do more.
useDataSomewhereElse({
target: target,
property: veryLongProperty,
willChange: willChange,
// .. more
});
}
对变量和对象属性使用相同的命名是很常见的。通过结合属性简写和解构,我们可以很大程度上简化这段代码:
function updateSomething (data = {}) {
// 这里我们使用解构把数据从对象中保存到常量中
const { target, veryLongProperty: property } = data;
let { willChange } = data;
if (willChange === 'unwantedValue') {
willChange = 'wayBetter';
}
// Do more.
useDataSomewhereElse({ target, property, willChange });
}
再次说明,这可能会需要一段时间来适应。最终,它成为了帮我更快速开发并保持更简洁的函数体的JavaScript新特性中的一员。
别急,还没完呢!对象中的属性简写还用于方法定义。
// 与其每次都写function关键字
const module = {
foo: 42,
bar: function (value) {
// do something
}
};
// 我们可以直接忽略它而使用一种更简短的写法
const module = {
foo: 42,
bar (value) {
// do something
}
};
默认属性和属性简写是一种可以让你的代码更有条理,甚至更短的好方法。总而言之,默认参数让我可以把更多的精力放在函数的实际目的而不用被准备大量的默认值和if语句分心。
属性简写实际上更像是一个美化代码的功能,但我发现使用它之后我的效率更高了而花在编写所有的变量,配置对象以及function关键字上的时间更少了。
你已经开始使用默认参数和属性简写了吗?
往期精选文章 |
---|
使用虚拟dom和JavaScript构建完全响应式的UI框架 |
扩展 Vue 组件 |
使用Three.js制作酷炫无比的无穷隧道特效 |
一个治愈JavaScript疲劳的学习计划 |
全栈工程师技能大全 |
WEB前端性能优化常见方法 |
一小时内搭建一个全栈Web应用框架 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
让你分分钟理解 JavaScript 闭包 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。