基本的用法
- ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
- ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {}
// 这里 y 的默认值为 'World'
log('hello')
log('hello', 'IOS')
--------------------------------------------------------------------
// 当调用函数时,没有给出 y 的新值,所以 y 以默认值 'World'呈现
// 当给定了实际值 'IOS'时,由于'IOS'不严格等于undefined,所以 y = 'IOS'
- 另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,
参数默认值是惰性求值
1的。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
与结构赋值默认值的结合使用
function deconstruct({name='', grade='six', parent={}} = {}){}
function deconstruct1({name='', grade='six', parent={}}){}
deconstruct() // name='', grade='six', parent={}
deconstruct1() // 报错 => 不能进行结构赋值
--------------------------------------------------------------------
// 第一种情况与第二种情况的区别在于前者给定了解构的默认值为一个空对象{}
// 后者没有默认值,当没有赋值时既没有默认值也没有实际值,所以解构失败
参数默认值的位置
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 报错
f(undefined, 1) // [1, 1]
函数的 length 属性
// argument.length = 调用时给定参数的个数
function len2(a, b=1){
console.log(arguments.length) // 3
console.log(len2.length) // 1
}
len2(1,2,3)
--------------------------------------------------------------------
function len2(a=1, b, c){
console.log(arguments.length) // 3
console.log(len2.length) // 0
}
len2(1,2,3)
--------------------------------------------------------------------
// 区别于 arguments.length, 函数的 length 指的是函数定义时参数的长度,
// 而 arguments.length 指的是函数调用时传入参数的个数,不受定义的影响
// 函数的 length 还有一个特别的地方就是将返回没有指定默认值的参数个数。
//也就是说,指定了默认值后,length属性将失真。
//如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
作用域
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
--------------------------------------------------------------------
let x = 1;
function f(x, y = x) {
let x = 2;
console.log(y);
}
f(2) // 2
--------------------------------------------------------------------
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 报错 : x 未定义
--------------------------------------------------------------------
// 上面的三个函数主要想向大家展示的是在函数定义参数的时候,他本身构成一个
// 局部作用域。 => 函数 f调用时,参数 y = x 形成一个单独的作用域。
// 这个作用域里面,变量 x 本身没有定义,所以指向外层的全局变量 x。
// 函数调用时,函数体内部的局部变量 x 影响不到默认值变量 x。
rest 参数
function len(...r){
console.log(arguments.length) // 3
console.log(r) // [2, 3, 4]
console.log(len.length) // 0
}
len(2,3,4)
--------------------------------------------------------------------
// 注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
// function(...r, a) => 报错!!!
// 函数的length属性,不包括 rest 参数。
箭头函数(比较重要)
- ES6 允许使用“箭头”(=>)定义函数。
- 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
--------------------------------------------------------------------
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
--------------------------------------------------------------------
//如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sum = (num1, num2) => { return num1 + num2; }
var one = '外部/全局'
var person = {
one:'内部',
a1:function(){
setTimeout(() => console.log(this.one), 1000);
}
}
person.a1() //"内部"
//此时的 this绑定在函数 a1上
-------------------------------------------------------------------
var one1 = '外部/全局'
var person1 = {
one1:'内部',
a2:function(){
setTimeout(function(){console.log(this.one1)},1000);
}
}
person1.a2() // "外部/全局"
// 此时的 this 绑定在 window 即全局上,因为 settimeout函数是在指定的毫秒数后调用函数或计算表达式
// 它由 window 调用,所以 this 指向 window
---------------------------------------------------------------------
//箭头函数虽然写法上比普通函数要简单的多,但是当我们在使用this的时候要注意
(1)
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)
不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)
不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
const cat = {
lives: 9,
jumps: () => {
cat.lives--; // 8
this.lives-- //9
}
}
cat.jumps() // 这段代码没有执行,this并不指向 对象 cat
console.log(this.lives) // NAN => 找不到
--------------------------------------------------------------------
// 这段代码起不到作用,这是因为对象不构成单独的作用域,导致jumps箭头函数
//定义时的作用域就是全局作用域。
尾调用优化
- 尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
- 他能够大幅度节省我们内存的使用从而提升我们代码运行的速率。
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
// 这里的 m(), n()都属于尾调用
具体的应用在于我们的递归函数
ES6的尾调用优化只在严格模式下开启,正常模式是无效的。
//斐波那契数列的计算
'use strict'
function add(n, a=0, b=1){
if(n == 1) return b;
retturn add(n-1, b, a+b)
}
// 1 1 2 3 5 7........ 斐波那契数列
// 上面的做法就可以很大程度地优化我们的内存了。
表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值 ↩︎