ES6箭头函数(=>)和拓展运算符(...)

本篇文章为this整理的拓展,在JavaScript编写过程中,充分使用ES6的特性,可以有效减少this的错误引用

匿名函数与箭头函数:

匿名函数,又称lambda,通常在无需给函数命名的场景下使用,目前许多语言都支持lambda语法(C++11、JAVA8开始也支持lambda)。
  JavaScript中的匿名函数长这样↓:

//了事拂袖去,深藏功与名.
(function(){
  //do something
})()
//数组排序
var arr = ["11","8","33", "9", "7", "10", "12"];
var newArr = arr.sort(function(a,b){
  return Number(a) - Number(b)
})

JavaScript的箭头函数与匿名函数,在用法上大同小异↓:

a.箭头左边为参数,箭头右边跟返回值

(a,b,c)=>a*b*c

b.箭头右边跟代码块

(a,b)=>{
  var c = a*a + b*b
  return Math.sqrt(c); // 返回值可选,用法和普通函数一样
}

c.单一参数,括号可以省略(无参数必须要括号)

a=>a*2-1

用法:

//作为参数传入:
var timer = setTimeout(()=>{
  console.log('bang!')
},1000)
var array = [1,2,3,4,5]
var sum = array.reduce( (prev,next)=>prev+next )//15
//赋值:
var isLeapYear = (year)=>{
  if( year%4 === 0 && year % 100 !== 0 ){
    return true;
  } else {
    return false;
  }
}
isLeapYear(2018) // false

箭头函数看起来比function(){}简洁多了,然而箭头函数并不仅仅是因为为了简介才设计出来,尽管跟function(){}用法相同,使用的时候还是需要注意一些问题:

1.箭头函数不会再其作用域生成this
对照xiaoming作用域下的三个变量:

var xiaoming = {
    age:18,
    getAge:function(){
        var xiaomingsAge= this.age;//this是xiaoming
        var getAgeWithLambda = ()=>this.age;//this还是xiaoming
        var getAgeWithFunction = function(){
            return this.age;// this是window,
            //window下没有声明age变量,故为undefined
        }//对照
        console.log(xiaomingsAge)//18
        console.log(getAgeWithLambda())//18
        console.log(getAgeWithFunction())//undefined
    }
}

这意味着在箭头函数中引用的this跟当前作用域保持一致,ES6之前保存this,有人会这么做(把this存为一个变量)↓:

//做一个1秒后爆炸的定时炸弹!
//no more
var bomb = {
  sound:"boooooooom!!",
  detonate:function(){
      const that = this;
      setTimeout(function(){
         console.log(that.sound)
      },1000)
   }
}

或者这么做(调用bind(this))↓

//no more!!
var bomb = {
  sound:"boooooooom!!",
  detonate:function(){
      setTimeout(function(){
         console.log(this.sound)
      }.bind(this),1000)
   }
}

有了箭头函数以后↓

//you can do like this!
var bomb = {
  sound:"boooooooom!!",
  detonate:function(){
      setTimeout(()=>{
         console.log(this.sound)
      },1000)
   }
}

2.箭头函数不再有prototype

var lambda = ()=>0;
console.log(lambda.prototype) //undefined

3.箭头函数无法作为构造器(new构造)或生成器(但仍可以被async修饰)

var lambda = (a)=>{this.a = "aaa"}
new lambda(a) //TypeError: lambda is not a constructor
var gen = *(a)=>{
  yield a++
}//SyntaxError: Unexpected token *

4.箭头函数作用于内不会再生成arguments
原理同this

function showArgs(){
    console.log(arguments)
}
showArgs(1,2,3,4,5);// 打印arguments对象

var showLambdaArgs = ()=>{
    console.log(arguments);
}
showLambdaArgs(1,2,3,4,5);//arguments is not definded!

上级作用域的arguments,在箭头函数中不变:

    function plus (a,b){
        var a  = arguments
        console.log('plus:',arguments); // arguments[2,3]
        ((c,d)=>{
              console.log('lambda:', arguments);//arguments [2,3]
              console.log(a === arguments);//true
        })(4,5)
    }
    plus(2,3)

