ES6 函数的拓展
来源:互联网 发布:linux 返回上级目录 编辑:程序博客网 时间:2024/05/06 02:50
1.函数参数的默认值
1.1 基本用法
ES5函数参数没有默认值,通常这样传默认值
function log(x,y) { if(typeof y === 'undefined') { y = 'world'; } console.log(x,y);};log('hello'); // hello worldlog('hello',false); //hello falselog('hello',''); //hello
ES6引入了函数默认值,使函数在需要使用默认值的情况下代码更加简洁。
function log(x,y = 'world') { console.log(x,y);}log('hello'); // hello worldlog('hello',false); //hello falselog('hello',''); //hello
这里需要注意两点
1)函数参数在函数体内不能用let 或者const再次声明导致重复声明变量
function foo(x = 5) { let x = 1; // error const x = 2; // error}
2)如果函数参数的传的默认值是一个表达式,表达式每次需要重新计算
let x = 99;function foo(p = x + 1) { console.log(p);}foo() // 100x = 100;foo() // 101
另一个例子
function point(x = 0, y = 0) { this.x = x; this.y = y;}const p = new point(3,4);console.log(p); //point {x: 3, y: 4}const q = new point();console.log(q); //point {x: 0, y: 0}
ES6 引入函数默认值的好处在于:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。
1.2 与解构赋值默认值结合使用
code one
function foo ({x, y = 3}) { console.log(x,y);}foo(); //Cannot destructure property `x` of 'undefined' or 'null'.foo({}); //undefined 3foo({x:1}) //1 3foo({x:4,y:5}); //4 5
同样与也可以与数组解构赋值结合使用
code two
function foo ([x,y]=[1,3]) { console.log(x,y);}foo([]); //undefined undefinedfoo([3,4]); // 3 4
code one中上只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。通过提供函数参数的默认值,如何提供函数的参数默认值?只需等于一个空对象即可。
function foo ({x, y = 3} = {}) { console.log(x,y);}foo(); //undefined 3
下面是另一个解构赋值默认值的例子。
function fetch(url,{body = '', method = 'GET', headers = {} }) { console.log(method);};fetch('http://www.baidu.com',{}); //GETfetch('http://www.baidu.com'); // Cannot destructure property `body` of 'undefined' or 'null'.
给第二个参数传入默认值,这样在给函数传值时可以省略第二个参数而不会报错。
function fetch(url,{body = '', method = 'GET', headers = {} } = {}) { console.log(method);};fetch('http://www.baidu.com'); //GET
考虑下面两种函数参数的区别
//函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;function fun1({x=0,y=0} = {}) { console.log ([x,y]);}//函数参数的默认值是一个有具体属性的对象,但没有设置对象解构赋值的默认值function fun2 ({x,y} = {x: 0, y: 0}) { console.log ([x,y]);}//函数没有传参数fun1(); //[0,0]fun2(); //[0,0]//函数传参数且x,y都有值fun1({x:1, y:3}); //[1, 3]fun2({x:1, y:3}); //[1, 3]//x 有值,y 无值的情况fun1({x:1}); //[1,0]fun2({x:1}); //[1,undefined]//函数有参数,但x,y都无值fun1({}); //[0,0]fun2({}); //[undefined, undefined]
1.2 有默认值参数的位置
引入函数参数默认值,是为了可以省略参数,所以通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
```function f1(x=1,y) { console.log([x,y]);}f1(2); //[2, undefined]function f2(y,x=1) { console.log([[x,y]]);}f2(2); //[1,2]```
所以如果设置参数默认值,应该将参数默认值写在后面,没有默认值的参数写在前面。
1.3函数的length属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
function f1(x, y, z = 3) { console.log(f1.length);};f1(); //2function f2(x=1, y=2, z = 3) { console.log(f2.length);};f2(); //0//如果函数的参数有默认值且不是尾参数,那么含有默认值的参数后面的参数不计入lengthfunction f3(x=1, y=2, z) { console.log(f3.length);};f3(); //0
1.4 作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
var x = 1;function f(x,y = x) { console.log(y);}f(2); //2
上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
let x = 1;function f(y=x) { let x = 2; console.log(y);}f(); //1f(3); //3, y值为3,默认值失效
上面代码中,函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。
这里特别说明一点:在ES6中 let x = x;无论在声明变量,还是在函数给定默认参数都会报错。
var x = x; //不报错,x为undefinedlet x = x; //报错,x is not defined
再看
let x = 3;function f(x = x) { console.log(x + 1);};f(); // ReferenceError: x is not definedf(3); //4
所以在项目中尽量避免let x = x;这种写法。
函数的参数也可以是一个默认值,同样遵守作用域规则,这种情况在实际中很少用到,这里就不作讨论,想了解更多细节请参考ES6入门。
1.5 应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
function throwIfMissing() { throw new Error('Missing parameter');}function foo(mustBeProvided = throwIfMissing()) { console.log(mustBeProvided+1);}foo(); // Missing parameterfoo(1); //2
2.rest参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {let sum = 0; for(let item of values) { sum += item ; } console.log(sum);}add(1,2,3);
上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。
注意,这里let item
不能写成let var
,ES6入门中是let var of values
会报错:Unexpected token var
。var是一个关键词,不能用作变量名。
rest 参数代替arguments变量
// arguments变量的写法function sortNumbers() { return Array.prototype.slice.call(arguments).sort();}// rest参数的写法const sortNumbers = (...numbers) => numbers.sort();
arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function f1(...items,a) { return;}f1(); //Rest parameter must be last formal parameter
函数的length属性不包括rest参数
function f1(a,...items) { return; }function f2(a=3,...items) { return;}console.log(f1.length); //1console.log(f2.length); //0
3.严格模式
从 ES5 开始,函数内部可以设定为严格模式。
function doSomething(a, b) { 'use strict'; // code}
ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
// 报错function doSomething(a, b = a) { 'use strict'; // code}// 报错const doSomething = function ({a, b}) { 'use strict'; // code};// 报错const doSomething = (...a) => { 'use strict'; // code};const obj = { // 报错 doSomething({a, b}) { 'use strict'; // code }};
两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。
1)设定全局性的严格模式
'use strict';function doSomething(a, b = a) { // code}
2)函数包在一个无参数的立即执行函数里面
const doSomething = (function () { 'use strict'; return function(value = 42) { return value; };}());
4.name属性
函数的name属性,返回该函数的函数名。
function foo() {}foo.name // "foo"
如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。
var f = function () {};// ES5f.name // ""// ES6f.name // "f"
如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。
const bar = function baz() {};// ES5bar.name // "baz"// ES6bar.name // "baz"
Function构造函数返回的函数实例,name属性的值为anonymous。
function foo() {};foo.bind({}).name // "bound foo"(function(){}).bind({}).name // "bound "
5.箭头函数
5.1 基本用法
ES6 允许使用“箭头”(=>)定义函数。一个参数可以省略圆括号
var f = v => v;<=>var f = function(v) { return v;}
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
//一个参数var f = () => 6;<=>var f = function() { return 6;}console.log(f()); //6----------//多个参数,参数用圆括号包括起来var f = (num1,num2) => num1 + num2;<=>var f = function(num1, num2) { return num1 + num2;}console.log
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var f = (num1,num2) => { let num3 = num1 + 1; let num4 = num2 + 2;; return [num3,num4];}console.log(f(1,2)); //[2,4]
如果箭头函数返回一个对象,需要在对象外面加上圆括号否则会报错。
var f = (x,y) => ({x:1,y:2});console.log(f()); //{x: 1, y: 2}
如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。
let fn = () => void doesNotReturn();
箭头函数可以与变量解构结合使用。
const full = ({first,last}) => first + ' ' + last;<=>function full(person) { return person.first + ' ' + person.last;}full({first:'harry',last:'potter'}) //harry potter
箭头函数使得表达更加简洁。
const isEven = n => n % 2 == 0;const square = n => n * n;console.log(isEven(3)); //falseconsole.log(square(2)); //4
箭头函数的一个用处是简化回调函数。
// 正常函数写法[1,2,3].map(function (x) { return x * x;});// 箭头函数写法[1,2,3].map(x => x * x);
另一个例子是
// 正常函数写法var result = values.sort(function (a, b) { return a - b;});// 箭头函数写法var result = values.sort((a, b) => a - b);
rest 参数与箭头函数结合
const numbers = (...nums) => nums;numbers(1, 2, 3, 4, 5)// [1,2,3,4,5]const headAndTail = (head, ...tail) => [head, tail];headAndTail(1, 2, 3, 4, 5)// [1,[2,3,4,5]]
5.2 使用注意点
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
this在JS中是一个比较复杂的问题,我会在另一篇文章中介绍传统函数的this和箭头函数this的区别。
- ES6 函数的拓展
- ES6学习4(函数的拓展)
- ES6之函数的拓展(部分)
- ES6基础教程(5)-函数的拓展
- ES6学习5(对象的拓展)
- es6---解构赋值与字符串的拓展
- ES6之字符串的拓展(部分)
- ES6之数组的拓展(部分)
- 函数的参数拓展
- ES6之字符串拓展
- es6的Generator函数
- es6函数的扩展
- es6的箭头函数
- ES6-函数的扩展
- ES6的"箭头"函数
- ES6--函数的扩展
- ES6学习3(各种类型的拓展)
- ES6---es6中函数function的改革
- Android之ZXing扫描二维码以及生成二维码
- oozie配置文件最全详解
- MyBatis Generator中文文档
- JS设计模式-单例模式
- Java中的除法结果与除数被除数的类型有关
- ES6 函数的拓展
- 祝大家双旦快乐——点开有彩蛋哦
- 【python】【穆晨】【171224】连续第七十五天总结
- LeetCode Remove Duplicates from Sorted Array
- Python3.6 在win10下安装xlwings
- 反射之字段、方法、构造器的调用(二)
- 目前使用过的不经常使用的函数
- Redis 主从配置心得及其高可用方案
- git 初学使用