ES6学习-函数

函数形参的默认值

JavaScript函数有一个特别的地方,无论在函数定义中声明了多少形参,都可以传入任意数量的参数,也可以在定义函数时添加针对函数数量的处理逻辑,当已定义的形参无对应参数时传入指定的默认值。

在ES5中模拟默认参数
function makeRequest(url,timeout,callback){
     timeout=timeout||2000;
     callback=callback||function(){//其它内容};
}
//当timeout=0时,会出现异常。
//这种情况下使用typeof检查参数类型更安全
function makeRequest(url,timeout,callback){
     timeout=(typeof timeout!==undefined)?timeout:2000;
     callback=(typeof callback!==undefined)?callback:function(){//其它内容};
}
//这种方式虽然安全但是需要额外的代码执行操作。
在ES6中的默认参数值

ES6简化了形参提供默认值的情况,如果没有参数传入值则为其提供一个。

function makeRequest(url,timeout=2000,callback=function(){}){
      //函数的其它部分。
}
//使用
makeRequest("/foo");//使用timeout和callback的默认值
makeRequest("/foo,500");//使用callback的默认值
makeRequest("/foo,500,function(body){doSomething(body);}");//不使用默认值
//声明函数时,可以为任意参数指定默认值,在已指定默认值的参数后可以继续声明无默认值参数。
function makeRequest(url,timeout=2000,callback){
     //其它内容
}
//调用
makeRequest("/foo",undefined,function(body){return body;});//使用timeout默认值。
makeRequest("/foo");//使用timeout默认值。
makeRequest("/foo",null,function(body){return body;});//使用timeout默认值。
//null是一个合法值。
默认参数值对arguments对象的影响

切记,当使用默认参数值时,arguuments对象行为和以往不同。

function mixArgs(first,second){
    console.log(first===arguments[0]);//true
    console.log(second===arguments[1]);//true
    first="c";
    second="d";
    console.log(first===arguments[0]);//true
    console.log(second===arguments[1]);//true
}

在非严格模式下,命名参数的变化会同步跟新到arguments对象中。
在严格模式下,取消了这个行为,arguments对象不再改变。

function mixArgs(first,second){
        "use strict"
    console.log(first===arguments[0]);//true
    console.log(second===arguments[1]);//true
    first="c";
    second="d";
    console.log(first===arguments[0]);//false
    console.log(second===arguments[1]);//false
}

ES6中,如果一个函数使用了默认参数值,则无论是否显式定义了严格模式,arguments对象都与ES5严格模式一致。

function mixArgs(first,second="b"){
    console.log(arguments.length);//1
    console.log(first===arguments[0]);//true
    console.log(second===arguments[1]);//false
    first="c";
    second="d";
    console.log(first===arguments[0]);//false
    console.log(second===arguments[1]);//false
}
mixArgs("a");
默认函数表达式

关于默认参数值,最有趣的特性可能就是非原始值传参。

function getValue(){
    return 5;
}
function add(first,second=getValue()){
    return first+second;
}
console.log(add(1,1,));//2
console.log(add(1));//6
//在这段代码中,如果不传入最后一个参数,就会调用getValue()方法,注意:函数声明时不会调用。
let value=5;
function getValue(){
    return value++;
}
function add(first,second=getValue()){
    return first+second;
}
console.log(add(1,1));//2
console.log(add(1));//6
console.log(add(1));//7

注意,当使用函数调用结果作为默认参数值时,如果忘记写小括号,传入的是对函数的调用。

//可以用先定义的参数作为后定义参数的默认值,反过来不行。
function add(first,second=first){
    return first+second;
}
console.log(add(1,2));//3
console.log(add(1));//2
默认函数的临时死区
function add(first=second,second){
    return first+second;
}
console.log(add(undefined,1));//报错
//调用时相当于:
let first=second;
let second=1;
ES5中的无命名函数
function pick(object){
    let result=Object.create(null);
    //从第二个参数开始
    for(let i=1,len<arguments.length;i<len;i++){
        result[arguments[i]]=object[arguments[i]]
    }
    return result;
}
let book={
    title:"Understanding ECMAScript",
    author:"Nicholas C.Zakas",
    year:2016
}
let bookData=pick(book,"author","year");
console.log(bookData.author);//"Nicholas C.Zakas"
console.log(bookData.year);//2016

用不定参数改写就可以解决索引不是1的问题。

不定参数

在函数的命名参数前添加三个点(...)就表明这是一个不定参数。

//改写pick()函数。
function pick(object,...keys){
    let result=Object.create(null);
    for(let i=0,len=keys.length;i<len;i++){
        result[keys[i]=object[keys[i]];
    }
    return result;
}
//好处是只要一眼就可以看出参数使用数量。
//注意:arguments包含所有传入参数,而length统计的是函数命名参数的数量。

不定参数使用有一定的限制。
①每个函数只能有一个不定参数,而且只能放在最后。
②不定参数不能使用对象字面量setter之中。

增强的Function函数

ES6增强了Function构造函数的功能,支持在创建函数时,定义默认参数和不定参数。

var add=new Function("first","second=first","return first+second");
console.log(add(1,2));//3
console.log(add(1));//2
//使用不定参数
var pickFirst=new Function("...args","return args[0]");
console.log(pickFirst(1,2));//1
展开运算符

在所有的新功能中,展开运算符和不定参数最相似。不定参数可以让你指定多个独立的参数,并通过整合后的数组来访问;而展开运算符可以让你指定一个数组,将它们打散后作为自己独立的参数传入函数。在大多数使用apply的方法使用展开运算符有一定好处。

let value1=25,value2=50;
console.log(Math.max(value1,value2));//50
//Math.max()方法可以接受任意数量的参数并返回值最大的那个。
let values=[25,50,75,100]
console.log(Math.max.apply(Math, values));//从数组中选择
//使用ES6可以简化
//等价于
console.log(Math.max(...values));//100
//可以传入单独限定值
console.log(Math.max(...values,90));//100
name属性

ES6中会打印名字属性。
注意,通过get,set,bind创建的函数名字有前缀。

元属性new.target

为了判断函数是否通过new调用,ES6引入了new.target这个元属性(指非对象属性,可以提供非对象目标的补充信息)。

function Person(name){
    if(typeof new.target!=="undefined"){
        this.name=name;
    }else{
        throw new Error("你必须通过new关键字来调用")
    }
}
var person=new Person("changchang");
var notPerson=Person.call(person,"changchangge");//抛出错误
//在函数外使用new.target是个语法错误。
块级元素

在ES6中,块级声明不会抛出错误,,作用域就是代码块。

//严格模式下
"use strict"//严格模式下提升到代码块顶部。
if(true){
    console.log(typeof doSomething);//"function"
    function doSomething(){
        //空函数
    }
    doSomething();
}
console.log(typeof doSomething);//"undefined"
//非严格模式,函数不提升至代码块顶部,而是提升到外围函数或者全局作用域。
if(true){
    console.log(typeof doSomething);//"function"
    function doSomething(){
        //空函数
    }
    doSomething();
}
console.log(typeof doSomething);//"function"
箭头函数

箭头函数与传统函数有些不同,主要集中在以下方面:
·没有this、super、arguments和new.target绑定。(箭头函数中的this、super、arguments及new.target这些值由外围最近一层非箭头函数决定。)
·不能通过new关键字调用。(箭头函数没有【【Construct】】方法,所以不能做构造函数。)
·没有原型。
·不可以改变this的绑定。(函数内部的this值不可被改变,在函数的生命周期内始终保持一致,使用call(),apply(),bind()方法无效)
·不支持arguments对象。(函数箭头没有arguments绑定,所以必须通过命名参数和不定参数这两种形式访问函数的参数)
·不支持重复的命名参数。(无论在严格还是非严格模式下,箭头函数都不支持重复的命名参数)

箭头函数语法
let reflect=value=>value;
//相当于
let reflect=function(value){
    return value;
};

let sum=(num1,num2)=>num1+num2;
//实际相当于
let sum=function(num1.num2){
    return num1+num2;
}

let getName=()=>"cc"
//相当于:
let getName=function(){
    return "cc";
}

let sum=(num1,num2)=>{
    return num1+num2;
};
//实际上相当于
let sum=function(num1,num2){
    return num1+num2;
}

let getTempItem=id=>({id:id,name:"changchang"});
//相当于
let getTempItem=function(id){
    return{
        id:id,
        name:"Temp"
    }
}
创建立即执行函数表达式
let person=((name))=>{
    return{
        getName:function(){
            return name;
        }
    }
}("changchang")
console.log(person.getName());
//等价于

let person=function(name){
    return {
        getName:function(){
            return name;
        }
    };
}("changchang");
console.log(person.name());
箭头函数没有this绑定
let PageHandler={
    id:"123456",
    init:function(){
        document.addEventListener("click",function(event){
            this.doSomething(event.type);//抛出错误
        },false);
    },
    doSomething:function(type){
        console.log("Handing"+type+"for"+this.id)
    }
};
//这段代码没有按照效果实行,因为,this绑定的document。
let PageHandler={
    id:"123456",
    init:function(){
        document.addEventListener("click",function(event){
            this.doSomething(event.type);
        }.bind(this),false);
    },
    doSomething:function(type){
        console.log("Handing"+type+"for"+this.id)
    }
};
//可以实现效果但实际上bind()创建了新函数,降低了效率。
let PageHandler={
    id:"123456",
    init:function(){
        document.addEventListener("click",function(event){
            event=>this.doSomething(event.type);
        },false);
    },
    doSomething:function(type){
        console.log("Handing"+type+"for"+this.id)
    }
};
//ES6很好的实现了效果,this指向箭头函数外层this。

箭头函数没有prototype属性。

let MyTpe=()=>{},object=new MyTpe();//报错
箭头函数和数组

箭头函数的语法简洁,非常适用于数组处理。
例如:简单的排序

var result=values.sort(function(a,b){return a-b;});
var result=values.sort((a,b)=>a-b);
箭头函数没有arguments绑定

箭头函数没有自己的arguments,且无论函数在哪个上下文执行,都可以访问外围的arguments对象。

function createArrowFunctionReturningFirstArg(){
    return ()=>arguments[0];
}
var arrowFunction=createArrowFunctionReturningFirstArg(5);
console.log(arrowFunction());//5
尾调用优化

ES5中,在循环调用中,每一个未用完的函数都会保存在内存中,当调用栈变得过大时会造成程序问题。
ES6中缩短了严格模式下尾调用栈的大小(非严格模式不受影响),如果满足以下条件,尾调用将不再创建新的栈,而是清除并重用当前的栈。
·尾调用不访问当前栈的变量(也就是说函数不是一个闭包)。
·在函数内部,尾调用是最后一条语句。
·尾调用结果作为函数值返回。

//例子
"use strict"
function doSomething(){
   //优化后
   return doSomethingElse();
}
//无法优化例子
//First
"use strict"
function doSomething(){
   //无法优化,无返回
   doSomethingElse();
}
//无法优化例子
//Second
"use strict"
function doSomething(){
   //无法优化,必须在返回值后添加其他操作
   return 1+doSomethingElse();
}
//无法优化例子
//Third
"use strict"
function doSomething(){
   //无法优化,调用不在尾部
   var result=doSomethingElse();
   return result;
}
//无法优化例子
//Fourth
"use strict"
function doSomething(){
    var num=1,func=()=>num;
   //无法优化,该函数是个闭包
   return func();
}
如何利用尾调用优化

递归函数式主要应用场景,请看例子。

function factorial(n){
    if(n<=1){
        return 1;
    }else{
        //无法优化,必须在返回后执行惩罚操作
        return n*factorial(n-1);
    }
}

function factorial(n,p=1){
    if(n<=1){
        return 1;
    }else{
        let result=n*p;
        //优化后
        return factorial(n-1,result);
    }
}

写递归函数时,利用好尾调用优化可以很好的提升程序性能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,699评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,124评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,127评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,342评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,356评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,057评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,654评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,572评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,205评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,343评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,015评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,704评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,196评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,690评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,348评论 2 358

推荐阅读更多精彩内容