1、 函数参数的默认值

参数变量是默认声明的,所以不能用let或const再次声明。

function foo(x = 5) {
    let x = 1; // error
    const x = 2; // error
}
foo()//Identifier 'x' has already been declared
1
2
3
4
5

2、 函数的length属性

  指定了默认值以后,函数的length属性,将返回没有指定默认值之前的的参数的个数。 也就是说,指定了默认值后,length属性将失真。默认值后面的参数将不参加计算。函数的length属性,不包括 rest 参数。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
1
2
3
4

3、 作用域

  一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。(本质上是暂时性死区和不能重复声明)

var x = 1;
function f(x, y = x) {
    //let x=3;Identifier 'x' has already been declared
    //let y=7;// Identifier 'y' has already been declared
    console.log(y);//2
}
f(2) 
1
2
3
4
5
6
7

4、 rest参数

  形式为(...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。注意:rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

function push(array, ...items) {
    items.forEach(function (item) {
        array.push(item);
        console.log(item);
    });
}
var a = [];
push(a, 1, 2, 3)
1
2
3
4
5
6
7
8

5、 严格模式

  只要函数参数使用了默认值、解构赋值、或者扩展运算符(ES6语法默认是严格模式),那么函数内部就不能显式设定为严格模式,否则会报错。

6、 name属性

  如果将一个匿名函数赋值给一个变量,ES5的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。 如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。

const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
1
2
3
4
5

7、 箭头函数

  ES6 允许使用“箭头”(=>)定义函数。如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。 如果箭头函数直接返回一个对象,必须在对象外面加上括号 ,否则会报错。

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

箭头函数需要注意的地方有以下几点
  1、函数体内的this对象,就是定义时所在的对象(固定不变),而不是使用时所在的对象。
  2、不可以当作构造函数, 也就是说,不可以使用new命令,否则会抛出一个错误。
  3、不可以使用arguments对象, 该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  4、不可以使用yield命令, 因此箭头函数不能用作Generator 函数。

function Timer() {
    this.s1 = 0;
    this.s2 = 0;
    // 箭头函数
    setInterval(() => this.s1++, 1000);
    // 普通函数
    setInterval(function () {
        this.s2++;//this表示window,setInterval是window的属性
    }, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

  上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

this固定化的本质:并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this 正常情况下,this引用的是函数据以执行的环境对象,或者说是调用该函数的对象。

function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id);
      };
    };
  };
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
1
2
3
4
5
6
7
8
9
10
11
12
13

  上面代码之中,只有一个this,就是函数foo的this,所以t1、t2、t3都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this

箭头函数不适用场合:

  1、定义函数的方法(此时应该用普通函数的方式)

var lives=18;
const cat = {
    lives: 9,
    a: this.lives,//this指向的是window,
    say: function () {
        console.log(this.lives);//this指的是cat
    },
    jumps: () => {
        this.lives--;//this指的是window,定义时的this指的就是window
    }
}
cat.say();
cat.jumps();
console.log(cat.a);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

  2、需要动态this的时候

var button = document.getElementById('press');
button.addEventListener('click', () => {
        this.classList.toggle('on');//this指的是window
})
1
2
3
4

适用场合:回调

var handler = {
	id: '123456',
	init: function() {
		document.addEventListener('click',
			event => this.doSomething(event.type), 
		false);//this指的是handler
	},
	doSomething: function(type) {
		console.log('Handling ' + type  + ' for ' + this.id);//this指的是hander
	}
};
1
2
3
4
5
6
7
8
9
10
11

8、 尾调用

  是指某个函数的最后一步是调用另一个函数。 尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。(就是说外层函数的作用域链会被销毁,但它的活动对象任然会留在内存中)

function f(x){
	return g(x);
}
1
2
3

9、 尾递归

  尾调用自身,就称为尾递归。缺点:把所有用到的内部变量改写成函数的参数。优点:不会发生栈溢出,相对节省内存。

function factorial(n, total) {
	if (n === 1) return total;
	return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
1
2
3
4
5

采用es6语法(参数的默认值)可以解决这个缺点

function factorial(n, total=1) {
	if (n === 1) return total;
	return factorial(n - 1, n * total);
}
factorial(5) // 120
1
2
3
4
5
Last Updated: 5/2/2020, 11:20:31 PM