基本语法:
普通函数 function
function fc(a1,b2,...,pnan){
sumnews;
}
即格式为:
funtion 函数名(参数列表) {
语句; return 表达式
}
2.箭头函数
// 当只有一个参数时,圆括号是可选的
(singleParam) => { statements }
singleParam => { statements }
// 没有参数的函数应该写成一对圆括号
() => { statements }
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
//相当于:(param1, param2, …, paramN) =>{ return expression; }
上述基本语法可以概括为:
- 如果只有一个形参,则可以省略圆括号();
- 如果没有形参,则必须写成一对圆括号();
- 如果函数体只有一行,则可以省略花括号{},且同时,省略return,函数的返回值为该条语句的执行结果;
看列子:
箭头函数
let f =v=>console.log('',v);
f(20)
//v:20
普通函数:
let fn =function(v){
console.log('v:',v);
}
fn(30)
// v:30
箭头函数的概念:
箭头函数内部的this是词法作用域(块级作用域),由上下文确定。(词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变 。)
箭头函数的this指向:
概念:
对于普通函数来说,内部的
this
指向函数运行时所在的对象,但是这一点对箭头函数不成立。它没有自己的
this
对象,内部的
this
就是定义时上层作用域中的
this
。也就是说,箭头函数内部的
this
指向是固定的,相比之下,普通函数的
this
指向是可变的
let fn = () => {
console.log('this',this);}
fn()
//结果 window
**列子如下**:**Timer**
函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的
this
绑定定义时所在的作用域(即
Timer
函数),后者的
this
指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,
timer.s1
被更新了 3 次,而
timer.s2
一次都没更新。
箭头函数实际上可以让
this
指向固定化,绑定
this
使得它不再可变,这种特性很有利于封装回调函数。
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
列子:DOM 事件的回调函数封装在一个对象里面。
var handler = {
id: '123456',
init: function() {
document.addEventListener('click',
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};
上面代码的
init()
方法中,使用了箭头函数,这导致这个箭头函数里面的
this
,总是指向
handler
对象。如果回调函数是普通函数,那么运行
this.doSomething()
这一行会报错,因为此时
this
指向
document
对象。
总之,箭头函数根本没有自己的
this
,导致内部的
this
就是外层代码块的
this
。正是因为它没有
this
,所以也就不能用作构造函数。
以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:
arguments
、
super
、
new.target
。
function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}
foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]
上面代码中,箭头函数内部的变量
arguments
,其实是函数
foo
的
arguments
变量。
另外,由于箭头函数没有自己的
this
,所以当然也就不能用
call()
、
apply()
、
bind()
这些方法去改变
this
的指向。
箭头函数没有 prototype 属性:
var Fn = () => {};
console.log(Fn.prototype);
箭头函数不绑定arguments
function fn(){
console.log(arguments ,'普通函数');
}fn(1,2,3,4,5 )
let fn2=(...num)=>console.log(...num,'箭头函数');
fn2(1,2,3,4,5)
let obj1 = {
a: 10,
// 匿名函数 function
b: function () {
console.log("obj1.b的this:",this);
console.log("obj1.b的this.a:",this.a);
},
// 箭头函数
c: () => {
// this继承父层的this
console.log("obj1.c的this:",this);
console.log("obj1.c的this.a:",this.a);
},
};
obj1.b()
obj1.c()
在对象的方法中分别使用匿名函数function和箭头函数,对象调用该方法时,匿名函数的this指向被其调用的对象obj1,箭头函数的this指向父层的this,由于该代码是写在 Vue 项目中,因此this指向组件实例,在组件实例中,并没有变量a,因此this.a是undefined。
箭头函数不能用作Generator函数
箭头函数内部不可以使用yield命令,因此箭头函数不能用作Generator函数。
let fn = function *() {
yield '萱不是渲';
}
let p = fn();
console.log(p.next());
let fn = *() => {
yield '渲不是萱';
}
let p = fn();
console.log(p.next());
不适用场合
由于箭头函数使得
this
从“动态”变成“静态”,下面两个场合不应该使用箭头函数。
第一个场合是定义对象的方法,且该方法内部包括
this
。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面代码中,
cat.jumps()
方法是一个箭头函数,这是错误的。调用
cat.jumps()
时,如果是普通函数,该方法内部的
this
指向
cat
;如果写成上面那样的箭头函数,使得
this
指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致
jumps
箭头函数定义时的作用域就是全局作用域。
再看一个例子。
globalThis.s = 21;
const obj = {
s: 42,
m: () => console.log(this.s)
};
obj.m() // 21
上面例子中,
obj.m()
使用箭头函数定义。JavaScript 引擎的处理方法是,先在全局空间生成这个箭头函数,然后赋值给
obj.m
,这导致箭头函数内部的
this
指向全局对象,所以
obj.m()
输出的是全局空间的
21
,而不是对象内部的
42
。上面的代码实际上等同于下面的代码。
globalThis.s = 21;
globalThis.m = () => console.log(this.s);
const obj = {
s: 42,
m: globalThis.m
};
obj.m() // 21
由于上面这个原因,对象的属性建议使用传统的写法定义,不要用箭头函数定义。
第二个场合是需要动态
this
的时候,也不应使用箭头函数。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面代码运行时,点击按钮会报错,因为
button
的监听函数是一个箭头函数,导致里面的
this
就是全局对象。如果改成普通函数,
this
就会动态指向被点击的按钮对象。
另外,如果函数体很复杂,有许多行,或者函数内部有大量的读写操作,不单纯是为了计算值,这时也不应该使用箭头函数,而是要使用普通函数,这样可以提高代码可读性。
箭头函数的注意点
(1)箭头函数没有 this ,普通函数的 this 指向依赖它是如何被调用的
(2)不可以当作构造函数,也就是说,不可以对箭头函数使用
new
命令,否则会抛出一个错误。
(3)不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用
yield
命令,因此箭头函数不能用作 Generator 函数。
版权归原作者 萱不是渲 所有, 如有侵权,请联系我们删除。