声明函数的时候,给形参赋一个值,这个值就是参数的默认值。调用函数时,如果没有进行相应的实参传递,参数就会使用默认值。

// num2拥有默认参数值5,如果没有给num2形参传值,它的取值将会是5
function sum (num1, num2 = 5) {
return num1 + num2;
} console.log(sum(1)) // 6 调用sum函数时, 只传递了一个参数1,所以函数中num1 =1, num2就会使用默认参数值5, 1+5 =6;
console.log(sum(1,2)) // 3 函数调用时,我们传递了两个参数,所以默认参数值不起作用, 函数使用我们传递过去的参数 1+2 =3

  这里所说的‘值’是广义的值,不仅仅是指像5这样的简单值,它可以是任意的js表达式,甚至是函数的调用

function getValue(value) {
return value + ;
}
// 函数参数是第一个参数的值。
function add(first, second = getValue(first)) {
return first + second;
}
console.log(add(, )); //
console.log(add()); //

  正如你看到的那样,默认参数为函数时,这个函数的调用是惰性的,如add(1,1)传递了两个参数,函数就不会调用。add(1)只传递一个参数, 这个函数才会调用。其次,getValue函数可以把函数的第一个参数first 作为自己的参数。但是这里有一个小细节要注意,函数的形参也有了自己的作用域,形参的作用域只是把函数声明()中的参数包起来,如果在参数默认值中去解析一变量,它先从形参作用域中进行查找,如果没有找到,它再从函数外面的作用域中进行查找。add 函数的参数的声明就像下面一样

let first;
let second = getValue(first);·

  getValue中的first参数,正好在形参作用域中找到了,所以没有问题,如果写反了,function add( second = getValue(first), first) {}, 函数的参数声明就变成了

let second = getValue(first);
let first;

  first 变量还没有声明,就使用了,造成了暂存死区。再来看一个例子,

let w = , z = ;

function foo( x = w + , y = x + , z = z +  ) {
console.log( x, y, z );
}
foo(); // ReferenceError

  先执行w + 1,  就会从形参作用域中找w,没有找到,就到函数外面去找,正好找到了w=1, 那x就赋值为2,再执行y=x + 1, 这时从形参作用域中找到了x,y变成了3,最后是z = z + 1,先执行z+1,在形参作用域找到了z,它就不会从外面的作用域去找了,但是z声明在后,z+1 就变成了引用一个未声明的变量,造成了暂存死区

  当参数拥有默认值以后,它影响了argumets 对象。我们都知道,每一个函数内部都有一个arguments 对象,保存函数调用时传递过去的参数,第一个参数对应的就是arguments[0], 第二个参数对应的就是arguments[1]. 像上面的sum 函数, num1 == argument[0]; 但有了默认参数值,这种对于关系打破了. sum(1) 调用sum 函数的时候,我们只传递了一个值1,也就意味着arguments[1] 的值是undefined, 但是它对应的num2 形参,num2 参数由于默认值的存在,这里取5.  arguments[1]  就不等于num2 了。还有一点就是,arguments 只是保存了传递过去的值,如果在函数内部 参数的值有更改,那么arguments 也不会实时反应这种变化,还是上面的sum(1) 调用,arguments[0] 永远等于1。 初始的时候,num1 == arguments[0]; 但如果在函数体中 num1 重新赋值为2, arguments[0] 就不等于num1 了。

function sum (num1, num2 = 5) {
console.log(arguments.length); // 1, 只传递了一个参数
console.log(num1 === arguments[0]); // true 初始时相等
console.log(num2 === arguments[1]); // false 只传一个参数,arguments[1] 是undefined, num2 取默认值5
num1 = 2;
console.log(num1 === arguments[0]); // false arguments只保存调用时的初值。
return num1 + num2;
} console.log(sum(1));

  记住一点就可以了, arguments 对象只保存调用函数时传递过去的参数的初始值。不太理解也没有关系,arguments 对象几乎用不到了,因为ES6 提供了更好的参数保存方式(剩余参数rest),下面会介绍。

  剩余参数 (rest)

  当我们调用函数的时候,我们可以传递任意数量的实参给函数,如果函数形参的数量少于实参的数量,我们就只能通过函数内部的arguments 获取多余的实参。ES6 提供了一个更简单的方法来获取这些多余的参数,就是剩余参数。我们在声明函数的时候,在一个参数的前面加上..., 这个参数就变成了一个数组,它会把多余的参数收集到它里面,变成它的元素。

