美文网首页
ES6学习之- 函数的扩展

ES6学习之- 函数的扩展

作者: 尤小小 | 来源:发表于2017-09-21 01:07 被阅读16次

Part2 函数的扩展

2.1 函数参数默认值,可以传任意类型

函数名 = ([param1, ..., params=默认值]) => { }
// 等同于
function 函数名([param1, ..., params=默认值]) = { }

2.1.1 es5采用变通方法实现函数参数默认值,为什么要给函数扩展参数默认值?

function foo(a, b) {
  b = b || 5;
  console.log(a + b);
 }

foo(1); // 6
foo(1, 2); // 3
foo(1, ''); // 6
foo(1, 0); // 6

上面代码检查函数doSomething的参数b有没有赋值,如果没有,则指定默认值为5。这种写法的缺点在于,如果参数b赋值了,但是对应的布尔值为false (0、-0、null、""、false、undefined 或者 NaN),则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。 当然es5也可以去处理这几种特殊情况,不过es6给出更好的解决办法:给函数的参数扩展了默认值写法。

2.1.2 es6函数参数默认值可以传任意类型

es6 允许函数的参数设置为默认值,既直接写在参数定义的后面,先判断一下参数b是否被赋值,如果没有,再等于默认值,es6 允许函数的默认值可以传任意类型。

// 参数默认值为数字
function foo (a, b=5) {
    console.log(a + b);
}
foo(1); // 6


// 参数默认值为字符串
function foo (a, b='World') {
    console.log(a + b);
}
foo('Hello'); // "Hello World"


// 参数默认值为对象
function doSomething (a, b={x: 1, y: 2}) {
    console.log(a + b.x + b.y);
}
foo(1, {x: null, y: 2}); // 3


// 默认值为解构赋值 (不建议)
function foo ({x, y = 2}) {
    console.log(x, y);
}
foo({}) // undefined 2
foo({x: 1}) // 1 2
foo({x: 1, y: 3}) // 1 3
foo() // TypeError: Cannot read property 'x' of undefined

// 默认值为解构赋值:参数是一个对象 (推荐)
function foo ({x, y = 2} = { }) {
    console.log(x, y);
}
foo() // undefined 2

函数参数默认值可以使用传任意类型,以上展示了数值、字符串、对象、解构赋值的写法,这种写法优势是:便于阅读,利于优化。

2.1.3 参数变量是默认声明时,不能用let或const再次声明。

(function foo (a) {
    var a = 1;
    console.log(a);
})()
// 1

( function foo (b) {
    let b = 2;
    console.log(b);
})()
// 报错:Identifier 'b' has already been declared

( function foo (c) {
    const c = 3;
    console.log(c);
})()
// 报错:Identifier 'c' has already been declared

以上例子,可以看出,在函数体中,不能用let或者const再次声明,否则会报错。

(4) 使用参数默认值时,函数不能有同名参数。

// 不报错
function foo(x, x, y, y) {
  // ...
}

// 报错
function foo(x, y, y = 1) {
  // ...
}

// 报错
function foo(x, x, y = 1) {
  // ...
}

上面代码中,跟默认值参数名同名的时候报错,如果我跟默认值参数名不重名,跟其他的非默认值参数重名可以么?也是不可以的,只要使用了参数默认值,就不能有同名参数。

(5) 参数默认值的位置--尾参数

function foo(x, y = 2, z) {
  return [x, y, z];
}

foo(1, ,2); // 报错
foo(1, undefined, 2); // [1, 2, 2]
foo(1, null, 2); // [1, null, 2]

如果默认值得参数不是尾参数,这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。传入undefined的,将触发该参数等于默认值,null则没有这个效果。为了避免这个问题,建议带参数默认值的函数,默认值参数设为尾参数。

正确写法:

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

2.2 rest参数,必须只能是最后一个参数

rest参数说白了就是扩展运算符参数,形式为...x,代表实参为数组的传进来的x变量,用于获取函数的多余参数。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。由于rest参数是一个真正的数组,数组持有的方法都可以使用。

function add (...items) {
  let s = 0;

  items.forEach(function(item) {
    s += item;
  });

  return s;
}

add(1,2,3); // 6

