函数相关

162 阅读6分钟

定义函数的方式(4种)

1. 函数声明

function 方法名("参数1", "参数2", ...) {}

2. 函数表达式

let 变量 = function("参数1", "参数2", ...) { };

3. 箭头函数

let 变量 = ("参数1", "参数2", ...) => {}

4. 使用Function构造函数(不推荐)

let 变量 = new Function("参数1", "参数2", ..., "函数体")

函数声明与函数表达式的区别

  • 以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的。
  • 函数声明在JS解析时存在函数声明提升,函数表达式不存在

箭头函数与普通函数区别及不适用场合

区别

1. 箭头函数的this指向

(1) 箭头函数没有prototype(原型),所以箭头函数本身没有this

// 箭头函数
let a = () => {};
console.log(a.prototype); // undefined

// 普通函数
function a() {};
console.log(a.prototype); // {constructor:f}

(2) 箭头函数this指向继承自外层第一个普通函数的this

function King() {
  this.name = 'Henry'
  setTimeout(() => {
    console.log(this.name);
  }, 10)
}
new King() // Henry

(3) 不能通过call、apply和bind直接修改箭头函数的this指向

// 外层不存在普通函数时,没法修改this指向,指向window
var id = 10;
let fun = () => {
  console.log(this.id)
};
fun();     // 10
fun.call({ id: 20 });     // 10
fun.apply({ id: 20 });    // 10
fun.bind({ id: 20 })();   // 10

// 外层存在普通函数时,去修改被继承的普通函数的this指向,然后箭头函数的this指向也会跟着改变
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 }); // id: 42

(4) 箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)

2. 箭头函数没有自己的arguments

(1) 箭头函数的this指向全局对象时,使用arguments会报未声明的错误

let b = () => {
  console.log(arguments);
};
b(1, 2, 3, 4); // Uncaught ReferenceError: arguments is not define

(2) 箭头函数的this指向普通函数时,它的arguments继承于该普通函数

function bar() {
  console.log(arguments); // ['外层第二个普通函数的参数']
  function bb() {
    console.log(arguments); // ["外层第一个普通函数的参数"]
    let a = () => {
      console.log(arguments, 'arguments继承this指向的那个普通函数'); // ["外层第一个普通函数的参数"]
    };
    a('箭头函数的参数'); // this指向bb
  }
  bb('外层第一个普通函数的参数');
}
bar('外层第二个普通函数的参数');

那么应该如何来获取箭头函数不定数量的参数呢?答案是:ES6的rest参数(...扩展符)

rest参数获取函数的多余参数

这是ES6的API,用于获取函数不定数量的参数数组,这个API是用来替代arguments的,API用法如下:

let a = (first, ...abc) => {
  console.log(first, abc); // 1 [2, 3, 4]
};
a(1, 2, 3, 4);

rest参数有两点需要注意:

  • rest必须是函数的最后一位参数:
let a = (first, ...rest, three) => {
  console.log(first, rest,three); // 报错:Rest parameter must be last formal parameter
};
a(1, 2, 3, 4);
  • 函数的length属性,不包括rest参数
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

3. 使用new调用函数会报错(也就是箭头函数不能作为构造函数使用)

无论箭头函数的this指向哪里,使用new调用箭头函数都会报错,因为箭头函数没有constructor

let A = () => {};
let b = new A(); // A is not a constructor

4. 箭头函数不支持new.target

new.tartget是ES6新增的属性,用于检测函数是否使用new关键字调用的。

如果函数是正常调用的,则new.target的值是undefined; 如果是使用new关键字调用的,则new.target将返回被调用的构造函数的引用。

  • 箭头函数的this指向全局对象,在箭头函数中使用箭头函数会报错
let a = () => {
  console.log(new.target); // 报错:new.target 不允许在这里使用
};
a();
  • 箭头函数的this指向普通函数,它的new.target就是指向该普通函数的引用。
function B() {
  let a = () => {
    console.log(new.target); // 指向函数B:function B(){...}
  };
  a();
}

new B()

5. 箭头函数不支持重命名函数参数,普通函数支持重命名函数参数

如示例所示,普通函数支持重命名函数参数,后面出现的会覆盖前面的,箭头函数会抛出错误

function func1(a, a) {
  console.log(a, arguments); // 2 [1,2]
}

let func2 = (a,a) => {
  console.log(a); // 报错:在此上下文中不允许重复参数名称
};
func1(1, 2); func2(1, 2);

6. 箭头函数相对于普通函数语法更简洁优雅

  • 箭头函数都是匿名函数,并且都不用写function
  • 只有一个参数的时候可以省略括号
let f = a => a; // 传入a 返回a
  • 函数只有一条语句时可以省略{}和return
let f = (a, b, c) => a;
  • 简化回调函数写法,让你的回调函数更优雅
[1,2,3].map(function (x) {
  return x * x;
}); // 普通函数写法 
[1,2,3].map(x => x * x); // 箭头函数只需要一行

7. 箭头函数不能用作Generator函数,不能使用yield关键字

总结:

  • 箭头函数没有prototype(原型),所以箭头函数本身没有this
  • 箭头函数的this在定义的时候继承自外层第一个普通函数的this。
  • 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)
  • 箭头函数本身的this指向不能改变,但可以修改它要继承的对象的this。
  • 箭头函数的this指向全局对象时,使用arguments会报未声明的错误。
  • 箭头函数的this指向普通函数时,它的argumens继承于该普通函数
  • 使用new调用箭头函数会报错,因为箭头函数没有constructor
  • 箭头函数不支持new.target
  • 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
  • 箭头函数相对于普通函数语法更简洁优雅

箭头函数不适用场合

1. 第一个场合是定义对象的方法,且该方法内部包括this

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。

调用cat.jump()时,如果是普通函数,该方法内部的this指向cat;但写成上面那样的箭头函数,this便指向全局对象,因此不会得到预期结果。

2. 第二个场合是需要动态this的时候,也不应该使用箭头函数

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

参考:juejin.cn/post/684490…