es6中函数方面的扩展(一)

es6新增了箭头函数,函数默认参数等功能,方便了代码的间接性,可读性

  • 函数的参数默认值

    es6以前要想实现函数参数的默认值,是需要做一些变通的

	function foo (x, y) {
		
		x = x || 'hello';
		y = y || 'es6';
		console.log(x, y) 
		
	}
	foo();
	// hello es6
	foo('hello');
	// hello es6
	foo('hello', 'fn');
	// hello fn
	foo('hello', '');
	// hello es6
	foo(undefined, undefined);
	// hello es6
	foo(null, null);
	// hello es6

上面代码有一个弊端,就是y明明赋了值,但是并没有替换掉默认值,虽然传入的是(‘’ , undefined, null)

可以判断一下

	typeof y === 'undefined' ? y = 'es6' : y = y;

这样就避免这些(’ ', undefined , null)返回默认值了

想想一下如果多个变量都需要这样写默认值,是很繁琐的,此时es6引入了新特性,参数默认值

es6允许函数参数设置默认值,即写在形参后面用等号赋值。

	function bar (x, y = 'es6') {
		console.log(x, y);
	}
	
	bar('hello');
	// hello es6
	bar('hello', '');
	// hello
	bar('hello', 'ECMA2015');
	// hello ECMA2015
	bar('hello', undefined);
	// hello es6
	bar('hello', null);
	// hello null

参数默认值这种写法,运用了等号右边必须为(===)undefined默认值才会生效。

上面有一个null,如我们自己封装的方法不一样
因为null === undefined 返回false。所以null会被返回。

可以看到这样写非常简洁

	function Test (x = 0, y = 0) {
		this.x = x
		this.y = y
	}
	let test = new Test();
	test
	// {
		x: 0,
		y: 0
	}	
		

构造函数也可以使用默认值。
并且这种写法,当外部删除实参,内部函数也不会因为实参的丢失,而报错。大大减少了维护的成本。

使用参数默认值,就不能使用let const声明

	function foo (x = 0) {
		let x = 1;
		const x = 2;
		
	}

以上都会报错,因为参数默认已经声明了,不可以使用let, const再次声明。

使用参数默认值时,函数不能有同名属性

	// 正常执行
	function foo (x, x, y) {

	}
	
	// 报错
	function foo (x, x, y = 1) {
	
	}
	// SyntaxError: Duplicate parameter name not allowed in this context
	// 改函数的上下文中不允许重复的参数名称

另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

	p = 99
	function test (p = x + 1) {
		return p;	
	}
	test();
	// 100

	p = 100;
	test();
	// 101
	

上面代码中,每次调用test,x + 1都重新执行,而不是默认p = 100。

解构赋值和参数默认值结合使用的好处

	function test ({x, y = 5}) {
		console.log(x, y)
	}
	
	test({}); 
	// undefined 5
	test({x: 1,y: 2});
	// {x: 1, y: 2}
	test({x: 1});
	// {x: 1, y: 5}
	test();
	// error

只有函数执行对象才能正常,执行因为你没有设置默认值,必须进行传值,
上面代码前三个都不是问题,最后一个为什么报错了。
因为你传入的是空,而参数({}x, y = 5})默认没有值。所以x会报错

通过给参数设置默认值,就可以解决了

	function bar ({x, y = 1} = {}) {
		console.log(x, y)
	}
	bar();

此时你就算不穿,他的默认值也是一个对象,x只能返回undefined ,不会导致程序报错

下面有两种写法

	function foo ({x = 0, x = 0} = {}) {
		return [x, y]
	}

	function bar ({x, y} = {x:0, y: 0}) {
		return [x, y]
	}
	
	
	foo(); // [0, 0]
	bar(); // [0, 0]
	
	foo({}); // [0, 0]
	bar({}); // [undefined, undefined]

	foo({x:1}) // [1, 0]
	bar({x:1}) // [1, undefined]
	
	foo({x: 5, y: 2}) // [5, 2]
	bar({x: 5, y: 2}) // [5, 2]
	
	foo({z: 1}) // [0, 0]
	bar({z: 1}) // [undefined, undefined]

