ES6 —— 函数的扩展

一、 函数的扩展
1. 函数参数的默认值
① 基本用法
  1. ES6允许为函数的参数设置默认值,即直接写在参数定义的后面
 function f(a=,b=5) {
            console.log(a,b)
        }
        f(1,2)//1,2
        f(1)//1,5
 function f(a=0,b=5) {
            this.a = a;
            this.b = b;
        }
        const p = new f();
        console.log(p);//f{a:0,b:5}
  1. 在函数中不能用let和const定义与参数同名的变量,否则会报错
  2. 在使用参数默认值时,函数不能有同名参数
function f(a,b,a) {
           console.log(a,b,a)
        }
        f(1,2,3)//3,2,3
        function f(a,b,a=1) {
           console.log(a,b,a)
        }
        f(1,2,3)//Duplicate parameter name not allowed in this context
  1. 参数默认值是惰性求值的,如果参数的默认值是一个式子,则在每次调用都重新求值。
//该例子中,每次调用foo函数时都重新求p值,而不是p默认为100
let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101
② 与解构赋值默认值结合使用

函数默认值可以与解构赋值结合使用。但是要注意,形参和实参的模式要相同,否则会错误。为了避免这样的错误,可以给函数的对象参数赋一个空对象。(该情况的具体描述请看上一篇文章)

function foo({x=2, y = 5}) {
  console.log(x, y);
}

foo({}) // 2 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
//5的解决
function foo({x=2, y = 5} = {}) {
  console.log(x, y);
}
foo();//2 5

function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  console.log(method);
}

fetch('http://example.com')// "GET"
③ 参数默认值的位置

通常情况下,定义了默认值的参数应该是函数的尾参数。在传实参时,,前不能无值。

  function f(x=1,y) {
            return [x,y]
        }
        f();//[1,undefined]
        f(,2)//报错
        f(undefined,2)//[1,2]
        function f(x=1,y,z) {
            return [x,y]
        }
        f(1,,2)//报错
        f(1,undefined,2)//1,undefined,2
④ 函数的length值

在指定了默认值后,函数的length属性将返回没有指定默认值的参数个数,即在指定了默认值后,length属性将失真。注意:在这里如果设置了默认值的参数不是尾参数,length属性也不再计入后面的参数了

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
console.log((function (a, b = 0, c) {
        }).length);//1,只计a,不计c
⑤ 函数的作用域

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

var x=1;
function f(x,y=x){
	console.log(y)
}
f(2)//2
var x=1;
function(y=x){
//x未定义,故只能用全局变量,若全局变量x不存在则会报错
	console.log(y);
}
f();//1
//在该例中,赋值语句右边的x仍然为左边的x(暂时性死区的原因)
var x=1;
function f(x=x){}
f()//x is not defined
 var x=1;
        function foo(x,y=function () {x=2;}){
            y();
            console.log(x);
        }
        console.log(x);//1
        foo();//2(x与y的匿名函数中定义x指向同一个作用域,故当y的匿名函数中的x改变时,形参x也改变,但是与全局变量x指的不是同一个作用域,故全局变量x不变)
var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;//该x与形参x指向同一个作用域
  y();
  console.log(x);
}

foo() // 2
x // 1
2. rest参数(扩展运算符)

ES6引入rest参数(形式为…变量名),用于获取函数的多余参数。rest参数之后不能再有其他参数,否则会报错。函数的length属性不包括rest参数

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10
3. 严格模式:use strict

1.从 ES5 开始,函数内部可以设定为严格模式。
2. ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。这样规定的原因是,函数内部的严格模式,同时适用于函数体和函数参数。但是,函数执行的时候,先执行函数参数,然后再执行函数体。这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。
3. 两种方法可规避这种限制 ① 设置全局性的严格模式 ② 把函数包在一个无参数的立即执行函数里(具体看文档)

4. 函数的name属性:返回该函数的函数名

如果将一个匿名函数赋值给一个变量,ES5该变量的name属性会返回空字符串,ES6会返回实际的函数名。
如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"
const bar = function baz() {};

// ES5
bar.name // "baz"

// ES6
bar.name // "baz"

