网站

函数 - JavaScript | MDN

this - JavaScript | MDN

引入

函数写好之后,不会自动执行,必须通过调用才会运行。

不同的调用方式,最明显的区别是:函数什么时候执行,以及函数里的 this 指向谁。

正文

定义

定义

函数调用方式,就是让函数执行的不同写法。调用方式会影响函数的执行时机、参数传递,以及 this 的指向。

函数本质上是把一段代码封装起来,需要时再调用。

function sayHello() {
  console.log("你好");
}
 
sayHello();

这里的 sayHello() 就是在调用函数。

普通函数调用

普通函数调用是最常见的写法,直接使用 函数名()

function add(a, b) {
  return a + b;
}
 
const result = add(1, 2);
 
console.log(result);

ab 是参数,12 是调用函数时传进去的数据。

return 后面的内容是函数执行后交回来的结果。

函数表达式调用

函数也可以保存到变量里,再通过变量名调用。

const sayName = function (name) {
  console.log("我的名字是" + name);
};
 
sayName("Tom");

这里的 sayName 保存的是一个函数。

调用时仍然使用 sayName()

箭头函数调用

箭头函数也是函数的一种写法,调用方式和普通函数一样。

const add = (a, b) => {
  return a + b;
};
 
console.log(add(3, 5));

如果函数体只有一行返回值,也可以简写。

const add = (a, b) => a + b;
 
console.log(add(3, 5));

初学阶段可以先用完整写法,方便看清楚 return 的作用。

对象方法调用

对象里的函数通常叫做方法。

通过 对象名.方法名() 调用。

const user = {
  name: "Tom",
  sayName: function () {
    console.log(this.name);
  }
};
 
user.sayName();

这里的 sayNameuser 对象的方法。

user.sayName() 表示通过 user 调用这个方法。

在这种调用方式里,this 通常表示当前调用这个函数的对象,也就是 user

构造函数调用

构造函数通常配合 new 使用,用来创建对象。

function User(name, age) {
  this.name = name;
  this.age = age;
}
 
const user = new User("Tom", 18);
 
console.log(user.name);

new User("Tom", 18) 会创建一个新对象。

构造函数里的 this 指向这个新创建的对象。

构造函数名字通常首字母大写,例如 UserPerson

事件回调函数调用

事件里的函数不是我们手动立刻调用,而是浏览器在事件发生时调用。

<button id="btn">点击</button>
const btn = document.querySelector("#btn");
 
btn.addEventListener("click", function () {
  console.log("按钮被点击了");
});

这里没有写成 function () {}(),而是把函数交给 addEventListener()

当用户点击按钮时,浏览器会自动执行这个函数。

定时器回调函数调用

定时器里的函数也是回调函数,由浏览器在指定时间后调用。

setTimeout(function () {
  console.log("1 秒后执行");
}, 1000);

1000 表示 1000 毫秒,也就是 1 秒。

这段代码不是立刻输出,而是等待时间到了再执行函数里的代码。

立即执行函数

立即执行函数是定义后马上调用的函数。

(function () {
  console.log("马上执行");
})();

这种写法以前常用来创建一个独立作用域,避免变量污染外部。

现在学习阶段不需要经常使用,先看懂即可。

使用 call() 调用

call() 可以手动调用函数,并指定函数里的 this

function sayName() {
  console.log(this.name);
}
 
const user = {
  name: "Tom"
};
 
sayName.call(user);

这里 sayName.call(user) 表示调用 sayName(),并让函数里的 this 指向 user

使用 apply() 调用

apply()call() 类似,也可以指定 this

区别是:apply() 传参数时使用数组。

function add(a, b) {
  return a + b;
}
 
const result = add.apply(null, [1, 2]);
 
console.log(result);

这里不需要使用 this,所以第一个参数写 null

使用 bind() 生成新函数

bind() 不会立刻调用函数,而是返回一个绑定好 this 的新函数。

function sayName() {
  console.log(this.name);
}
 
const user = {
  name: "Tom"
};
 
const fn = sayName.bind(user);
 
fn();

bind() 更像是先准备好一个函数,后面需要时再调用。

常见写法

写法作用
sayHello();普通函数调用
add(1, 2);调用函数并传入参数
user.sayName();调用对象的方法
new User("Tom");使用构造函数创建对象
btn.addEventListener("click", fn);把函数作为事件回调交给浏览器
setTimeout(fn, 1000);把函数作为定时器回调交给浏览器
sayName.call(user);调用函数并指定 this
sayName.apply(user, args);调用函数并指定 this,参数用数组传
sayName.bind(user);返回一个绑定好 this 的新函数

例子

普通调用和返回值

函数执行后,可以通过 return 把结果交回来。

function getTotal(price, count) {
  return price * count;
}
 
const total = getTotal(10, 3);
 
console.log(total);

方法调用和 this

对象调用方法时,this 通常指向这个对象。

const student = {
  name: "Jack",
  showName: function () {
    console.log(this.name);
  }
};
 
student.showName();

事件中调用函数

点击按钮时,再执行 showMessage()

<button id="btn">显示消息</button>
const btn = document.querySelector("#btn");
 
function showMessage() {
  console.log("欢迎学习 JS");
}
 
btn.addEventListener("click", showMessage);

注意这里写的是 showMessage,不是 showMessage()

如果写成 showMessage(),函数会在绑定事件时就立刻执行。

特点

  • 函数必须调用后才会执行
  • 函数名() 表示立刻调用函数
  • 函数可以接收参数,也可以通过 return 返回结果
  • 对象方法调用时,this 通常指向调用这个方法的对象
  • new 调用构造函数时,this 指向新创建的对象
  • 事件回调和定时器回调不是立刻执行,而是交给浏览器在合适的时候执行
  • call()apply() 会立刻调用函数
  • bind() 不会立刻调用函数,而是返回一个新函数

注意事项

  • 函数名后面加 (),表示现在就执行
  • 把函数作为参数传递时,很多时候不要加 ()
  • return 后面的代码不会继续执行
  • 函数可以没有参数,也可以没有返回值
  • this 的值和函数在哪里定义关系不大,更重要的是函数怎么被调用
  • 箭头函数没有自己的 this,初学阶段不要随便用箭头函数写对象方法
const user = {
  name: "Tom",
  sayName: () => {
    console.log(this.name);
  }
};
 
user.sayName();

上面这种写法容易出问题,因为箭头函数里的 this 不是 user

对象方法初学阶段建议先使用普通函数写法。

const user = {
  name: "Tom",
  sayName: function () {
    console.log(this.name);
  }
};

理解

函数调用方式可以理解为:用不同方式让同一段代码执行

普通函数调用关注的是执行代码和拿到结果。

对象方法调用还要关注是谁在调用它,因为这会影响 this

事件和定时器里的函数不是自己马上执行,而是先交给浏览器,等用户操作或时间到了再执行。

call()apply()bind() 主要用于控制 this,初学时先知道它们的区别即可。

引出

理解函数调用方式之后,可以继续学习 JS this,重点理解不同调用方式下 this 的变化。

也可以结合 JS 事件JS DOM API,理解页面交互中函数是怎么被浏览器调用的。