第二十四节: ES6 函数扩展

1. 函数参数的默认值

1.1. 函数的默认参数
function show(a,b){
    console.log(a,b);
}
show("welcome","wuwei");  // welcome wuwei

既然是函数封装,那函数封装是不是要具有通用性,

比如 我突然不想传第二个参数

function show(a,b){
    console.log(a,b);
}
show("welcome","");  // welcome 
show("welcome");    //welcome undefined


1.1.1. ES6之前设置默认参数

我们以前是怎么处理的,是不是用我们的短路算法

function show(a,b){
    b = b || "china"
    console.log(a,b);
}
show("welcome","");  //  welcome china
show("welcome");     //  welcome china

如果我第一个参数不传,我是不是也要一个默认值啊

function show(a,b){
    a = a || "welcome";
    b = b || "china";
    console.log(a,b);
}
show("您好","wuwei");  //  您好 wuwei
show("","wuwei");     //  welcome wuwei


1.1.2. ES6设置默认参数

ES6的默认参数,

function show(a = "欢迎",b = "无为"){
    console.log(a,b);
}
show();          //  欢迎 无为
show("welcome"); //welcome 无为


1.1.3. 默认参数的位置

起一个不传不行,会挂掉的

show( , "wuwei"); // 就报错

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

// 例一
function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

// 例二
function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]


1. 2. 默认参数结合对象解构赋值

遇到我们无法上面的情况怎么办,还记得我们之前的对象解构吗

function show({a = "欢迎",b = "无为"}){
    console.log(a,b);
}
show({a:"welcome",b:"wuwei"});  // welcome wuwei
show({b:"wuwei"});              //欢迎 wuwei
show({});                       // 欢迎 无为

// 但是这样就报错
function show({a = "欢迎",b = "无为"}){
    console.log(a,b);
}
show();                  // 报错

// 所以我们对象也得给一个默认值
function show({a = "欢迎",b = "无为"} = {}){
    console.log(a,b);
}
show();                  // 欢迎 无为


1.3. 函数的参数默认已经定义了,你不能再用let,const声明了
function show(age = 18){
    let age = 20;
    console.log(age);
}
show();    // 程序报错,age已经被定义过了
// Uncaught SyntaxError: Identifier 'age' has already been declared


2. 默认参数对函数length的影响

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
  • fn.length 返回形参个数
  • arguments.length 返回实参个数


3. 默认值与作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

var x = 1;

function f(x, y = x) {//AO{x:2,y:2}
  console.log(y);
}

f(2) // 2

上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2

let x = 1;

function f(y = x) { //AO{y:1,x:2}
  let x = 2;
  console.log(y);
}

f() // 1

上面代码中,函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x

var x = 1;

function foo(x = x) {   //AO{x:1}
  // ...
}

foo() // ReferenceError: x is not defined

上面代码中,参数x = x形成一个单独作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错”x 未定义“。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo()//3
x//1

如果将var x = 3var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1


4. rest 参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。reset 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。


4.1. 扩展运算符,reset运算符

写法就是... 三个点

两个作用,扩展和剩余


4.1.1 扩展运算符
let arr = ["apple","banana","arange"];
console.log(arr);      //  ["apple", "banana", "arange"]
console.log(...arr);   //   apple banana arange

// ...arr  是将数组arr的内容每一项扩展出来

... 能展开数组,

4.1.2 重置运算符,配合函数使用

刚讲的扩展运算符,是将[1,2,3,4,5] ==> 1,2,3,4,5

function show(a){
    console.log(a);  // 1
}
show(1,2,3,4,5);

// 如果我想讲 1,2,3,4,5  == > [1,2,3,4,5]
function show(...a){
    console.log(a);  // [1, 2, 3, 4, 5]
}
show(1,2,3,4,5);


4.1.3 解决了arguments问题

arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push方法的例子。

