首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ES6--函数的扩展

ES6--函数的扩展

作者头像
Clearlove
发布2019-08-29 14:58:52
4180
发布2019-08-29 14:58:52
举报
文章被收录于专栏:前端客栈前端客栈

最近因为参与小程序的开发,本身就支持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,所以也不能作为构造函数。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数参数默认值
  • 与解构赋值默认值结合使用
  • 参数默认值的位置
  • 函数length属性
  • rest参数
  • 箭头函数
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档