给函数参数赋默认值
function log(x,y="hello") {
console.log(x,y);
}
log("hai~","大妹纸");
log('hi');
log('hi',"");
log();
function Point(x,y) {
this.x = x;
this.y = y;
}
const p = new Point(10,10);
console.log(p);
参数变量是默认声明的,因此不能用let const再次声明
function foo(x = 5) {
console.log(x);
var x = 1;
console.log(x);
// let x = 2;//Identifier 'x' has already been declared
}
foo();
使用参数默认值时,函数不能有同名参数
// 不报错
function foo1(x, x, y) {
// ...
}
// 报错
function foo2(x, x, y = 1) {
...
}
// SyntaxError: Duplicate parameter name not allowed in this context
参数默认值是惰性求值的
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo()
x = 100;
foo()
参数默认值与解构赋值默认值联合使用
function foo({x,y=5}) {
console.log(x,y)
}
foo({});
foo({x:2});
// foo();
函数参数为空时 设置默认值为空对象 对象属性为空时 默认值为设定值
function foo1({x,y=5}={}) {
console.log(x,y)
}
foo1({});
foo1({x:2});
foo1();
下面两个方法的不同之处
function m1({x = 0,y=0}={}) {
console.log(x,y)
}
function m2({x,y}={x:0,y:0}) {
console.log(x,y)
}
m1();
m2();
m1({x:1});
m2({x:1})
// m1方法设置参数默认值为空对象 且利用了对象的解构赋值
// m2方法仅设置了参数默认值为一个固定对象 未设置对象的解构赋值
定义默认值的参数应该是函数的尾参数, 如果非尾部的参数设置默认值,实际必须传入undefined 否则会报错
function f1(x=1,y) {
console.log(x,y);
}
// f1(undefined,2);
function f2(x,y,z=2) {
console.log(x,y,z);
}
f2(2,3);
函数的length属性在设置默认参数的情况下 返回为指定默认值的参数个数 length属性的含义:该函数预期传入的参数个数
console.log((function (a) {}).length )
console.log((function (a = 5) {}).length )
rest参数不会计入length属性,如果指定默认值的参数不是尾参数,那么length属性不再计入其后的参数个数
console.log((function (a = 0, b, c) {}).length);
console.log((function (a, b = 1, c) {}).length);
设置了函数默认值,进行函数初始化时会形成一个单独的作用域,初始化结束后,该作用域会消失 不设置函数参数默认值时不存在该行为
let x = 1;
function f(x,y=x) {
console.log(y)
}
f(2);
function f3(y=x) {
let x = 3;
console.log(y);
}
f3();
function f4(x,y=function (){x = 2}) {
var x = 3;
y();
console.log(x);
}
f4();
console.log(x)
function f5(x,y=function (){x = 2}) {
x = 3;
console.log(x);
y();
console.log(x);
}
f5();
rest参数用于获取函数多余参数 代替arguments对象 rest参数是真正数组 可直接使用数组方法 arguments对象是类数组 需使用Array.prototype.slice.call()方法转换为真正的数组
function add(...values) {
var sum = 0;
values.forEach((item)=>{
sum += item;
})
return sum;
}
console.log(add(1,2,3));
上面的方法用arguments变量写的话
function add() {
let args = Array.prototype.slice.call(arguments);
let sum = 0
args.forEach((item)=>{
sum += item;
})
return sum;
}
console.log(add(1,2,3))
注:rest参数之后不能再有其他函数参数,否则会报错 且函数的length属性不包括rest参数
// 报错
function f(a, ...b, c) {
...
}
es5开始函数内部可以设定为严格模式 函数体第一行 ‘use strict’
function f6() {
"use strict";
}
es6 规定 使用了默认参数、解构赋值、扩展运算符,函数内部就不能设置为严格模式,否则会报错 Illegal 'use strict' directive in function with non-simple parameter list
function f7(x,y=0) {
// "use strict";
}
因为函数的严格模式适用于函数参数和函数体 执行顺序是先执行函数参数再执行函数体,但是否使用严格模式是在函数体中决定的,因此产生矛盾 规避方法如下
设置全局性的严格模式
"use strict"
function f8() {
}
把函数放在无参数的立即执行函数中
(function () {
"use strict";
function f9() {
}
})()
函数的name属性,返回函数的名字 对某些特殊情况es5和es6返回值不一样
匿名函数的name值 es5返回空字符串“” es6返回匿名函数赋值的变量名
let a = function () {
}
console.log(a.name);
具名函数赋值给变量后 函数的name属性返回函数本来的名字
let b = function c() {
}
console.log(b.name)
Function构造函数实例 name值为anonymous
console.log(new Function().name)
bind返回的函数 name值为加入bound前缀
function a() {
}
console.log(a.bind(this).name)
箭头函数 参数大于1个或者没有参数时箭头前用圆括号 函数体语句大于1条时用{}包裹 并使用return语句
let a = (num1,num2) => num1+num2;
console.log(a(11,22))
如果箭头函数返回的是一个对象 必须在对象外加() 否则会被解释为代码块标识
let a = (a,b) => ({a:a,b:b});
console.log(a(10,20));
箭头函数与结构赋值联合使用
const full = ({first,last})=> first+","+last
console.log(full({first:"再见",last:"你好"}));
常用方法示例
const isEven = num => num%2===0 ;
const squre = num => num*num;
[1,2,3].map(item=>item*item);
[3,5,2,4,1].sort((a,b)=> a-b);
const arr = (...nums) => nums;
const headAndTail = (head,...tail) => [head,tail];
箭头函数注意点:
1.函数内部的this指向函数定义生效时所在的对象箭头函数内部没有this 不能用call()bind()apply()改变this的指向 2.不可以做构造函数 不能使用new命令
3.不能使用arguments对象 可用rest参数代替
4.不能使用yield 命令 不能做Generator函数
function abc() {
setTimeout(()=>{console.log(this.id)},100)
}
var id = 21;
abc();
abc.apply({id:42});
function Timer() {
this.s1 = 0;
this.s2 = 0;
setInterval(()=>this.s1 ++,1000);
setInterval(function () {
this.s2 ++;
},1000)
}
let timer = new Timer();
setTimeout(() => console.log(timer.s1),3001);
setTimeout(() => console.log(timer.s2),3001);
双冒号运算符 将运算符右边的方法绑定到运算符左边的对象上 如果::运算结果是一个对象,可采用链式写法
foo::bar1;
foo::bar2(...argument);
::obj.foo
尾调用是指 函数的最后一步是调用某个方法
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
尾调用优化:用内层函数的调用帧取代外层函数的调用帧 ,只保留内层函数的调用帧,节省内存空间 只有不在使用外层函数的内部变量,才能进行尾调用优化
递归是指函数调用自身 尾递归是指函数的最后一步调用自身方法
// 阶乘函数
function factorial(n) {
if(n ===1)return 1;
return n*factorial(n-1);
}
console.log(factorial(3));
//改写成尾递归 只保留一个调用记录
function factorial1(n,total) {
if(n ===1) return total;
return factorial1(n-1,n*total)
}
改写递归函数 将函数内部用到的变量改写成函数参数 尾调用优化和尾递归优化都只有在严格模式下才生效