function show(){
    console.log(arguments);  // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
show(1,2,3,4,5);


// 如果想把arguments这个类数组变成数组
function show(){
    let arr = Array.prototype.slice.call(arguments)
    console.log(arr);  // [1, 2, 3, 4, 5]
}
show(1,2,3,4,5);

// 如果想给这个数组排序
function show(){
    let arr = Array.prototype.slice.call(arguments)
    arr.sort(function(a,b){return a-b});
    console.log(arr); 
}
show(1,102,32,14,5);     //[1, 5, 14, 32, 102]

但是如果用ES6排序

function show(...arr){
    arr.sort(function(a,b){return a-b});
    console.log(arr);  
}
show(1,102,32,14,5);   // [1, 5, 14, 32, 102]


4.1.4 传参时扩展

我们还可以在传参的时候扩展

// 解构方法
function show([a,b,c]){
    console.log(a,b,c);  
} 
show([1,9,6]);     //  1 9 6

// 扩展运算符,我在实参的时候就让你扩展
function show(a,b,c){
    console.log(a,b,c);  
} 
show(...[1,9,6]);  // 1 9 6


4.1.5 又叫剩余运算符
function show(a,b,c){
    console.log(a,b,c);  
} 
show(1,2,3,4,5);  // 1 2 3

这样我们abc拿到的就是1,2,3其他的是不是就没管啊

剩余运算符就是将剩余的参数放到一个数组中

function show(a,b,...c){
    console.log(a,b,c);  
} 
show(1,2,3,4,5);  // 1 2 [3,4,5]

// 这个时候发现c是出了a,b对应的那两项外剩余的所有参数组成的数组

如果当成剩余运算符来用必须放最后

注意哦!下面的写法会报错

function show(a,...b,c){
    console.log(a,b,c);  
} 
show(1,2,3,4,5);  // 报错

如果我们需要赋值一份数组,

var arr = [2,3,6,5,8];
var arr2 = [...arr];

// 以前Array身上还有一个from方法,拷贝数组
var arr = [2,3,6,5,8];
var arr2 = Array.from(arr);

// 这个方法还可以将字符串变成数组
var str = "abc";
var arr = Array.from(str); 
console.log(arr);   //["a", "b", "c"]

总结

  1. 展开数组

    ...[1,2,3] ==> 1,2,3

  2. 重置为数组

    ... 1,2,3 ==> [1,2,3]; 只能在传参的时候用

  3. 剩余参数,必须放到最后

    function(a,b,...c){}


4.1.6 函数的length不包括rest参数
(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1


5. 箭头函数

5.1. 基本用法

ES6 允许使用“箭头”(=>)定义函数。

箭头函数

​ () => {

​ }


5.1.1 箭头函数里面只有return语句
//  之前函数的写法
function show(){
    return 1;
}

// 箭头函数的写法
let show = () => 1;

// 这里的1 就是return 1
function show(a,b){
    return a + b;
}

// ES 6
let show = (a,b) => a + b;

注意如果我想return一个对象,这样写就会出问题

// 报错
let getTempItem = id => { id: id, name: "Temp" };

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });


5.1.2 常用的使用方法

如果想写语句,那么语句的写法是这样的

() => {

​ 语句;

​ return;

}

这才是一个完整的箭头函数的写法

let show = (a,b) => {
    console.log(a,b);
    return a + b;
}
show();


5.2. 箭头函数注意点

箭头函数需要注意一下几点:

5.2.1 箭头函数的this指向
let obj = {
    age: 18,
    show: function(){
        console.log(this.age);
    }
}
obj.show();   // 毋庸置疑 打印18 this 指向obj

// 如果换成箭头函数呢
var age = 20;
let obj = {
    age: 18,
    show: () =>{
        console.log(this.age);
    }
}
obj.show();


// 关于this指向定义时作用域中的this
var a = 11;
function test(){
    this.a = 22;
    let b = () =>{
        console.log(this.a)
    }
    b()
}
var obj = new test();

// 如果我里面添加定时器
let obj = {
    age : 18,
    show: function(){
        setTimeout(function(){
            alert(this.age); // 此时弹出undefined,此时this指向window
        },2000)
    }
}
obj.show();

// 如果使用箭头函数呢
let obj = {
    age:18,
    show: function(){
        setTimeout(()=>{
            alert(this.age);  // 这里会弹出18  ,为什么呢
        },2000)
    }
}
obj.show();

此时就是this的问题,以前我们讲this的时候,说this指向运行时,也就是调用这个函数运行的对象,

但是箭头函数的this指向,函数定义时所在函数的this对象

因为箭头函数this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。


5.2.2 箭头函数中的arguments

在箭头函数中没有arguments

// 普通函数
function show(){
    console.log(arguments);
}
show(1,2,3,4,5);  // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 箭头函数
let show = () => {
    console.log(arguments);
}
show(1,2,3,4,5); // 报错
// Uncaught ReferenceError: arguments is not defined at show

那在箭头函数里面没有arguments,那用什么呢,可以用剩余参数

// 箭头函数
let show = (...args) => {
    console.log(args);
}
show(1,2,3,4,5);  // 打印 [1, 2, 3, 4, 5]


5.2.3 箭头函数不能到构造函数

因为箭头函数内部没有this,所有不能当成构造函数使用

// 普通的构造函数
function Show(){
    this.name = "abc";
}
let show = new Show();
console.log(show.name);  // abc

let Show = () => {
    this.name = "abc"
}
let show = new Show();
console.log(show.name);  // 报错  类型错误
// Uncaught TypeError: Show is not a constructor


5.2.4 总结一下
  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  3. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。


5.3. 函数参数的尾逗号

ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。

这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。

知道一点东西

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