一、函数默认值特殊情况
(1)length
函数一旦给了默认值,length会发生变化,体现在默认值所给的位置
function test(a, b, c) {}
test();
console.log(test.length); //输出3
function test(a, b, c = 1) {}
test();
console.log(test.length); //输出2
function test(c = 1, a, b) {}
test();
console.log(test.length); //输出0
function test(a, b, c = 1, d, e, f) {}
test();
console.log(test.length); //依然是2
发现length不会计算给默认值的形参及后面的形参
(2)映射关系
函数一旦给了默认值,实参和形参的映射关系就会失效
function test(a, b, d, e, f) {
arguments[1] = 7;
console.log(b); // 输出7 形参和实参是映射的关系
}
test(1, 2, 3, 4, 5, 6)
function test(a, b, d, e, f = 1) {
arguments[1] = 7;
console.log(b); // 输出2 一旦给了默认值就会造成映射失效
}
test(1, 2, 3, 4, 5, 6);
(3)形参解构
-
如果解构没有匹配到,那么赋值为默认参数
-
如果解构匹配到了,那么赋值为匹配上的值
-
如果解构undefined,那么就会报错
function foo({x, y = 5}){
console.log(x, y);
}
foo({}); //undefined 5
foo({x:1});//1 5
foo({x:1,y=2})//1 2
foo();//报错
// 解决报错
function foo({x, y = 5}={}){
console.log(x, y);
}
foo();//undefine 5 如果没有传值为undefined,就会找默认值进行解构
(4)fetch请求
如果没有传配置对象,那么就会与默认值{}进行模式匹配,从而避免报错
function fetch(url,{body = "",method = "get"} = {})
fetch('http://www.baidu.com'); // 配置对象就应用了默认值
(5)默认值作用域
调用栈
函数在调用的时候会创建一个调用栈(call stack),这个调用栈记录着函数所调用的位置(可以借助控制台的source工具查看)
y函数里的x赋值给了形参x
var x = 1;
function foo(x, y = function() { x = 2; console.log(x); }) { // 这里是外层作用域,函数y也产生了作用域,它与外层形成了闭包
var x = 3; // 这里是里层作用域
y();
console.log(x);
}
foo();
console.log(x);
// 输出2 3 1
// 模拟
var x = 1; {
let x = 2;
let y = function() {
x = 2;
console.log(x)
}; {
let x = 3;
y();
console.log(x);
}
}
console.log(x);
// 输出 2 3 1
二、this指向问题
(1)规律
-
默认绑定规则:函数内部this默认指向window、严格模式下没有this指向
-
隐式绑定:谁调用指向谁,全局调用指window
-
显示绑定:call(obj,a,b,c)、apply(obj,[a,b,c])、bind(obj,a,b,c),前两个是函数执行,后者是函数本身
-
new 构造函数最后一步this指向实例对象
优先级:4 > 3 > 2 > 1
三、箭头函数基本形式
(1)基本形式
-
基本形式
() => {}
-
参数只有一个
a => a; // 如果形参只有一个省略() 、返回值只有一个表达式可以省略{}
-
没有参数的情况
() => a; // 没有形参必须有()
-
简写返回对象
(a, b) => ({a:1, b:2}); // 不包括号会报错, JS认为是语法块
(2)与解构赋值来结合
const full = ({first,last} = {} ) => first +''+last;
console.log(full({first:3,last:5}))//输出3,5
(3)简化排序
var arr = [123,12,31,23,1,4,4213,3213,43];
var arr1 = arr.sort((a,b) => a - b)
// 即可排序
(4)不存在arguments
var sum = (a, b) => {
console.log(arguments); // 报错
return a + b;
}
sum(1, 2)
四、rest运算符
… spread / rest 运算符:展开或收集
(1)收集
形式:… + 变量
//将值收集为数组
var sum = (...args) =>{
log(args[0]+args[1]); // 3
}
sum(1,2)
//收集必需是最后一个参数
let fn =(a,b,...c) =>{
console.log(a,b,c); // 1,2,3,[4,5,6,7]
}
fn(1,2,3,4,5,6,7)
(2)展开
形式:… + 数组
//将数字展开为各个值
function foo(x, y, z){
console.log(x, y, z) // 1 2 3
}
foo(...[1,2,3])
foo(...[1,2,3,3,4,5]) //不影响
//有点类似apply, 也是以数组传参
function foo(x, y, z){
console.log(x, y, z) // 1 2 3
}
foo.apply(undefined,[1,2,3,4,5]); // 让this指向不发生改变
foo.apply(null,[1,2,3,4,5]);
//特殊用法
let a = [2, 3, 4];
let b = [1,...a,5];
// [1,2,3,4,5]
(3)简化排序
// es5
function sortNum() { //这里slice起到把类数组转成数组的作用
return Array.prototype.slice.call(arguments).sort(function(a, b) { return a - b });
}
console.log(sortNum(12, 12, 34, 5, 22, 56, 7, 78));
// es6
const sortNum = (...args) => args.sort((a, b) => a - b);
console.log(sortNum(12, 12, 34, 5, 22, 56, 7, 78));
(4)length
函数形参长度不包括rest运算符和默认值
log((function(a){}).length);//1
log((function(...a){}).length);//0
log((function(b,...a){}).length);//1
log((function(b,c,...a){}).length);//2
log((function(b,c,...a,d=0){}).length);//报错
log((function(b,...a,a=0){}).length);//1 length不再准确