javascript
// 普通函数
function (){}
// 箭头函数
()=>{}
ES6 中允许使用 => 来定义函数,省去关键字 function
箭头函数与普通函数功能基本类似,但是写法简单、清晰
箭头函数相当于匿名函数,并简化了函数的定义
箭头函数一般当作函数表达式或回调函数去使用
一、特点
1、只有一个形参的时候,() 可以省略
javascript
// 只有一个形参
var test = (a)=>{
console.log(111,a)
}
// 省略 () 写法
var test2 = a =>{
console.log(111,a)
}
2、只有一条执行语句,且只返回某个变量或者js表达式,可以省略 {} 和 return
less
// 只有一个返回值
var test = (a)=>{
return 100*a
}
// 省略 {} 后的代码
var test2 = a=>100*a
3、如果只返回一个对象,可以把对象看作一个整体,用小括号括起来,当作一条语句,就可以省略大括号 和 return
javascript
const fn = ()=>{
return {
a:1,
b:2
}
}
const fn2 = ()=>({a:1,b:2})
4、没有 arguments
javascript
//通过形参获取参数
var test = function(a,b,c){
console.log(a,b,c)
}
test(1,2,3)
//通过函数内部的 arguments 属性获取函数传参
var test2 = function(){
console.log(arguments[0],arguments[1],arguments[2])
}
test2(1,2,3)
// 箭头函数访问 arguments
var test3 = ()=>{
console.log(arguments)
}
test3(1,2,3)

5、this 指向
箭头函数 this 指向父级作用域
箭头函数的 this 继承自它所在词法作用域的 this
函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="myInput">
<script>
const inputEl = document.getElementById('myInput')
inputEl.oninput = function(){
//1、 普通函数
// setTimeout(function(){
// // 此时的 this 指向 window
// console.log(this)
// }, 1000);
//2、 临时存储 this
// const _this =this
// setTimeout(function(){
// // 此时的 _this 指向 input 元素
// console.log(_this)
// }, 1000);
// 3、箭头函数
setTimeout(()=>{
// 此时的 this 指向 input 元素
console.log(this)
}, 1000);
}
</script>
</body>
</html>
javascript
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);
javascript
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
javascript
function debounce(func,duration=500){
let timer;
return function(...args){
clearTimeout(timer)
timer = setTimeout(()=>{
func.apply(this,args)
},duration)
}
}
function debounce2(func, duration = 500) {
let timer;
return function () {
const context = this; // 保存 this 指向
const args = arguments; // 获取参数
clearTimeout(timer);
timer = setTimeout(function () {
func.apply(context, args); // 使用 apply 保证 this 指向
}, duration);
};
}
scss
//请问下面的代码之中有几个this?
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()();
var t2 = f().call({id: 3})();
var t3 = f()().call({id: 4});
kotlin
var name="张三"
function fun(){
this.name="李四"
return ()=>{
console.log(this.name)
}
}
const obj = {
name:"王麻子",
fun2:fun()
}
const obj2 = {
name:"王麻子",
fun2:()=>console.log(this.name)
}
obj.fun2()
obj2.fun2()
typescript
<script lang="tsx">
import { SetupContext } from "vue"
interface TabItemProps {
label: string
value: string | number
}
interface IProps {
modelValue: string | number
tabs: TabItemProps[]
}
export default defineComponent({
props: {
modelValue: {
type: [String, Number] as PropType<string | number>,
default: "",
},
tabs: {
type: Array as PropType<TabItemProps[]>,
required: true,
},
},
emits: ["update:modelValue", "change"],
setup(_props: IProps, ctx: SetupContext) {
const tabAct = computed({
get() {
return _props.modelValue !== undefined ? _props.modelValue : _props.tabs.length ? _props.tabs[0]?.value : ""
},
set(value) {
ctx.emit("update:modelValue")
},
})
const handleTabClick = (item: TabItemProps) => {
tabAct.value = item.value
}
return () => {
return (
<div class="w-full flex">
<div class="tabs-box">
{_props.tabs.map((item: TabItemProps) => {
return (
<div
class={`tab-item ${item.value === tabAct.value ? "tab-active" : ""}`}
onClick={() => handleTabClick(item)}
>
{item.label}
</div>
)
})}
</div>
</div>
)
}
},
})
</script>
5、不可以使用new
命令
ini
var test = ()=>{}
var newTest = new test()

