ES6: 参数默认值及中间域
下午看了一章 ECMA-262 by Dmitry Soshnikov, 现在稍稍来小结下ES6中的参数默认值以及由此产生的参数中间作用域。
原文地址: http://dmitrysoshnikov.com/ecmascript/es6-notes-default-values-of-parameters/#conditional-intermediate-scope-for-parameters
ES6中的参数默认值用法和其他语言都差不多,直接在参数后赋值:
1 function log(message, level = 'warning') { 2 console.log(level, ': ', message); 3 } 4 5 log('low memory'); // warning: low memory 6 log('out of memory', 'error'); // error: out of memory
不过和Python有一点不太相同的是,Python的默认值是在函数定义的时候计算的,然后作为函数的一个__defaults__参数保存下来。在函数执行时,对指向这个默认值的参数的操作也会导致默认值发生改变,
从而产生下面的问题:
1 def foo(x = []): 2 x.append(1) 3 return x 4 5 # We can see that defaults are created once, when 6 # the function is defined, and are stored as 7 # just a property of the function 8 print(foo.__defaults__) # ([],) 9 10 foo() # [1] 11 foo() # [1, 1] 12 foo() # [1, 1, 1] 13 14 # The reason for this as we said: 15 print(foo.__defaults__) # ([1, 1, 1],)
解决方法是使用特殊常量None作为默认值,在函数内再判断并初始化为真正的默认值。
1 def foo(x = None): 2 if x is None: 3 x = [] 4 x.append(1) 5 print(x) 6 7 print(foo.__defaults__) # (None,) 8 9 foo() # [1] 10 foo() # [1] 11 foo() # [1] 12 13 print(foo.__defaults__) # ([None],)
ES6的实现是在函数每次调用是都执行一次默认值的计算,保证默认值不会在之前的执行过程中被更改。
参数的TDZ(Temporal Dead Zone)
ES6中提到的TDZ,指的是程序中,变量或者参数不能被访问直到初始化完成的区域。
因此在下面一段中,参数的默认值不能设置为参数本身。
1 var x = 1; 2 3 function foo(x = x) { // throws! 4 ... 5 }
=x 中的默认值x,是在参数作用域中解析的,而不是全局的作用域。所以此时x是在TDZ中,因此不能访问,从而不能赋值给x本身作为默认值。
1 function foo(x, y = x) { // OK 2 ... 3 }
这种写法是可以的,因为在赋值给y之前,x已经被初始化为undefined了。
有条件的参数中间作用域
当至少有一个变量有默认值时,ES6定义了一个中间作用域来储存这些参数变量,同时这个作用域是不和函数主体的作用域共享的,这个是和ES5的一个主要的区别。
1 var x = 1; 2 3 function foo(x, y = function() { x = 2; }) { 4 var x = 3; 5 y(); // is `x` shared? 6 console.log(x); // no, still 3, not 2 7 } 8 9 foo(); 10 11 // and the outer `x` is also left untouched 12 console.log(x); // 1
在上面这种情况下,这里共有三个作用域:全局作用域,参数作用域,以及函数本体的作用域
1 : {x: 3} // inner 2 -> {x: undefined, y: function() { x = 2; }} // params 3 -> {x: 1} // global
此时函数y中,x的值是在自身的作用域,即参数作用域中解析的。
如果Transpiling(从一种语言编译到另一种相同抽象层次的语言)成ES5的话,这三层作用域就看的更清楚了:
1 // ES6 2 function foo(x, y = function() { x = 2; }) { 3 var x = 3; 4 y(); // is `x` shared? 5 console.log(x); // no, still 3, not 2 6 } 7 8 // Compiled to ES5 9 function foo(x, y) { 10 // Setup defaults. 11 if (typeof y == 'undefined') { 12 y = function() { x = 2; }; // now clearly see that it updates `x` from params 13 } 14 15 return function() { 16 var x = 3; // now clearly see that this `x` is from inner scope 17 y(); 18 console.log(x); 19 }.apply(this, arguments); 20 }
需要定义参数作用域的原因在于,函数类型的默认值,无论放在外部的作用域或者函数内部的作用域上执行,都会产生问题。
先看看如果是放在函数内部的作用域上执行的情况,
1 var x = 1; 2 3 function foo(y = function() { return x; }) { // capture `x` 4 var x = 2; 5 return y(); 6 } 7 8 foo(); // correctly 1, not 2
如果 function() { return x; } 放在函数内部的作用域执行,那么其中的x捕获的就会是函数内部的变量x(因为在VO的静态解析过程中,内部作用域上的x已经被定义了),但显然从理解上来说,函数中的x应该对应的是外部的x而非内部的(除非参数中还定义的一个同名的变量,然后覆盖了外部的同名变量)。由此看出,不将参数作用域和函数内部作用域共享的主要原因是:函数内部作用域中的同名变量不应该影响到参数闭包中所绑定的同名变量的值。
那如果放在外部的作用域上呢,
1 var x = 1; 2 3 function foo(y, z = function() { return x + y; }) { // can see `x` and `y` 4 var x = 3; 5 return z(); 6 } 7 8 foo(1); // 2, not 4
这是问题就会变成,function() { return x + y; }这个函数由于是在外部作用域上的,所以没有办法去访问到内部函数的参数了,即y的值访问不到,因为y不是定义在外部作用域上的。
那何谓“有条件的”中间作用域,就是当函数没有定义参数默认值的时候,是不会创建这个参数的中间作用域的,此时,参数绑定是和函数的内部作用域共享的,和在ES5的模式下的方式一致。
这种实现方式算是对ES5的下手动实现参数默认值的方式的一种兼容,使得能够在函数内部访问并修改函数参数中的变量,将他们放在相同的作用域中。
基本的内容就是这些了,这一部分的内容也并不多,但还是能看出不少语言设计时的构想以及对旧版本的兼容的考虑。自己写着写着算是把核心的内容都翻译了一遍,不过有些细节上的问题,也是非反复斟酌不能够理清其中的头绪的。
之后计划接着看 Lexical environments的部分,
http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-1-lexical-environments-common-theory/#environment-frames-model
之前大概看了遍但不足以深入的理解,有不少名称的定义等感觉比较绕,看来,语义的这桩事情还是要靠语义才搞的定。
So far.
转载于:https://www.cnblogs.com/emiletobias/p/4769508.html
ES6: 参数默认值及中间域相关推荐
- ES6新特性之函数优化-参数默认值
函数优化 在ES6中,对函数的操作做了优化,使得我们在操作函数时更加的便捷. 函数参数默认值 在ES6以前,我们无法给一个函数参数设置默认值,只能采用变通写法: function add(a , b) ...
- ES6——函数参数默认值
ES6 之前,不能直接为函数的参数指定默认值,ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面. function fun(x = 'Hello'){console.log(x); } f ...
- ES6:Rest 参数和参数默认值【转】
Rest 参数 通常,我们需要创建一个可变参数的函数,可变参数是指函数可以接受任意数量的参数.例如,String.prototype.concat 可以接受任何数量的字符串作为参数.使用 Rest 参 ...
- ES6函数参数的解构赋值,以及2种设置参数默认值方法的区别
文章目录 1. 函数参数的解构赋值方式 2. 函数参数解构设置参数默认值 1. 方法1 2. 方法2 3. 总结 1. 函数参数的解构赋值方式 下面代码中,函数add的参数表面上是一个数组,但在传入参 ...
- ES5-5 参数默认值、递归、预编译、暗示全局变量
1. 参数默认值 默认是undefined 形参可以有默认值,形参.实参哪个有值取哪个ES6,默认值属于ES6的内容,打印出的是符合人性化的结果 形参有默认值,形参.实参无法统一.无论实参传入有值还是 ...
- ES6-ES11-第一部分-let、const、解构赋值、模板字符串、简化对象写法、箭头函数、函数参数默认值、rest 参数、扩展运算符、Symbol、迭代器、生成器、Promise、Set、Map
根据视频进行整理 [https://www.bilibili.com/video/BV1uK411H7on?p=1] 视频资源(百度网盘): 链接:[https://pan.baidu.com/s/1 ...
- C++ 笔记(13)— 函数(函数声明、函数定义、函数调用[传值、指针、引用]、函数参数默认值、函数重载)
每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数. 1. 函数声明 函数声明告诉编译器函数的名称.返回类型和参数.函数声明包括以下几个部分: ret ...
- JavaScript中实现函数重载和参数默认值
2019独角兽企业重金招聘Python工程师标准>>> 参数默认值是指在调用函数时,若省略了某个实参,函数会自动为该参数分配一个默认值,使得函数调用的方便性和灵活性大大提高. 举个例 ...
- 【Shell】设置变量默认值,参数默认值, 自动赋值
设置变量默认值,参数默认值, 自动赋值 转自:https://zhuanlan.zhihu.com/p/98636736 默认参数(变量默认值) if 繁琐方式 if [ ! $1 ]; then$1 ...
最新文章
- Java并发设计模式--不可变模式(immutable)
- HTTP Header 详解,互联网营销
- 设置IP安全策略将***阻杀在端口外
- Access转Sql Server问题
- Python中zip()函数的解释和可视化
- 离线安装宝塔lnmp_宝塔LNMP环境 Nginx安装EduSoho教程说明
- 2016蓝桥杯省赛---java---B---1(有奖猜谜)
- netbeans下开发rails快捷键 及 Ruby On Rails开发技巧总结
- chrome插件:提取页面数据
- markdown字体颜色_Markdown转微信公众号格式
- 通俗易懂的图解机器学习之机器学习概论
- Excel2003进制转换方法
- html中如何调整图片的对比色,风光照片如何调出冷暖对比色?后期案例分享
- 数字图像隐写术之卡方分布
- Win10系统Edge可以上网其他浏览器不能上网怎么回事
- 定义一个电话簿,用人名查电话
- python中的row函数均方_均方位移python(Mean square displacement python)
- 基于微信小程序二手跳蚤市场系统设计与实现毕业设计论文
- dellT440和T620重装系统问题总结
- Ubuntu搜狗输入法乱码问题
热门文章
- 在EditPlus里直接运行PHP
- [翻译]RoboChamps城市挑战赛
- ASP.NET 数据访问类
- [JavaScript]自定义MessageBox
- [Java] 蓝桥杯ALGO-27 算法训练 FBI树
- 蓝桥杯 ADV-82 算法提高 填充蛋糕
- 非广告--推荐Dynatrace:树立数字化性能管理DPM标杆
- myeclipse 实现框架 spring+springmvc+springsecurity+myibatis+mysql用户认证和人员增删改查
- 君信财富获数千万元战略投资,投资方为银江集团
- python-requests数据驱动延伸