“this 关键字难倒你了吗?看完这篇文章你就能搞定了!”

前言

在 JavaScript 中,this 是一个关键字,用于指代当前函数执行的上下文或对象。具体来说,this 可以用来引用当前执行环境中的对象,例如函数、方法、构造函数等。

this 的值在运行时动态确定,取决于调用函数的方式和上下文环境。在不同的情况下,this 可能指向全局对象(浏览器中为 window 对象),也可能指向函数所属的对象,或者是通过 call、apply 或 bind 方法显式绑定的对象,还有可能是通过 new 操作符创建的新对象等。

了解 this 的使用规则对于编写优雅的 JavaScript 代码至关重要。理解 this 的行为有助于避免一些常见的错误,同时也能帮助开发人员更好地控制函数内部的上下文,提高代码的可读性和可维护性。

初步认识this

javascript 复制代码
let obj = {
    name: '小明',
    say: function() { 
      console.log(this.name); 
    }
 } 
 obj.say() // 输出:小明

在这段代码中,say 函数内部使用了 this 关键字来引用当前对象。当 obj.say() 被调用时,thissay 函数的上下文中指向 obj 对象。

这是因为 this.name 解析为调用 say 方法的对象 objname 属性。在JavaScript中,当一个函数作为对象的方法被调用时,this 通常指向那个对象。所以在这个例子中,this 指向 objthis.name 就是 obj.name

根据上面的例子,我相信你对this有一个大概的了解。

默认绑定原则

什么是默认绑定原则呢?

javascript 复制代码
    function foo() { 
      var a = 1 
      console.log(this.a); // 输出:undefined
    }
    foo()

在JavaScript中,函数的 this 值取决于函数是如何被调用的。对于普通函数调用,this 通常指向全局对象。在浏览器环境中,全局对象是 window

在上面的代码中,foo 是作为一个普通函数被调用的(不是作为某个对象的方法或通过 new 关键字构造的实例)。在这种情况下,由于 a 是在 foo 函数内部声明的局部变量,它不是 windowglobal 对象的属性,所以 this.a 将会是 undefined

要使上面代码输出 1 ,则可以:

javascript 复制代码
 var a = 1 
 function foo() { 
    console.log(this.a);  输出:1
 }
foo() 

在这个例子中,变量a被定义为全局变量,其值为1。函数foo()内部的this指向的是全局对象。 当在foo()函数中调用console.log(this.a)时,它会访问全局作用域中的变量a,因为this指向的是全局对象。因此,输出结果将是1

根据上面例子我们再进行拓展:

scss 复制代码
var a = 1;
function foo() { 
      console.log(this.a);
  } 
function bar() {
      var a = 2;
      foo();
    }
bar()

在这个例子中,foo()函数在bar()函数的作用域内被调用。然而,由于foo()函数是独立的函数,并没有绑定到任何对象上,因此它的this指向的是全局对象。

因此,当在foo()函数中打印this.a时,它会访问全局作用域中的变量a,而不是bar()函数的作用域中的变量a。在全局作用域中,变量a的值为1,所以输出结果是1

即使foo()函数在bar()函数内部被调用,但这并不影响foo()函数的this指向。foo()函数的this值是在函数被调用时确定的,而不是在定义函数时确定的。

再看下面的代码中,this会指向谁:

scss 复制代码
function foo() {
  function bar() {
      console.log(this);
 }
  function baz () {
        bar()
    }
    baz()   
}
 foo()

在这个例子中,`foo()`函数内部定义了两个函数:`bar()`和`baz()`。在`baz()`函数中调用了`bar()`函数。因为`bar()`函数是在全局作用域中定义的,所以它的执行上下文中的`this`指向全局对象(在浏览器环境中通常是`window`对象)。

因此,当在bar()函数中调用console.log(this)时,它会输出全局对象。

总结:函数在哪个词法作用域生效,this就指向哪里,而独立调用的函数this都指向window。

隐式绑定原则

JavaScript 中的隐式绑定原则是指当函数作为对象的方法被调用时,函数内部的 this 指向该对象。具体来说,当函数通过对象来调用时,函数内部的 this 指向该对象。这种绑定方式称为隐式绑定。

