专栏首页前端客栈ES6--函数的扩展

ES6--函数的扩展

最近因为参与小程序的开发,本身就支持ES6的语法,所以也是对ES6一次不错的实践机会,接下去会逐一的将学习过程中涉及的常用语法和注意事项罗列出来,加深印象。

函数参数默认值

ES6之前,不能直接给函数参数指定默认值,只能用变通的方法,例如:

function log(x, y) {
  y = y || 'word';
  console.log(x, y); 
}

log('hello'); // hello, word
log('hello', 'clearlove07'); // hello clearlove07

但上例中y对应的值如果是false的话,该赋值就不起作用了,例如y''空字符串:

log('hello', ''); // hello word

为了避免这个问题,通常还需要对y进行判断,看是否有赋值,如果没有再使用默认值。

ES6允许为函数的参数设置默认值,直接写在参数定义后面,例如:

function foo(x, y = 'word') {
  console.log(x, y);
}

log('hello'); // hello word
log('hello', 'clearlove07'); // hello clearlove07
log('hello', ''); // hello

除了简约,这种写法还有两个好处:

  1. 代码自说明,阅读代码的人通过参数就可以一目了然的清楚哪些参数是非必填的,不需要查看文档就可以了解。
  2. 可扩展性强,调用函数方哪怕不传这个参数值,也不会影响函数的执行。

注意事项:

  • 参数变量一旦指定默认值,函数体内不能再次声明
function foo(x = 3) {
  let x = 4; // error
  const x = 5; // error
}
  • 参数有默认值是,函数参数不能同名
// 不会报错
function foo(x, x, y) {
 ...
}

