本篇文章为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的内容足够放到一篇新的文章去了(容我踩一脚刹车,有空再写:-)