如果我一定要引用传入的参数怎么办?答↓:

拓展运算符(Spread syntax):

拓展运算符允许一个可迭代的对象(数组、类数组对象、字符串、ES6中的Set、Map等拥有默认迭代器Symbol(Symbol.iterator)属性,可以被for...of遍历的对象)去拓展函数调用的参数、数组的元素,同时也允许以Object键值对的形式去拓展另一个对象

概念看看就好,不理解就往下看
(1)长啥样?

...

(2)怎么用?
记下文加粗字体就行

a1.在上文中,如果想引用箭头函数的参数,就在参数声明里拓展,拓展出来的参数就是一个真正的数组(化参数为数组)

var showLambdaArgs = (...foo)=>{
  return foo
}
showLambdaArgs(1,2,3,4,5,6)//[1,2,3,4,5,6]

//在普通函数中也能使用,注意与arguments的区别
function showArgs(...foo){
    console.log(arguments instanceof Array)// false
    console.log(foo instanceof Array)// true;
}

a2.也可以先定义固定参数,再引用接受剩余的参数:
注意,剩余参数必须放到最后,此后不能再跟其他参数(仅限a1,a2其他用法无此限制)

function introduce(name,age,...args){
   console.log(name)
   console.log(age)
   console.log(args)//an array
}
introduce("xiaoming","male",192,1920000,19.2,true);
//xiaoming,male,[192,1920000,19.2,true]
function wrongUsage(a,b,...c,d){
}//SyntaxError

b.调用函数时,把一个可迭代对象拓展传入,这个对象里面的元素就会变成单独的参数(化数组为参数)

function introduce(){
   console.log(arguments)//对象
   console.log(arguments.length)//参数个数
}
introduce(1,2,3,...[8,9,0])// arguments对象 和 6
introduce(4,5,..."abcde")// arguments对象 和 7

前文中原本需要 Math.max.apply(null,[a,b,c])的调用,变得简简单单:

Math.max(...[11,2,5,99,1]) //99
Math.min(...[11,2,5,99,1]) //1

c.化可迭代对象为数组

//字符串转化为数组
[..."ABCDEFG"] //["A", "B", "C", "D", "E", "F", "G"]
//解构赋值也可以这么用
var [a,b,c] = [...'123']//a = 1;b = 2;c = 3
//拓展运算的对象不用非得放在最后:
["a",..."bcdefg","h","i"]//["a", "b", "c", "d", "e", "f", "g", "h", "i"]
//一行去重数组ver2:
[...new Set([1,1,2,3,3,4,4,4])] //1 , 2 , 3 ,4
//倒序参数
function reverseArgs(){
  return [...arguments].reverse()//现在可以调用数组方法了
}
reverseArgs(1,2,3,4,5)//[5, 4, 3, 2, 1]

d.拓展对象:
对象的拷贝方法五花八门,
有JSON.parse(JSON.stringify(obj))的
有for(var i in obj){}赋值的
使用ES6的Object.assign()可能是大多数使用ES6开发者的选择:

var o = {a:1,b:2}
var p = Object.assign({},o)
console.log(o === p) //false

也可以使用拓展运算:

var object = {a:1,b:2}
var prince = {...object } 
console.log( prince )// {a: 1, b: 2}
console.log(object  === prince ) // false

不仅如此,对象可以实现任意次数和位置的拓展:

//也可以任意拓展:
var object = {a:1,b:2}
var queen = {c:3,d:4}
var rose = {
  zero:0,
  ...object,
  ...queen,
  last:666
}
console.log( rose )//"{"zero":0,"a":1,"b":2,"c":3,"d":4,"last":666}"

结束语

虽说是前文的拓展,但是与this相关的讲解不算紧密相连,大概是es6有意地弱化了开发者对this和arguments依赖的结果吧。本来想一口气顺带连class也介绍的,但class的内容足够放到一篇新的文章去了(容我踩一脚刹车,有空再写:-)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。