二、箭头函数与普通函数的区别
语法格式 | new 和 原型 | arguments super new.target | this 指向 | call、apply、bind | |
---|---|---|---|---|---|
箭头函数 | ()=>{} 函数表达式 | 没有 | 没有 | 一般是全局对象,被普通函数包含指向上一层 | 不可改变 this 的指向 |
普通函数 | function(){} 函数声明 函数表达式 | 有 | 有 | 动态 | 可修改 this 的指向 |
三、箭头函数为什么不能作为构造函数?
1、箭头函数没有自己的 this ,this 的值是在定义的时候确定的,而不是在运行的时候,它继承自父级作用域。而构造函数需要动态绑定 this,指向新创建的实例对象。
2、不能通过 new 调用,当通过 new 调用一个箭头函数时, javascript 引擎会直接报错。因为箭头函数没有构造函数的特性
- 构造函数必须有 prototype ,用于定义创建的实例对象的原型。
3、super 关键字不可用,而子类的构造函数通常需要使用 super() 调用父类的构造函数。
四、arguments、super、new.target
arguments
- 作用: arguments 是一个类数组对象,它包含了传递给函数的所有参数。
- 可用性: arguments 对象只在函数内部可用。
- 特点:
-
- 虽然 arguments 看起来像数组,但它不是真正的数组,它没有数组方法,例如 push、pop、slice 等。
- 你可以使用索引访问 arguments 中的参数,例如 arguments[0] 表示第一个参数。
- arguments 对象拥有一个 length 属性,表示传递给函数的参数个数。
javascript
function myFunction() {
console.log(arguments.length); // 输出参数个数
console.log(arguments[0]); // 输出第一个参数
}
myFunction(1, 2, 3);
super
- 作用: super 关键字用于访问父类的构造函数和方法。
- 可用性: super 关键字只在类的构造函数和方法内部可用。
- 使用场景:
-
- 在子类的构造函数中,你必须使用 super() 调用父类的构造函数,以初始化继承的属性。
- 在子类方法中,你可以使用 super.methodName() 调用父类的方法。
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
super.speak(); // 调用父类方法
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak();
new.target
- 作用: new.target 属性用于确定一个函数或构造函数是否使用 new 关键字调用。
- 返回值:
-
- 如果函数使用 new 调用,则 new.target 返回构造函数本身。
- 如果函数未使用 new 调用,则 new.target 返回 undefined。
sql
function myFunction() {
if (new.target) {
console.log('Called with new');
} else {
console.log('Called without new');
}
}
new myFunction(); // 输出: Called with new
myFunction(); // 输出: Called without new
scss
function myConstructor() {
if (!new.target) {
throw new Error('Must be called with new');
}
// ... 构造函数逻辑
}
new myConstructor(); // 正确调用
myConstructor(); // 抛出错误
五、使用注意点
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
六、不适用场景
- 定义对象的方法,且该方法内部包括
this
。
javascript
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
// 上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。
// 调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;
// 如果写成上面那样的箭头函数,使得this指向全局对象
// 因此不会得到预期结果。这是因为对象不构成单独的作用域
// 导致jumps箭头函数定义时的作用域就是全局作用域。
- 需要动态
this
的时候,也不应使用箭头函数。
javascript
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
// 上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,
// 导致里面的this就是全局对象。
// 如果改成普通函数,this就会动态指向被点击的按钮对象。
七、使用场景
- ES6数组方法(map、forEach、filter...)
- 不需要动态绑定 this 时
- 高阶函数
- 链式调用
- vue 组件 tsx 语法 事件绑定