定义函数时为参数指定默认值的能力,是现代动态编程语言的标配。在ES6出现之前,JavaScript是没有这种能力的,框架为了实现参数默认值,用了很多技巧。

ES6 的默认参数值功能,与其他语言的语法类似,但功能更强大。

首先,是可以用标量值为函数参数指定默认值,这个标量可以是基本类型、数组、对象。

例1:

function foo(name = {first:"张",last:"三"},age = 20, phones = ['18888888888','18666666666']) {
console.log("姓名:" + name.first + name.last);
console.log("年龄:" + age);
console.log("电话:" + phones.join());
}
foo();
foo({first: "李",last: "向阳"});
foo({first: "张",last: "三"}, age = 40);
foo({first: "张",last: "三"}, age = 20, ['18666666666', '18888888888', '13333333333']);

输出结果为:

姓名:张三
年龄:20
电话:18888888888,18666666666 姓名:李向阳
年龄:20
电话:18888888888,18666666666 姓名:张三
年龄:40
电话:18888888888,18666666666 姓名:张三
年龄:20
电话:18666666666,18888888888,13333333333

在其它语言中,对于有默认值的参数,通常只能从末端省略,不能省略中间或前面的参数,只传递后面的参数。如果排在前面的参数要使用与默认值相同的值,就只能采用上面例子中的第三和第四种方式,传递一个与默认值相同的值了。

但 JavsScript 有所不同:不传递参数与传递 undefined 是等同的,所以上例可以改写为(例2):

function foo(name = {first:"张",last:"三"},age = 20, phones = ['18888888888','18666666666']) {
console.log("姓名:" + name.first + name.last);
console.log("年龄:" + age);
console.log("电话:" + phones.join());
}
foo();
foo({first: "李",last: "向阳"});
foo(undefined, age = 40);
foo(undefined, undefined, ['18666666666', '18888888888', '13333333333']);

另一个特点是可以传递表达式,这个表达式是广义的表达式,可以是变量、计算式、函数定义、函数调用...

下面举一个综合了上面各种表达式的函数定义(例3):

let fn = function fn(x = , y = x + , f = function () { console.log(x); }, fs = (function (i) { return i * ;})(x)) {
console.log(x);
console.log(y);
f();
console.log(fs);
}
fn();

输出结果为:


作用域!绕不开的作用域!

在没有默认参数值的时候,JavaScript 函数的作用域问题并不突出,但在有默认参数值时,情况就很难描述的清楚了。

总体来说,参数括号部分会形成一个作用域,但这个作用域很奇特,兹举例说明:

首先,这个作用域与函数体作用域不同,见下例(例5):

let fn = function (x = y) {
var y = 4;
console.log(x);
}
fn();

这段代码会报变量 y 未定义的错误,而不会使用函数体作用域中定义的 y 。由于 var 定义的变量,会在整个作用域中存在(在定义语句前也存在),因此如果参数定义的作用域与函数体作用域是同一个作用域,它就应该使用函数体内的定义,虽然它不会拿到 4 这个值,但至少不会摄错。可参照的示例如下(例6):

let fn = function () {
let x = y;
var y = 4;
console.log(x);
}
fn();

这个例子中,x = y 时 y 是已经声明的变量,但是没有赋值,所以最后的结果不会报错,只会打印出 undefined。

其次,这个作用域也与外部作用域不同,见下例(例7)

let y = 4;
let fn = function (x = y, y = 1) {
console.log(x);
}
fn();

这段程序会报错,原因是参数字义中的 y = 1 相当于 let y = 1,而参数字义区自成作用域,因此 x = y 不会使用全局定义的 y,而是尝试使用本作用域定义的 y,但是 let 定义在 x = y 之后,形成了暂时性死区。

再次,这个作用域也不是函数体作用域的父作用域,见下例(例8)

let fn = function (x = 3, y = x) {
let x = 2;
}
fn();

这段代码会报变量 x 已定义的错误,如果参数定义区域是函数体的父作用域,那么子作用域是可以重定义父作用域的变量的。例8的反应说明 x 是定义到函数体内的作用域里的变量。

但是函数定义区却又是有自己的作用域的,见下例(例9)

let fn = function fn(x, y = () => {console.log(x)}, z = () => { x = 2; console.log(x);}) {
console.log(x); // 8
y(); // 8
var x = 6;
x++;
console.log(x); // 7
y(); // 8
z(); // 2
console.log(x); // 7
y(); // 2
console.log(x); // 7
}
fn(8);

为清晰对照,我把函数调用的结果直接以注释的形式写在了对应的行末。

第一次调用 console.log(x); 显示为 8,此时调用 y(),结果也是8,似乎没什么疑问。

但紧接着二行代码,已经将 x 改变为了 7,第二个console.log(x)的结果也证实了这一结果;但第二次调用 y(),却仍然输出 8,说明定义在函数参数区的闭包函数,仍然在使用参数定义区自己的作用域,这个作用域的 x 并没有发生变化。