let sum = (obj, ...rest) => {
console.log(rest) // [2,3,4,5]
} sum({a:1},2,3,4,5)

  上面代码中的rest就是一个剩余参数,它把2,3,4,5 收集起来,变成了它的元素,它本身是一个数组。

  注意:一个函数中只能有一个剩余参数,且它必须放到所有参数最后,这很好理解,因为,它把所有参数都收集到一起了,一个就足够了,如果它后面还有参数,这些参数也获取不到数据了,所以也就没有必要设置参数了。

  扩展操作符(...)

  扩展操作符,把一个可迭代对象(如数组)扩展给一个一个的单体。

let array = [1,2,3,4,5];
console.log(...array) // 1 2 3 4 5

  js 中的函数有两个内部的方法, [[Call]]  和[[Construct]] , 当我们调用函数的时候,没有使用new, 那[[Call]] 方法就会被调用,执行函数体。当调用函数的时候前面加了一个new, 那[[Construct]]  方法就会被调用,生成一个对象,调用函数,给对象赋值,并返回对象。当然并不是每一个函数都有[[Construct]] 方法,比如箭头函数就没有,所以箭头函数就不能使用new 进行调用。那怎么决定函数是用那种方法呢?

  ES6 增加了一个new.target,  如果一个函数通过new 调用,它内部会获取一个new.target 的元属性,它指向的就是我们的构造函数。 当然,如果这个函错误地通过一般函数调用,new.target 就是undefined. 这样我们就可以轻松地判断一个函数是不是通过new进行调用,从而避免了构造函数用普通方式进行调用产生的错误。