在es5中arguments可以用来获取函数参数集合的类数组对象。由于arguments对象不是真正的数组,要想使用真正的数组方法,必须使用 Array.prototype.slice.call(arguments)转为数组。

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}
sortNumbers(3, 1,  0, 2); // [0, 1, 2, 3]

// rest参数的写法
sortNumbers = (...numbers) => numbers.sort();
sortNumbers(3, 1,  0, 2); // [0, 1, 2, 3]

显而易见,rest 参数的写法更自然也更简洁。对于rest参数,就可以替代arguments类数组,不需要 arguments对象了。

注意:rest 参数只能是最后一个参数,否则会报错

// 报错:Rest parameter must be last formal parameter
function foo(a, ...b, c) {
  // ...
}

// 报错:Rest parameter must be last formal parameter
function foo(a, ...b, ...c) {
  // ...
}

function foo(a, b, ...c) {
  // ...
}

第一种情况,rest 参数没有放在最后的位置;第二种情况,虽然参数c是最后一个参数, 但是b却不是,就说明了rest参数只能有一个扩展运算符;第三种情况满足条件。

注意:一个函数只能有一个rest参数,并且rest参数只能是最后一个参数。

2.3 严格模式

从es5开始,函数内部可以设定为严格模式。

function foo(a, b) {
  'use strict';
  // code
}

es6 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

// 报错
function foo(a, b = a) {
  'use strict';
  // to do something
}

// 报错
const foo = function ({a, b}) {
  'use strict';
  // to do something
};

// 报错
const foo = (...a) => {
  'use strict';
  // to do something
};

两种方法可以规避这种限制。
第一种是设定全局性的严格模式,这是合法的。

// 第一种是设定全局性的严格模式
'use strict';

function foo (a, b = a) {
  // code
}

// 第二种是把函数包在一个无参数的立即执行函数里面
foo = (function () {
  'use strict';
  return function (value = 42) {
    return value;
  };
}());

2.4 箭头函数 Arrow functions

es6 中对函数的扩展多出了一个新东西“箭头函数”,可以说箭头函数是es6里使用频率最高的一个语法糖,箭头函数相当于匿名函数,并且简化了函数定义,es6 允许使用箭头 () => {} 定义函数。Arrow functions

命名箭头函数:

functionName =  ([param1, ...])  => {
    todoSomething
}

匿名箭头函数:

([param1, ...])  => {
    todoSomething
}

一条语句,不同的参数的写法如下:

// 不传参数的箭头函数,使用一个圆括号代表参数部分
{
    let foo = () => 5;
    foo (); // 5
}

// 只传一个参数的箭头函数,参数部分的圆括号可以省略
{
  let foo = a => a;
  foo(5); // 5
}

// 传多个参数的箭头函数,使用圆括号代表参数部分
{

  let foo = (a, b) => a + b;
  foo(5, 10); // 15,
}

箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }return。下面展示几个例子:

// 包含多条语句的箭头函数
{
  let foo = (a, b) => {
    console.log(a); // 5
    console.log(b); // 10
    console.log(this); // Window
    return a + b;
  };
  foo(5, 10); // 15
}

// 带有默认值参数的箭头函数
foo = (a, b=5)  => a + b;
foo(1); // 6

// rest参数的箭头函数
// 箭头函数不可以使用arguments对象,若想使用,可以用 rest 参数代替
{
    let add = (...items) => {
        let s = 0;
        items.map( item =>  {
            s += item;
        });

        return s;
    }
    
    add(1,2,3); // 6
}

箭头函数很适合于无复杂逻辑或者无副作用的纯函数场景下,比如map, filter等回调函数定义中。

2.5 箭头函数中的this

2.5.1 普通函数中的this

  • 如果调用函数属于某个对象,那么函数内的this指向该对象
  • 在普通函数中,this指向它的直接调用者;如果找不到直接调用者, 则是函数独立调用,this则指向window
  • 在严格模式下,没有直接调用者的函数中的this是undefined
  • 如果是构造函数,那么函数内的this则指向实例化对象
  • 使用call, apply,bind绑定的,可以改变this的指向

2.5.1 箭头函数中的this指向