css 复制代码
 function foo() {
    console.log(this.a);
}
var obj = {
    a: 1 ,
    foo: foo
}
var obj2 = {
    a: 2,
    obj: obj
}
obj2.obj.foo()

在这个例子中,函数foo()被定义为一个函数表达式,并且可以通过不同的对象进行调用。在最后一行代码中,我们看到了一个嵌套的对象引用,它会影响 this 的指向。

首先,让我们逐步分析代码:

  1. 定义函数foo(),该函数内部使用this来访问变量a
  2. 创建名为obj的对象,其中包含属性a和一个指向foo()函数的方法。
  3. 创建名为obj2的对象,其中包含属性a和另一个对象obj作为属性。
  4. obj2对象上调用obj.foo(),这将触发foo()函数的执行。

根据隐式绑定规则,当函数作为对象的方法被调用时,函数内部的 this 指向调用该方法的对象。

因此,在obj2.obj.foo()调用中,函数内部的 this 指向obj对象,因为调用链中最后一个对象是obj。所以输出结果将是1,因为obj对象的a属性值为1

因此,最终的输出结果将是1

显示绑定

示绑定是通过call()apply()bind()方法来直接指定函数中的 this 指向的过程,它可以被用来覆盖隐式绑定规则。

call ( )

call() 是 JavaScript 中的一个方法,它用于在指定的 this 值和参数列表下调用函数。使用 call() 方法,我们可以显式地设置函数内的 this 值,并将函数作为对象的方法调用。

call() 方法接受两个参数,第一个参数是要设置为函数上下文的对象,第二个参数及其后的参数是传递给函数的参数列表。

以下是 call() 方法的语法:

scss 复制代码
functionName.call(thisValue, arg1, arg2, ...)
  • functionName:要调用的函数名称。
  • thisValue:要绑定到函数内部的 this 值,如果不传入,则默认为全局对象。
  • arg1, arg2, ...:要传递给函数的参数列表。

话不多说,我们进入实战:

javascript 复制代码
function foo() {
    console.log(this.a);
}
var obj = {
    a: 1
}
foo()

前面我们已经得知,这里会输出undefined,那我们如何让这段代码输出1呢?

javascript 复制代码
function foo() {
    console.log(this.a);输出:1
}
var obj = {
    a: 1
}
foo.call(obj)

在这个例子中,我们定义了一个名为 foo 的函数和一个名为 obj 的对象。接着,我们使用 call 方法来显式地调用 foo 函数,并将 obj 对象作为 this 值传递给它。

通过调用 foo.call(obj),我们改变了 foo 函数的执行上下文,使得函数内部的 this 指向了 obj 对象。这意味着在函数内部,通过 this.a 可以访问到 obj 对象的属性 a

因此,当 foo 函数被调用时,它会打印出 obj.a 的值,即输出结果为 1

再看一个例子:

javascript 复制代码
function foo(x,y) {
    console.log(this.a, x + y);
}
var obj = {
    a: 1
}
foo.call(obj,4,5)

通过调用 foo.call(obj, 4, 5),我们改变了 foo 函数的执行上下文,使得函数内部的 this 指向了 obj 对象。在函数内部,this.a 可以访问到 obj 对象的属性 a。 此外,我们还传递了参数 45foo 函数,它们会被作为 xy 的值传递进来。

因此,当 foo 函数被调用时,它会打印出 obj.a 的值(即 1)和 x + y 的和(即 9)。

apply ()

apply() 是 JavaScript 中的一个方法,它与 call() 方法类似,用于在指定的 this 值和参数数组下调用函数。使用 apply() 方法,我们可以显式地设置函数内的 this 值,并将函数作为对象的方法调用。

apply() 方法接受两个参数,第一个参数是要设置为函数上下文的对象,第二个参数是一个参数数组,这些参数将作为数组元素传递给函数。

以下是 apply() 方法的语法:

arduino 复制代码
functionName.apply(thisValue, [arg1, arg2, ...])
  • functionName:要调用的函数名称。
  • thisValue:要设置为函数上下文的对象。如果不需要设置上下文,可以传入 nullundefined
  • [arg1, arg2, ...]:作为参数数组传递给函数的参数列表。