function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name;
} else {
console.error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // You must use new with Person.

  箭头函数

  语法: 参数列表 => 函数体;一个简单的箭头函数如下

(a,b) => a+b; 

  可以看到它只是一个匿名函数表达式,只有把它赋值给一个变量引用,才能对它进行调用,

let sum = (a,b) => a+b;
sum(, )

  箭头函数的参数列表,可以有一个参数,可以有多个参数,也可以没有参数。多个参数呢, 就像上面一样,把所有参数一一列出来,并用括号括起来,没有参数简单,直接一个() 就可以了,有一个参数呢,也可以使用()把这个参数包括起来,但通常会省略括号

let hello = () => console.log('hello'); // 箭头函数没有参数,直接用一个括号表示
let add10 = num => num +; // 箭头函数只有一个参数num,通常直接写这个参数,不用括号括起来。

  箭头函数右侧的函数体,可以只有一句表达式,如上面的a +b, 这时,除了可以直接写这一句表达式外,它默认是会返回表达式的值,这也就是我们没有写return a +b 的原因,这里要注意一点,如果返回一个对象,这个对象要用() 括起来。

let obj = name =>({name:name}) // 如果不写外面的括号,{} 就会被当做块级作用域

  那如果函数体是一段可以执行的语名块呢?那就需要用{}把语名块包起来,如果语句块执行完毕,还要返回值,那就要在语句块的末尾显示调用return

let amount = n => {
let sum =;
for(let i=; i<=n; i++){
sum = sum + i;
}
return sum;
}

  可以看到箭头函数大大简化了写法,尤其是回调函数的函数体只有一句表达式的时候,比如,sort 函数

var arr =[, , , ];
arr.sort(function(value1, value2){
return value1 – value2
}) // 箭头函数
arr.sort((value1, value2) => value1 –value2);

   箭头函数如果用到高阶函数上,也简化了高阶函数的书写。所谓的高阶函数,他或者接受一个或多个函数作为参数,它或者返回一个函数作为运行结果,只要两者满足其一,就是高阶函数。在高级程序设计书中,根据对象的属性对对象进行排序,就用到了高阶函数。

function compare(property) {
return function(obj1, obj2) {
var value1 = obj1[property];
var value2 = obj2[property]; if(value1 < value2) {
return -;
}else if(value1 > value2) {
return ;
}else {
return
}
}
}

  用箭头函数进行简化, 箭头首先是一个函数表达式,也就是我们要把箭头函数赋值给一个变量,否则无法调用。let compare =  箭头函数; 箭头函数表示方法是()=》表达式,接受多个参数,返回一个值。let compare =(property) => 返回值,但这里返回值又是一个函数,  函数有又可以用箭头函数表示 (obj1, obj2) => {},

let compare = property => (obj1, obj2) = > {
     var value1 = obj1[property];
var value2 = obj2[property]; if(value1 < value2) {
return -;
}else if(value1 > value2) {
return ;
}else {
return
}
}

但当我看到这个箭头函数,怎样转化成一般的函数,我们怎么读懂连缀的箭头函数?

  箭头函数永远是 ()=》表达式, 所以右侧的连缀箭头可以一个一个分开。从左向右依次分开, property => 返回值。 箭头前面永远是函数调用需要的参数,后面是返回的值,后面有多个箭头,我们可以看作一个整体(返回值)。 let compare = property => 返回值。  那么调用时, compare(name)  ======》(obj1,obj2)=> 返回值,他又是函数,接着调用 compare(name)(obj1,obj2),函数执行了。像这种箭头函数,它返回的永远都是函数,所以是闭包,可以获得前面参数的值。

  箭头函数除了简化书写外,还有一个更重要的内容,就是this 指向。

  箭头函数内部没有自己的this,但是我们可以在它里面使用this,这时this 的指向继承自外围作用域,就是this 会顺着函数的作用域链向上进行查找,直到找到离它最近的一个this,然后使用它。我们都知道this只存在函数中,所以箭头函数内部的this 就指向它的父函数或祖先函数中this,这样我们内部的this值和外部的this 值就统一了,再也不用 let that = this , 用that 去代替this了。

var object = {
f1: function(){
console.log(this);
var f2 = () => console.log(this);
f2();
setTimeout(f2, );
}
}
object.f1(); // f1 函数内部的this 全都指向 object

  f2函数体中有this, 但它是箭头函数,所以只能向上找,使用包含它的函数中的this, 找到了f1函数, f2 中的this 和f1 中的this 保持一致。这里我们再改一下,把f1 也改成箭头函数,

var object = {
f1: () => {
console.log(this);
var f2 = () => console.log(this);
f2();
setTimeout(f2, );
}
}
object.f1(); // f1 函数内部的this 全都指向 object

  这时你会发现this 指向了window, 按照 object.f1() 的调用方式,f1 函数中的this 应该指向object.其实不是,箭头函数的this 是在它定义的时候,就已经确定了,就像使用了bind方法,而不是动态绑定了, 当箭头函数调用的时候,真正要确定的是它在定义的时候,它所能向上寻找到的包含它的最外围的函数中this. 我们再来分析一下,f2 向上找f1,  f1 也是箭头函数,它还要向上找,但你发现包含f1的函数没有了,只有全局对象window了,this 指向了window,  箭头函数在调用的时候,它真正确定的是包含f1函数的函数中this 的指向, 如果没有包含函数,就是window全局对象了。

  箭头函数始终保持它里面的this和它外围的包含它的函数中this 一致。再看一个例子

var object = {
f1: function(){
console.log(this);
var f2 = () => console.log(this);
f2();
setTimeout(f2, );
}
}
var anoter = object.f1;
anoter() // this 指向了window

  anoter 函数在全局作用域中调用, f1 函数中的this 指向了window, f2 是箭头函数,和它外围的f1 保持一致,里面的this 指向window.

  

ES6函数增强的更多相关文章

  1. ES6函数扩展

    前面的话 函数是所有编程语言的重要组成部分,在ES6出现前,JS的函数语法一直没有太大的变化,从而遗留了很多问题和的做法,导致实现一些基本的功能经常要编写很多代码.ES6大力度地更新了函数特性,在ES ...

  2. ES6 对象增强和结构赋值

    The enhanced Object literals: ES6 has added some new syntax-based extensions to {} object literal fo ...

  3. 深入理解javascript函数系列第四篇——ES6函数扩展

    × 目录 [1]参数默认值 [2]rest参数 [3]扩展运算符[4]箭头函数 前面的话 ES6标准关于函数扩展部分,主要涉及以下四个方面:参数默认值.rest参数.扩展运算符和箭头函数 参数默认值 ...

  4. ES6函数剩余参数(Rest Parameters)

    我们知道JS函数内部有个arguments对象,可以拿到全部实参.现在ES6给我们带来了一个新的对象,可以拿到除开始参数外的参数,即剩余参数(废话好多 O(∩_∩)O~). 这个新的对象和argume ...

  5. ES6函数默认参数(Default Parameters)

    语言更新时每一个新增的特性都是从千百万开发者需求里提取过来的,规范采用后能减少程序员的痛苦,带来便捷. 我们经常会这么写 function calc(x, y) { x = x || 0; y = y ...

  6. es6函数的rest参数和拓展运算符(...)的解析

    es6的新特性对函数的功能新增加了rest参数和...的拓展运算符.这是两个什么东西呢? 先来看一个问题:如何获取一个函数除了定义的参数之外的其他参数?传统的做法是借助函数的arguments关键字来 ...

  7. 深入理解ES6之—增强的数组功能

    创建数组 Array.of()方法 ES6为数组新增创建方法的目的之一,是帮助开发者在使用Array构造器时避开js语言的一个怪异点.Array.of()方法总会创建一个包含所有传入参数的数组,而不管 ...

  8. ES6函数的拓展

    ES里面现在支持在函数的参数直接给参数赋一个默认值,ES6支持拓展运算符(...)三个英文的点,这个形式如function(...a)这个里面...a可以接受若干的值,这个拓展运算符也可以把若干的值转 ...

  9. ES6 函数的扩展1

    1. 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值. ES6允许为函数的参数设置默认值,即直接 ...

随机推荐

  1. 【mongoDB查询进阶】聚合管道(三)--表达式操作符

    https://segmentfault.com/a/1190000010910985 管道操作符的分类 管道操作符可以分为三类: 阶段操作符(Stage Operators) 表达式操作符(Expr ...

  2. 下拉框选择blur与click冲突问题

    缘由:今天在做下拉框选择时,遇到click和blur冲突问题:具体现象如下 1.intput框获取焦点(focus事件)时显示隐藏的下拉框,失去焦点(blur事件)则隐藏下拉框 2.点击选择(clic ...

  3. 网络应用简记(4):DNS使用

    dns,domain name system,域名系统,把域名转化成ip的系统. 先来看几上工具的使用,这几个工具都能把域名转换成ip,都使用了dns.dns就好比数据库,通过对它的查询,能给url找 ...

  4. 【全网最全的博客美化系列教程】01.添加Github项目链接

    全网最全的博客美化系列教程相关文章目录 [全网最全的博客美化系列教程]01.添加Github项目链接 [全网最全的博客美化系列教程]02.添加QQ交谈链接 [全网最全的博客美化系列教程]03.给博客添 ...

  5. hibernate多对多 一对多 及简单入门 主键生成策略

    Hibernate简单使用 入门 通过hibernate的 一对多 多对多轻松看懂hibernate配置 (不使用注解) hibernate对jdbc访问数据库的代码进行轻量级封装,简化重复代码 减少 ...

  6. 【转】ubuntu 双机热备

    1.关于软件安装 sudo apt-get install libssl-dev sudo apt-get install openssl sudo apt-get install libpopt-d ...

  7. c语言之字符串和格式化输入输出

    字符串和格式化输入输出 #include<stdio.h> #include<string.h> #define DENSITY 62.4 int main(void) { f ...

  8. 【kindle笔记】读书记录-总

    注:谨以此贴记录各种读书心得的目录.包括纸质书和电子书等等. 我的啃豆 说起来,买啃豆的初心是<鸟哥私房菜> 来自亚马逊中国官网,于公元2017年,在双十一与双十二中间,一千大洋购得 有了 ...

  9. PAT L2-016 愿天下有情人都是失散多年的兄妹

    https://pintia.cn/problem-sets/994805046380707840/problems/994805061769609216 呵呵.大家都知道五服以内不得通婚,即两个人最 ...

  10. http/https与websocket的ws/wss的关系以及通过Nginx的配置

    http/https与websocket的ws/wss的关系 - 哒哒哒 - CSDN博客 https://blog.csdn.net/Garrettzxd/article/details/81674 ...