箭头函数里面的this, 跟es5里普通函数里的this不一样的,箭头函数中的this始终指向函数定义时的作用域的this。实际原因是箭头函数根本没有自己的this,导致内部this就是外层代码块的this。

// 箭头函数中的this,在事件监听中的应用

<button id="button">按钮</button>

<script>
    (function (d, log) {
        let obj = {
            a: 5,
            clickHandle() {
                log('这是事件监听内部')
                log(this) // this指向obj这个对象
            },
            init() {
                let btn = d.querySelector('#button');
                let _this = this;

                btn.addEventListener('click', (e) => {
                    log(this); // this指向obj这个对象
                    log(e.target); // 获取被点击的按钮
                    e.target.innerHTML = '点过';  //去操作按钮自身的属性
                    this.clickHandle(); // 调用外层对象的方法
                });
            }
        };
        obj.init();
    })(document, window.console.log)
</script>

不要在最外层定义箭头函数,因为在函数内部操作this会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将this控制在可见的范围内。

2.5.2 不要滥用箭头函数

// 普通的构造函数
function Person( name){
    console.log(this);
    this.name =name;
}

var p1=new Person('小王'); // Person {name: "小王"}
var p1=new Person('小李'); // Person {name: "小李"}


// 箭头函数不能用作构造函数
let Person = ( name) => {
    console.log(this);
    this.name =name;
}

let p1=new Person('小王'); // 报错:Person is not a constructor
let p1=new Person('小李'); // 报错:Person is not a constructor

Person构造函数的this,指向每次实例化的对象,若是用箭头函数改写构造函数,就会改变this的指向外层的this对象。所以箭头函数不能用作构造函数。除此之外箭头函数还有一些不能使用的情况,大家可以参考这篇文章:王仕军写的 什么时候你不能使用箭头函数?

箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。

箭头函数有几个使用注意点。

(1)箭头函数体内的this对象,就是`定义`时所在的对象,而不是`调用`时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会报错。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

以上就是函数扩展的主要内容,其中尤为重要的是默认值参数和箭头函数。

小结

  • 函数参数默认值:

    • 可以传任意类型
    • 不能用let或const再次声明
    • 一个函数只能有一个rest参数,只能是最后一个参数
  • 箭头函数:

    • 参数不传、只传一个,传多个,以及代码块为单行、多行的书写形式
    • 箭头函数中的this指向

相关文章

  • es6的数值,函数,字符串扩展

    一、es6的数值扩展 二、es6的函数扩展 代码效果:对象解构在函数中的应用 三、es6字符串扩展 常用方法 代码...

  • ES6学习之- 函数的扩展

    Part2 函数的扩展 2.1 函数参数默认值,可以传任意类型 函数名 = ([param1, ..., par...

  • ES6学习笔记之函数扩展

    一. 函数参数的默认值 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。 Tips:参数变量是默认声...

  • es6之扩展运算符 三个点(...)

    es6之扩展运算符 三个点(...)对象的扩展运算符数组的扩展运算符总结es6之扩展运算符 三个点(…)对象的扩展...

  • ES6之函数扩展

    目录一、 函数参数的默认值二、rest参数三、严格模式四、name 属性五、箭头函数六、双冒号运算符(目前报错,只...

  • ES6之函数扩展

    (1)rest参数 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用argument...

  • ES6之函数扩展

    关键词:函数扩展 参数的默认值 属性的默认值 rest:获取函数的多余参数 箭头函数 箭头函数使用注意:1.函数体...

  • ES6学习—函数的扩展

    1.函数参数的默认值 (1)基本用法 ES6之前不能直接给函数的参数指定默认值,只能通过变通的方法 ES5允许为函...

  • ES6之函数的扩展

    ES6上函数的扩展表现在不仅体现在参数和属性变化还有具体函数表现形式的变化。 首先是ES6上函数参数上的...

  • ES6之函数的扩展

    ES6对函数的扩展函数蛮多的,不过常用的估计也就是默认值和箭头函数了。 函数参数默认值: 估计很多人都这样用过默认...

网友评论

      本文标题:ES6学习之- 函数的扩展

      本文链接:https://www.haomeiwen.com/subject/bbwasxtx.html