// 报错
function foo(x, x, y = 1) {
 ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

与解构赋值默认值结合使用

参数默认值可以与解构赋值的默认值,结合起来使用。例如:

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

fetch('https://segmentfault.com/u/clearlove07', {});
// 'GET'

fetch('https://segmentfault.com/u/clearlove07');
// Uncaught TypeError: Cannot match against 'undefined' or 'null'

上面代码中,fetch的第二个参数是个对象的话,那么就可以个这个对象赋值默认值,但这种写法不能省略第二个参数,否则会报错。那如果希望第二个参数也有默认值,则需要双重赋值:

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

fetch('https://segmentfault.com/u/clearlove07'); // 'GET'

还有一个比较有意思的写法:

// 写法一
function foo1({x = 0, y = 0} = {}) {
  console.log([x, y]);
}

// 写法二
function foo2({x, y} = {x:0, y:0}) {
  console.log([x, y]);
}

// 函数没有参数情况下
foo1(); // [0, 0]
foo2(); // [0, 0]

// 参数x, y都有值的情况下
foo1({x: 1, y: 2}); // [1, 2]
foo2({x: 1, y: 2}); // [1, 2]

// x有值, y无值的情况下
foo1({x: 1}); // [1, 0]
foo2({x: 1}); // [1, undefined]

// x和y都无值的情况下
foo1({}); // [0, 0]
foo2({}); // [undefined, undefined]

foo1({z: 1}); // [0, 0] 
foo2({z: 1}); // [undefined, undefined]    

上面两种写法共同点是: 有默认值。 区别在于: 写法一: 默认值是个空对象,但是设置了对象解构赋值的默认值 写法二: 默认值是个有具体属性的对象,但是没有设置对象解构赋值的默认值

参数默认值的位置

通常情况下,定义了函数的默认值的参数,应该放在参数列表的后面,这样比较清晰那些参数可以省略的。如果是在非尾部的参数设置默认值,实际上这个参数是没办法省略的。

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

foo(); // [1, undefined]
foo(2); // [2, undefined]
foo(, 2); // Uncaught SyntaxError: Unexpected token ,
foo(undefined, 2); // [undefined, 2]

如果传入undefined,则会触发默认值的行为, 但是null则不会

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

foo(undefined, null); // [4, null]

函数length属性

函数声明/函数表达式都具有length属性,这个属性返回函数参数长度。 指定函数参数默认之后, 函数的length属性值将不包含指定默认值的参数个数。

let foo = function(x, y = 1) {}
foo.length // 1

let foo = function(x = 1, y) {}
foo.length // 0

let foo = function(x, y = 1, z) {}
foo.length // 1

rest参数

用于获取函数剩余参数,替代arguments对象,形式为...args。rest变量是数组形式。

function add(...values) {
  let sum = 0;
  for(let val for values) {
    sum += val;
  }
  return sum 
}

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

add()为求和函数,可以传递任意长度的参数进行求和。

使用rest参数替代arguments例子:

function numSort() {
  return Array.prototype.slice.call(arguments).sort();
}

// 替代方案
const numSort = (...nums) => nums.sort()

后面这种方法比较简洁,清晰明了。 rest参数中的变量代表一个数组,所以所有数组的方法都可以用于这个参数,例如:

function push(arr, ...nums) {
  nums.forEach(num => {
    arr.push(num);
  })
  return arr;
}

let arr = []
push(arr, 1, 2, 3, 4); // [1, 2, 3, 4]

注意:

  • rest参数后面不能有其他参数,即它为最后一个参数,否则会报错。
  • 函数的length属性, 不包含rest参数

箭头函数

如果 return 值就只有一行表达式,可以省去 return,默认表示该行是返回值,否则需要加一个大括号和 return。

let foo = (x) => x * x

// 等价于
let  foo = function(x) { return x * x; }

let foo = (x, y) => { 
  let total = x + y;  
  return total;     
} 

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 报错
let obj = () => { id: '1', age: 20 } 

// 正常
let obj = () => ({id: '1', age: 20})

箭头函数的一个用处是简化回调函数。

[1, 2, 3].map(function(x) {
  return x * x
}) 

// 简化
[1, 2, 3].map(x => x * x)

rest 参数与箭头函数结合的例子。

let concatArr = (num, ....items) => [num, items]
concatArr(1, 2, 3, 4) // [1, [2, 3, 4]]

使用注意事项:

  1. 函数体内this指向是定义时所在对象,而非执行时所在对象。 普通函数的this是可变的,我们通常把函数归为两种状态: 定义时/执行时。函数中的this始终指向执行时所在的对象。比如全局函数执行时,this指向的是window。对象的方法执行时,this指向的是该对象,这就是函数this的可变性,但箭头函数中的this是固定不变的。看下面的例子: function obj() { setTimeout(() => console.log(this.id), 1000) } let id = 1 obj.call({id: 2}); // 2
  2. 箭头函数里并没有存储this,将箭头函数转义成ES5: // ES6 function obj() { setTimeout( () => console.log(this.id), 1000) } // ES5 function obj() { var _this = this setTimeout(function() { console.log(_this.id) }, 1000) } 由于箭头函数没有自己的this,所以当然也不能使用call()apply()bind()等方法来改变this的指向。例如: (function() { return [ (() => this.id).bind({ id: 'inner' })() ]; }).call({ id: 'outer' }) // outer 上面代码中箭头函数没有自己的this,所以bind无效。内部的this指向外部的this
  3. 在对象中使用箭头函数,this执行的是window。 let obj = { id: 1, foo: () =>{ console.log(this.id) } } let id = 2 obj.foo() // 2
  4. 不可以当做构造函数,也就是不能使用new,否则会报错。
  5. 不可以使用arguments对象,该对象在函数内不存在。可以用rest参数代替。
  6. 不可以使用yield命令,因为箭头函数不能当做Generator函数。

箭头函数可以让this指向固化,这种特性有利于与封装回调函数。下面例子中,DOM时间的回调函数封装在一个对象里面。

let handler = {
  id: 'handler',
  init: function() {
    document.addEventListener('click', event => this.doSomething(event.type), false)
  },
  doSomething: function(type) {
    console.log('Handling ': type + ' id ' + this.id)
  } 
}

上面代码的init方法中,使用了箭头函数,这就导致了这个箭头函数里面的this总是指向handler对象。否则回调函数允许时,this.doSomething这一行就好报错,因为this指向的是window对象。 this指向的固化,并不是因为箭头函数内容有绑定this的机制,实际原因是因为箭头函数内部根本就没有this,而是一直指向外层代码块的this。正因为箭头函数没有this,所以也不能作为构造函数。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • CSS编码规范建议

    解释: BEM是一种命名方法,能够帮助你在前端开发中实现可复用的组件和代码共享。 BEM的命名规矩很容易记:block-name__element-name--...

    Clearlove
  • 知识整理之CSS篇

    CSS篇主要从CSS兼容、CSS3新特性、CSS选择器、高频属性、高频布局、高频知识点、性能优化等方面进行归纳。如对HTML知识点感兴趣,可移步至:知识整理之H...

    Clearlove
  • 知识整理之浏览器篇

    最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向与只指渲染引擎。 关于浏览器工作原理详解,请移步至:浏览器工作原理详解

    Clearlove
  • 防抖和节流 原

    浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上...

    tianyawhl
  • [前端] js中call方法的理解和思考

    最近接手前端的工作,对当前项目中自制的js框架下,js的使用产生了非常多的困惑.尤其是js的类,对象,函数,this等等相互之间的关系和转换,以前学过也忘得差不...

    陶士涵
  • golang数据结构之递归解决迷宫问题

    递归可以解决各种数学问题:n皇后问题、阶乘问题、汉诺塔、迷宫问题、球和篮子问题等等;

    绝命生
  • 认识PHP函数

    PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 ... 语法实现;在 PHP 5.5 及更早版本中,使用函数 fun...

    老雷PHP全栈开发
  • 学习PHP函数

    PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 ... 语法实现;在 PHP 5.5 及更早版本中,使用函数 fun...

    老雷PHP全栈开发
  • JavaScript闭包与箭头函数

    用户1203875
  • 彻底搞懂闭包,柯里化,手写代码,金九银十不再丢分!

    这段时间我试着通过思维导图来总结知识点,主要关注的是一些相对重要或理解难度较高的内容。下面是同系列文章:

    Tusi

扫码关注云+社区

领取腾讯云代金券