5. 箭头函数
  1. 如果箭头函数不需要参数或需要多个参数,使用一个圆括号代表参数部分。
  2. 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
  3. 如果箭头函数返回一个对象,必须在对象外面加上括号,否则会报错(大括号被解释为代码块)
 let fn = () => ({a:1,b:4});
        console.log(fn().b);//4
  1. 箭头函数可以与变量解构结合使用。
const full = ({first,last})=>first+' '+last;
//相当于
function full(person) {
	return person.first+' '+person.last
}
使用箭头函数的几个注意点:

① 函数体内的this对象时定义时所在的对象而不是使用时所在对象。即在箭头函数中的this是固定的
② 箭头函数不可以当做构造函数,即不可以使用new命令,否则会抛出错误。
③ 不可以使用arguments对象,该对象在函数体内不存在,如果要用可以通过rest参数代替。
④ 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。
⑥ 由于箭头函数没有自己的this。故不能用call()apply()bind()方法去改变this的指向。(bind方法只是绑定对象,并不会执行。)

function foo() {
//1
  return () => {
    //console.log(this);2
    return () => {
       //console.log(this);3
      return () => {
       //console.log(this);4
        console.log('id:', this.id);
      };
    };
  };
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
  function foo() {
  			//1
            return () => {
                //console.log(this);2
                return function () {
                    //console.log(this);3
                    return () => {
                        //console.log(this);4
                        console.log('id:', this.id);
                    };
                }
            };
        }

        var f = foo.call({id: 1});//第一个this为{id:1},第二个this为{id:1},第三个this为window,第四个this为window

        var t1 = f.call({id: 2})()(); // id: undefined  第一个this为{id:1},第二个this为{id:1},第三个this为window,第四个this为window
        var t2 = f().call({id: 3})(); // id: 3第一个this为{id:1},第二个this为{id:1},第三个this为{id:3},第四个this为{id:3}
        var t3 = f()().call({id: 4}); // id: undefined
箭头函数不适用的场合

① 定义对象方法时,有时需要在方法内调用该对象相关方法、属性,此时的this需要指向对象,而不是定义时的对象。此时不能用箭头函数
② 当需要动态this时不能使用箭头函数。

嵌套使用的箭头函数
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
  array.splice(array.indexOf(afterValue) + 1, 0, value);//在元素1后添加一个value元素(先找到元素1的索引后+1,再插入)
  return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
5. 尾调用(具体尾调用和尾递归参考文档)
  1. 尾调用(Tail Call)是函数式编程的一个重要概念。 其含义是在一个函数的最后一步调用另一个函数。尾调用不一定出现在函数的尾部,只要是最后一步操作即可。
  2. 在可用循环时用循环取代递归,由于纯粹的函数式编程语言中没有循环,故不可用循环取代递归,此时尾递归/尾调用就显得尤为重要(可节省内存)
  3. “尾调用优化”(Tail call optimization):只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
  4. 只要严格模式下才会开启尾调用优化,正常模式下时无效的(因为正常模式下由于函数的调用栈会改写,故函数内部的两个可以跟踪函数调用的变量arguments和caller会失真,严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效
function f(x){
	return g(x)
}
//错误
function f(x){
	let y = g(x);//函数调用后返回y
	return y;
}
function f(x){
	return g(x)+1;//函数调用后又进行了运算
}
function f(x){
	g(x);//实际上与g(x);return undefined等价,故在调用后依旧return undefined
}
6. 函数参数的逗号

之前函数定义和调用时都不允许最后一个参数后面出现逗号。但是对于版本管理系统来说就会显示添加逗号那行也发生改变,故ES6允许定义和调用时,最后一个参数后面有一个逗号。

7. 函数对象.toString()

之前的函数对象.toString()方法只会返回代码本身,而ES2019要求返回一模一样的原始代码(即连注释也要返回)。

8. catch命令的参数省略

之前try…catch结构明确要求catch命令后面必须跟参数,该参数为try代码块抛出的错误对象。但有时用不到该参数,故ES2019允许catch语句省略参数。

发布了72 篇原创文章 · 获赞 72 · 访问量 6321

猜你喜欢

转载自blog.csdn.net/weixin_43314846/article/details/102705403