参数默认值的位置

一般来说形参的末尾的值,是设置默认值最好的位置。这样方便别人阅读代码。
如果默认值的形参非末尾,那么不可以省略。否则报错

	function test (x, y = 1, z) {
		return [x, y, z]
	}
	test(1, 2, 3) // [1, 2, 3]
	test(undefined, 2, undefined) // [undefined, 2, undefined]
	test(1, undefined, 3) // [1, undefined, 3]
	test(2, ,5) // error

上面代码演示了,非末尾参数,不可以省略,但可以使用undefined占位,这样默认值即生效。

因默认值采用了(===)undefined,所以传入undefined会使用默认值,但是null不可以

	function test (x = 1, y = 2) {
		return [x, y]
	}
	test(undefined, null)
	// [1, null] 

函数的length属性

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

	(function (a) {}).length // 1
	(function (a = 5) {}).length // 0
	(function (a, b. c = 520)).length // 2 

如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

	(function (a = 1, b, c) {}).length // 0
	(function (a, b = 1, c) {}).length // 1

作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

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

	test(2);
	// 2

上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。

再看一个例子

	let x = 1;
	function test (y = x) {
		let x = 2;
		console.log(y);
	}
	test()
	// 1

当函数调用时,参数形成单独的作用域,但是这个作用域中,x没有定义,所以获取外部的变量x,函数调用时,函数体内部的x变量不会影响到默认值变量x

如果此时x不存在就会报错

	function test (y = x) {
		let x = 1;
		console.log(y);
	}
	test();

test函数执行时,形成单独作用域的变量中没有x,此时去找外部的,但是全局中也没有变量x, 此时的x就是undefined,但是函数体内部的变量x又影响不到默认值变量x,当打印y的时候就会报错 x is not defined

还有一种写法

	let x = 1;
	function test (x = x) {

	}
	test();

此时也会报错,因为单独作用域中,x = x 就等于let x = x; 此时属于暂时死亡区, 导致报错,因为x还没声明,你就赋值了。

参数的默认值是函数,也会遵循这种原则

	let foo = 'bar';
	function test (show = () => foo) {
		let foo = 'foo';
		show();
	}
	test();
	// 'bar'

test执行时,参数即使是函数,也要遵循原则行程单独作用域,它返回的就是外部‘bar’, 内部foo影响不了默认值。

因此foo指向外部foo,输出’bar’

如果写成下面这样,会报错

	function bar (func = () => foo) {
		let foo = 'sex';
		func();
	}
	
	bar();

上面的func指向的函数里没有foo这个变量,foo这个变量依赖外部,外部没有,所以报错。

下面是一个复杂的例子

	var x = 1;
	function foo (x, y = () => { x = 2; }) {
		var x = 3;
		y();
		console.log(x);
	}
	foo()
	// 3
	x
	// 1

首先foo执行时,参数形成单独作用域,x使用外部x变量,y使用同作用域第一个x值,y内部更改了同作用域里x的值。

紧接着函数内部声明x并且打印,但是影响不到外部,也影响不到内部参数单独作用域

打印的x是内部声明x
x依旧没有改变,因为y改变的只是同作用域的x,而内部声明的x = 3,根本不在一个作用域。

如果将var去掉,它将指向第一个参数的x,输出就是2,但是依旧外部的x不会被改变。

上述不可以使用let ,否则会声明在后,使用在前

应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出错误。

	function throwIfMissing () {
		throw new Error('不可能省略的参数')
	}
	
	function test (foo = throwIfMissing()) {
		console.log('传入了参数')
		return foo;
	}
	
	test();

上面代码,如果传入参数就不会报错,如果没有传入就会报错

注意throwIfMissing方法是运行时执行,这表明是在运行时执行,而不是定义时执行,如果参数已经赋值,那么默认值的函数就不会被执行。

另外还可以设置undefined,让这个参数可以不传参

	function test (option = undefined) {...}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值