调用z(),显示为 2,这也没什么疑问,此时再调用 console.log(x),仍然是 7 ,说明这个 x 是函数体作用域的 x,不受闭包函数 z() 的影响,但此时参数定义区作用域的 x 应该被改变了。再调用一次 y(),输出的是 2,证实了刚刚的猜想,即 z() 和 y() 都还在使用参数定义区的作用域。

最后一次调用 console.log(x),输出 7,这个就还是函数体作用域的 x。

给个笔者的结论吧:在定义函数时,参数定义区自成作用域,同时在参数定义区定义的变量,将进入函数体作用域,成为函数体作用域内的局部变量;这两个作用域互不干涉。

ES6 学习笔记之三 函数参数默认值的更多相关文章

  1. ES6学习 --函数参数默认值与解构赋值默认值

    1. ES6的解构ES6中引入了解构赋值的操作,其作用是:将值从数组Array或属性从对象Object提取到不同的变量中 即分为两种情况:从数组Array中解构,以及从对象Object中解构 ①.从数 ...

  2. ES6函数参数默认值作用域的模拟原理实现与个人的一些推测

    一.函数参数默认值中模糊的独立作用域 我在ES6入门学习函数拓展这一篇博客中有记录,当函数的参数使用默认值时,参数会在初始化过程中产生一个独立的作用域,初始化完成作用域会消失:如果不使用参数默认值,不 ...

  3. ES6 - 函数扩展(函数参数默认值)

    函数参数默认值 ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console.log(x, y); ...

  4. ES6学习笔记之函数(一)

    1.函数的默认参数 在ES6 之前,我们不能直接为函数的参数指定默认值,只能采用其他方法.如: function show (num, callback){ num = num || 6; callb ...

  5. Python函数参数默认值的陷阱和原理深究"

    本文将介绍使用mutable对象作为Python函数参数默认值潜在的危害,以及其实现原理和设计目的 本博客已经迁移至: http://cenalulu.github.io/ 本篇博文已经迁移,阅读全文 ...

  6. java函数参数默认值

    java函数参数默认值 今天,需要设定java函数参数的默认值,发现按照其它语言中的方法行不通 java中似乎只能通过函数的重载来实现 函数参数默认代码

  7. Python函数参数默认值的陷阱和原理深究(转)

    add by zhj: 在Python文档中清楚的说明了默认参数是怎么工作的,如下 "Default parameter values are evaluated when the func ...

  8. 【C#基础概念】函数参数默认值和指定传参和方法参数

    函数参数默认值和指定传参 最近在编写代码时发现介绍C#参数默认值不能像PL/SQL那样直接设置default,网上也没有太多详细的资料,自己琢磨并试验后整理成果如下: C#允许在函数声明部分定义默认值 ...

  9. ES6中函数参数默认值问题

    参数默认值 // 以前的参数默认值写法 let fn = (a, b) => { a = typeof a === "undefined" ? 10 : a b = type ...

随机推荐

  1. Binary Tree Traversal 二叉树的前中后序遍历

    [抄题]:二叉树前序遍历 [思维问题]: 不会递归.三要素:下定义.拆分问题(eg root-root.left).终止条件 [一句话思路]: 节点非空时往左移,否则新取一个点 再往右移. [输入量] ...

  2. sqlserver查询效率 (转)

    很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解.比如: select * from table1 where name='zhan ...

  3. php使用jquery Form ajax 提交表单,并上传文件

    在html5中我们通过FormData就可以ajax上传文件数据,不过因为兼容问题.我们选用jquery.form.min.js来进行ajax的表单提交.   一.jquery.form.js下载地址 ...

  4. Adplus 抓取Crash Dump

    本实例在win8.1 安装window kits https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit 1 ...

  5. PAT 1053 住房空置率 (20)(代码+思路)

    1053 住房空置率 (20)(20 分) 在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断.判断方法如下: 在观察期内,若存在超过一半的日子用电量低于某给定的阈值 ...

  6. DNA甲基化检测服务

    DNA甲基化检测服务 DNA甲基化是最早发现的基因表观修饰方式之一,真核生物中的甲基化仅发生于胞嘧啶,即在DNA甲基化转移酶(DNMTs)的作用下使CpG二核苷酸5'-端的胞嘧啶转变为5'-甲基胞嘧啶 ...

  7. usaco oct09 Watering Hole

    Farmer John希望把水源引入他的N (1 <= N <= 300) 个牧场,牧场的编号是1~N.他将水源引入某个牧场的方法有两个,一个是在牧场中打一口井,另一个是将这个牧场与另一个 ...

  8. part1:1-embeded学习心态

    遇到问题,要冷静分析问题,采用排除法,个个排除查找问题之所在!切记!在没分析完自己问题之前,别把问题所在指向他人!

  9. 开始Java之旅

      从今天起,cgg将给大家讲讲Java这种神奇的东西.   至于配置环境变量,大家可以看看我的博客:环境变量上面有详细解释.   下面先给大家一个公式:    public class [文件名]{ ...

  10. 2018.07.10NOIP模拟 Knapsack(单调队列优化dp)

    Knapsack 题目背景 SOURCE:NOIP2016-RZZ-4 T2 题目描述 有 n 个物品,第 i 个物品的重量为 ai . 设 f(i,j,k,l,m) 为满足以下约束的物品集合数量: ...