我不允许你还不了解this、call、apply、bind

前言

大家好,我是热爱前端的菜鸟luckyCover。

this

this是JavaScript语言的一个关键字,它是函数运行时内部产生的一个对象,这个对象只能在函数内部使用

javascript 复制代码
function test() {
    this.x = 1;
}

上边函数运行时,函数内部会自动有一个this对象可以使用。

那么this的值是什么呢?也就是我们常说的this的指向

函数在不同场合下,this有不同的值。总的来说,this就是函数运行时所在的环境对象。

分以下几种情况:

情况一:纯粹的函数调用

这是最通常的写法,属于全局调用,因此this就代表了全局对象。

javascript 复制代码
let x = 1;
function test() {
    console.log(this.x);
};
test(); //1

情况二:作为对象的函数调用

函数还可以作为某个对象的方法调用,这时候this就指这个上级对象。

javascript 复制代码
function test() {
    console.log(this.x);
};
let obj = {};
obj.x = 1;
obj.m = test;
m(); //1

情况三:作为构造函数调用

所谓构造函数,其实就是new这个函数会产生一个新对象。如果这个函数使用new调用,this就指向这个新创建的对象。

javascript 复制代码
function test() {
    this.x = 1;
};
const obj = new test();
console.log(obj.x); //1

运行结果为1,为了证明此时的this不是全局对象,我们对代码做如下变动:

javascript 复制代码
let x = 2
function test() {
    this.x = 1;
};
const obj = new test();
console.log(x); //2

运行的结果没有变,证明全局变量x的值根本没变。

情况四:apply调用

apply()是函数中的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变调用这个函数的对象。因此,这时候this的指向就是接收的第一个参数

javascript 复制代码
let x = 0;
function test() {
    console.log(this.x);
};
const obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply(); //0

apply()参数为空,默认调用全局对象。因此这时的运行结果为0,证明this指向的是全局对象。

如果把最后一行代码修改为:

javascript 复制代码
obj.m.apply(obj); //1

运行结果就变为1了,证明此时this指向对象obj

根据上边四种情况,我们可以简单总结一下this指向了。

判断this关键字的引用(即指向)是谁的唯一方法就是看this所在的方法在哪里被调用。

接下来,我们介绍下callapplybind这三个关键字

call

call是每个函数都有的一个方法,它允许你在调用某个函数的时候为其指定上下文

比如下边这段代码:

javascript 复制代码
function greet() {
    alert(`Hello, my name is ${this.name}`);
};
const user = {
    name: 'Tyler',
    age: 27
};

greet(); //直接调用,此时this指向的是window,由于window上没有name属性,会输出undefined

根据上边的总结,我们都知道为了判断this的引用必须查看这个函数的调用位置。现在就引出一个问题,上面这段js代码中,如果直接调用greet()函数,那么此时的this指向的是全局对象window,然而现在我们要让greet()方法的调用对象是user,也就是让this指向user。这时候就可以用call方法了。

下面的代码可以实现在调用greet()方法时使用user做上下文。

javascript 复制代码
greet.call(user);

概况一下call属性call是每个函数都有的一个属性,传递给它的第一个参数会作为调用函数时的上下文。换句话说,this会指向传递给它的第一个参数。

apply

apply函数和call函数唯一的不同就是函数的第二个参数。

我们先来看看以.call调用的例子:

javascript 复制代码
function greet(lang1, lang2, lang3) {
  alert(`Hello, my name is ${this.name} and I know ${lang1}, ${lang2}, and ${lang3}`)
};

const user = {
  name: 'Tyler',
  age: 27
};

const languages = ['JavaScript', 'Ruby', 'Python'];

greet.call(user, languages[0], languages[1], languages[2]);

使用call方法调用奏效,它显示了如何将参数传递给.call调用的函数。不过,这样传递参数显然比较麻烦。如果我们需要传10个参数,那得一个一个在后边传递。幸运的是,apply()方法可以让我们以数组的形式传递参数,并且会自动将其展开

将上边代码改为使用.apply来调用:

javascript 复制代码
const languages = ['JavaScript', 'Ruby', 'Python']

greet.apply(user, languages);

相信看到这里的你已经get到callapply两者的区别了,没错,其实就是第二个参数的传递形式不同,我们先不进行总结,后边还有一个bind没看,看完你就能归纳出它们的异同点了。

bind

bind属性和call完全相同,它不会立即调用函数,而是返回一个能在以后调用的函数。看下这段代码:

javascript 复制代码
function greet(lang1, lang2, lang3) {
  alert(`Hello, my name is ${this.name} and I know ${lang1}, ${lang2}, and ${lang3}`)
};

const user = {
    name: 'Tyler',
    age: 27
};

const language = ['JavaScript', 'Ruby', 'Python'];
const newFn = greet.bind(user, languages[0], languages[1], languages[2]);
newFn(); //"Hello, my name is Tyler and I know JavaScript, Ruby, and Python"

看完上边代码,我们可以发现bindcall参数传递以及调用形式上是完全相同的,唯一区别就是在bind等号的左边可以接收一个新函数(即bind返回的那个函数),而这个函数正是绑定了this上下文的函数

好了,了解完上边三个关键字,我相信你自己都可以做总结了,那我这里还是走完流程吧😊~

总结

对于applycallbind三个关键字

相同点:它们的第一个参数都是函数绑定的上下文(即this的指向)

不同点:

  • callapply:第二个参数传递形式不同,call中的参数需要逐个传,而apply可以以数组的形式传递
  • callbind:第一、二个参数完全相同,不同的是bind会返回一个绑定好上下文的新函数

结语

以上就是本篇的所有内容了,如果文章对于你有点用,请留个赞鼓励一下luckyCover嘿嘿,我们下篇文章见!

相关推荐
csgo打的菜又爱玩38 分钟前
Vue 基础(实战模板与命名指南)
前端·javascript·vue.js
ding_zhikai2 小时前
SD:在一个 Ubuntu 系统安装 stable diffusion Web UI
前端·ubuntu·stable diffusion
gerrgwg3 小时前
Vue-library-start,一个基于Vite的vue组件库开发模板
前端·javascript·vue.js
你的人类朋友4 小时前
【Node】单线程的Node.js为什么可以实现多线程?
前端·后端·node.js
iナナ5 小时前
Spring Web MVC入门
java·前端·网络·后端·spring·mvc
驱动探索者5 小时前
find 命令使用介绍
java·linux·运维·服务器·前端·学习·microsoft
开心不就得了5 小时前
自定义脚手架
前端·javascript
星晨雪海7 小时前
怎么格式化idea中的vue文件
前端·vue.js·intellij-idea
没事多睡觉6667 小时前
Vue 虚拟列表实现方案详解:三种方法的完整对比与实践
前端·javascript·vue.js
white-persist7 小时前
Burp Suite模拟器抓包全攻略
前端·网络·安全·web安全·notepad++·原型模式