前言
在 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()
被调用时,this
在 say
函数的上下文中指向 obj
对象。
这是因为 this.name
解析为调用 say
方法的对象 obj
的 name
属性。在JavaScript中,当一个函数作为对象的方法被调用时,this
通常指向那个对象。所以在这个例子中,this
指向 obj
,this.name
就是 obj.name
。
根据上面的例子,我相信你对this有一个大概的了解。
默认绑定原则
什么是默认绑定原则呢?
javascript
function foo() {
var a = 1
console.log(this.a); // 输出:undefined
}
foo()
在JavaScript中,函数的 this
值取决于函数是如何被调用的。对于普通函数调用,this
通常指向全局对象。在浏览器环境中,全局对象是 window
。
在上面的代码中,foo
是作为一个普通函数被调用的(不是作为某个对象的方法或通过 new
关键字构造的实例)。在这种情况下,由于 a
是在 foo
函数内部声明的局部变量,它不是 window
或 global
对象的属性,所以 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
的指向。
首先,让我们逐步分析代码:
- 定义函数
foo()
,该函数内部使用this
来访问变量a
。 - 创建名为
obj
的对象,其中包含属性a
和一个指向foo()
函数的方法。 - 创建名为
obj2
的对象,其中包含属性a
和另一个对象obj
作为属性。 - 在
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
。 此外,我们还传递了参数 4
和 5
给 foo
函数,它们会被作为 x
和 y
的值传递进来。
因此,当 foo
函数被调用时,它会打印出 obj.a
的值(即 1
)和 x + y
的和(即 9
)。
apply ()
apply()
是 JavaScript 中的一个方法,它与 call()
方法类似,用于在指定的 this
值和参数数组下调用函数。使用 apply()
方法,我们可以显式地设置函数内的 this
值,并将函数作为对象的方法调用。
apply()
方法接受两个参数,第一个参数是要设置为函数上下文的对象,第二个参数是一个参数数组,这些参数将作为数组元素传递给函数。
以下是 apply()
方法的语法:
arduino
functionName.apply(thisValue, [arg1, arg2, ...])
functionName
:要调用的函数名称。thisValue
:要设置为函数上下文的对象。如果不需要设置上下文,可以传入null
或undefined
。[arg1, arg2, ...]
:作为参数数组传递给函数的参数列表。
以下是一个使用 apply()
方法的例子:
javascript
function foo(x,y) {
console.log(this.a, x + y);
}
var obj = {
a: 1
}
foo.apply(obj,[4,5])
这段代码定义了一个名为 foo
的函数,它接受两个参数 x
和 y
,并在控制台中输出 this.a
和 x + 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
函数的上下文,并绑定参数 4
和 5
,返回一个新函数 bar
。此时,bar
函数已经绑定了上下文和部分参数。
接着,调用 bar()
,没有传入任何参数。由于 bar
函数已经绑定了上下文 obj
和部分参数 4
和 5
,所以在控制台中输出的是 1 9
,其中 this.a
的值为 1
,x + 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
是在定义函数时确定的,而不是在运行时确定的。