以下是一个使用 apply() 方法的例子:

javascript 复制代码
function foo(x,y) {
    console.log(this.a, x + y);
}
var obj = {
    a: 1
}
foo.apply(obj,[4,5]) 

这段代码定义了一个名为 foo 的函数,它接受两个参数 xy,并在控制台中输出 this.ax + y 的值。然后创建了一个名为 obj 的对象,并将其赋值为 {a: 1}

接下来,使用 apply() 方法调用 foo 函数,并将 obj 对象作为上下文,将参数数组 [4, 5] 传递给函数。因此,this.a 将被设置为 obj.a,也就是 1,而 x + y 的值为 9。因此,控制台将输出 1 9

总之,使用 apply() 方法可以显式地设置函数内的 this 值,并将函数作为对象的方法调用。同时,它还可以接受一个参数数组来传递函数的参数。

bind()

bind() 是 JavaScript 中的一个方法,它用于创建一个新函数 ,并将指定的对象作为新函数的上下文(this 值)。与 call()apply() 方法不同,bind() 方法不会立即调用函数,而是返回一个新函数,可以在稍后的时间点调用。

bind() 方法接受一个参数,即要设置为新函数上下文的对象。

以下是 bind() 方法的语法:

bash 复制代码
javascript复制代码
functionName.bind(thisValue)
  • functionName:要绑定上下文的函数名称。
  • thisValue:要设置为函数上下文的对象。

以下是一个使用 bind() 方法的例子:

javascript 复制代码
function foo(x,y) {
    console.log(this.a, x + y);
}
var obj = {
    a: 1
}
let bar = foo.bind(obj,4,5)
bar()

使用 bind() 方法将 obj 对象作为 foo 函数的上下文,并绑定参数 45,返回一个新函数 bar。此时,bar 函数已经绑定了上下文和部分参数。

接着,调用 bar(),没有传入任何参数。由于 bar 函数已经绑定了上下文 obj 和部分参数 45,所以在控制台中输出的是 1 9,其中 this.a 的值为 1x + y 的值为 9

综上所述,通过使用 bind() 方法,我们可以创建一个新函数,并绑定指定的上下文和参数。这个新函数可以稍后被调用,保持绑定的上下文和参数不变。如果在调用新函数时不传入参数,那么绑定的参数将会被使用。

箭头函数

箭头函数是ES6引入的一种新的函数语法格式。它使用箭头(=>)来定义函数,相比传统的函数声明或函数表达式,有一些特殊的行为和用法。

下面是箭头函数的表示:

ini 复制代码
var functionName = () => {};

以下是一个示例:

ini 复制代码
var obj = {
    a: 1
}
var foo = () => {
    console.log(this.a);
}
foo.call(obj) 

在这段代码中,使用箭头函数 foo 并尝试通过 call 方法来设置 this 指向对象 obj。然而,需要注意的是,箭头函数无法通过 call 方法来改变其 this 值,因为箭头函数的 this 值是由外层作用域决定的。

因此,当执行 foo.call(obj) 时,在浏览器环境下,外层作用域的 this 值通常是全局对象 window,所以代码会尝试打印 window.a 的值。

全局对象中没有 a 这个属性,那么输出结果将是 undefined

再看下面代码:

javascript 复制代码
 var obj = {
    a: 1
}
function foo() {
    var bar = () => {
        console.log(this);
    }
    bar ()
}
foo.call(obj)

在执行 foo 函数时,它会定义并调用 bar 函数。由于 bar 是一个箭头函数,它会继承外层作用域(即 foo 函数)的 this 值。因此,当 bar 函数被调用时,它的 this 值就是 foo 函数的 this 值,也就是对象 obj

最终执行 console.log(this) 时,将会打印对象 obj 的信息,即 {a: 1}

箭头函数没有自己的 this 绑定机制,它会继承外层普通函数的 this 值。 具体来说,箭头函数中的 this 是在定义函数时确定的,而不是在运行时确定的。

相关推荐
PandaCave3 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟5 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾26 分钟前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧35 分钟前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7011 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue
疯狂的沙粒2 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript
gqkmiss2 小时前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools