ES6随笔--各数据类型的扩展--函数
Posted muting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6随笔--各数据类型的扩展--函数相关的知识,希望对你有一定的参考价值。
ES6随笔--各数据类型的扩展(3)--函数
1. 函数参数的默认值
可以在函数参数中指定默认值,函数内不能再用let
或const
声明;
使用参数默认值时,不能出现同名参数;
参数默认值惰性求值,每次调用函数会重新计算参数默认值表达式;
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
可以与解构赋值默认值结合使用:
function ({x, y = 5} = {}) {
console.log(x, y);
}
function(); // undefined 5
// 以下对比默认值设置不同方式
function bar({x, y} = {x:0, y:0}) {
console.log(x, y);
}
function bar1({x=0, y=0} = {}) {
console.log(x, y);
}
bar(); //0 0
bar1(); //0 0
bar({x:2, y:3}); //2 3
bar1({x:2, y:3}); //2 3
bar({x:2}); // 2 undefined
bar1({x:2}); // 2 0
bar({y:2}); // undefined 2
bar1({y:2}); // 0 2
bar({z:4}); // undefined undefined
bar1({z:4}); // 0 0
bar({}); // undefined undefined
bar1({}); // 0 0
设置了默认值的参数一般是函数的尾参数,该参数可以省略;如果位于中间,则无法只省略该参数继续为后面的参数赋值,必须要设置为undefined
,否则直接省略而为后面的参数赋值会报错;
undefined
可以触发默认值;
函数的length
属性中不包含指定了默认值的参数。
指定了参数默认值的函数,函数进行声明初始化时该参数会形成单独的作用域;函数执行完毕后该作用域消失;
// 函数内部 let 声明的变量与参数变量不同作用域
var x = 1;
function foo(y = x) {
let x = 2;
console.log(y);
}
foo() // 1
// 参数的作用域与全局和函数内部都不同;
var x = 1;
function foo(x, y = x) {
x = 2;
console.log(y);
}
foo(); // undefined
foo(2); // 2
console.log(x); // 1
// 参数设置默认值时指向全局变量x, 函数内部修改了全局变量,但并不影响参数y--不在同一个作用域;
var x = 1;
function foo(y = x) {
x = 2;
console.log(y);
}
foo(); // 1
console.log(x); // 2
// 暂时性死区报错(参数默认值相当于 let x = x,此时会忽略全局里的定义)
var x = 1;
function foo(x = x) {
console.log(x);
}
foo() // ReferenceError: x is not defined
函数的参数默认值可以用来设置哪些参数不能省略;
2. rest函数
形式为...varialbleName
,获取函数的多余参数放入数组中;
// 改写 push 方法的例子
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
return array.length;
}
var a = [];
console.log(push(a, 1, 2, 3)); // 1 2 3 3
rest
参数只能是最后一个参数;函数的length
属性,不包含rest
函数;
3. 严格模式
只要函数参数使用了默认值,解构赋值或者扩展运算符,函数内部就不能显式设定严格模式;
替代的办法 1) 使用全局性的严格模式;2) 使用立即执行的无参函数,使用严格模式并返回该函数;
4. name 属性
函数的name
属性,返回该函数的函数名;
如果把匿名函数赋值给一个变量,匿名函数的name
属性返回该变量名(ES5 则为空字符串);
如果把一个具名函数赋值给一个变量,该函数的name
属性返回原本的函数名。
const f = function () {};
console.log(f.name); // f
const f1 = function func() {};
console.log(f1.name); // func
Function
构造函数返回的函数实例, name
属性返回anonymous
;
使用bind()
返回的函数,name
属性会自动在函数名前面加bound
;
5. 箭头函数
使用箭头=>
定义函数;funcName(args) => {code block}
与function funcName(args) = {code block}
相同;
注意:
1. 箭头函数中的this
绑定函数定义时所在的作用域,而不是运行时的作用域;
2. 不能使用new
命令;
3. 不可以使用argumets
对象,可以使用rest参数代替;
4. 不可以使用yield
命令,因此箭头函数不能用作Generator
函数;
箭头函数本身没有自己的this
,在定义时指向外部作用域的对象;也不能通过call
,bind
,apply
改变this
的指向。
var f = function () {console.log(this.id);}
var f1 = () => {console.log(this.id)};
f.call({id:11}); // 11
f.call({id:22}); // 22
f1.call({id:11}); // undefined
f1.call({id:22}); // undefined
// 大括号被解释为代码块,所以需要返回一个对象的情况下需要在大括号外加小括号;(再加一层大括号无效);
var f = (x) => {a:x};
console.log(f(3)); // undefined
var f = (x) => ({a:x});
console.log(f(3)); // a : 3
嵌套函数;
部署管道机制;
改写λ演算;
6. 双冒号运算符(提案)
用来取代bind
, apply
, call
的作用,绑定对象作用域;
// 将左边对象,作为上下文环境(即this 对象),绑定到右边的函数上
object::function
// 双冒号左边为空,右边为一个对象的方法,代表将此方法绑定到该对象上;
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
7. 尾调用优化
尾调用(Tail Call)函数的最后一步是调用另一个函数。
// 这种情况不属于尾调用,相当于调用g(x)后返回undefined;‘return g(x)’算是一种尾调用。
function f(x){
g(x);
}
在函数内部调用其他函数时,所有当前被调用而未结束的函数的调用帧会形成调用栈;
尾调用由于是最后一步操作,所以不需要保留外部函数的调用帧,因为调用位置、内部变量等信息都不会再用到。
尾调用优化,即只保留最后调用函数的调用帧。但是需要做到内部调用函数不再使用外部函数的内部变量;
函数尾调用自身,即尾递归;
//尾调用 Fibonacci数列案例
function fib(n) {
if (n<=1) return 1;
return fib(n-1)+fib(n-2);
}
function fib1(n, ac1=1, ac2=1) {
if (n<=1) return ac2;
return fib1(n-1, ac2, ac1+ac2);
}
console.log(fib(10)); //~20ms 是优化函数运行时间至少10倍;
console.log(fib1(10)); //~1ms
console.log(fib(100)); // 无法计算,栈溢出
console.log(fib1(100)); // <10ms
尾递归优化只能在严格模式下开启,正常模式无法执行;
尾递归优化在非严格模式下无效;实现方法可以使用循环代替:
// 非严格模式下实现尾调用优化--循环
// 递归函数
function test(x, y) {
if (y > 0) {
return test(x+1, y-1);
} else {
return x;
}
}
// 蹦床函数
function trampoline(fn) {
while (fn && fn instanceof Function) {
fn = fn();
}
return fn;
}
//改写递归函数
function test1(x, y) {
if (y > 0) {
return test1.bind(null, x+1, y-1);
} else {
return x;
}
}
// 通过蹦床函数调用递归函数
var a1 = trampoline(test1(1, 100000));
console.log(a1); // 100001
8. 函数参数的尾逗号
ECMA2017允许函数的最后一个参数后面有逗号;
附加个人随记:bind方法
bind
可以直接指定绑定的对象,以及对应的参数,返回绑定后的函数,后面加括号才会执行;
function testbind(str) {
return str + this;
}
var a1 = {name:"Bai"};
console.log(testbind.bind(a1));
// ? testbind(str) {
// return str + this;
// }
console.log(testbind.bind(a1,"huahua"));
// ? testbind(str) {
// return str + this;
// }
console.log(testbind()); //undefined[object Window]
console.log(testbind.bind(a1,"huahua")()); //huahua[object Object]
console.log(testbind.bind(a1)("huahua")); //huahua[object Object]
以上是关于ES6随笔--各数据类型的扩展--函数的主要内容,如果未能解决你的问题,请参考以下文章
三阶段课程——Day02(ES6简介let和const)(数据类型和数据结构:字符串扩展Symbol数组新增方法对象新增SetMap数组解构对象解构...运算符)