序言
在 JavaScript 中,this
关键字是一个非常重要和特殊的关键字,它代表当前函数的执行上下文,也就是函数被调用时所处的环境。this
的指向是动态确定的,根据函数的调用方式不同而有所不同。this
的绑定规则是开发过程中常常会遇到的一个重要问题。本文将深入讨论 JavaScript 中 this
的五种绑定规则,并介绍 ES6 新增的箭头函数对于 this
的独特处理方式。
this 的五种绑定规则
1. 默认绑定
当函数在全局作用域中被调用时,this 会指向全局对象(浏览器中是 window 对象)。这意味着函数在哪个词法作用域里生效,this 就指向哪里。
console.log(this);
这段代码如果在JS中用Node
输出结果是是一个空对象 {}
,因为this在这里指向的是全局;如果在浏览器中执行这段代码打印这段代码打印的是Window
,因为Window代表浏览器的环境
var a = 1
console.log(window.a); //window代表浏览器环境
那么我们将这段代码放到浏览器中去执行可以拿到吗?答案是可以的,输出的结果是1
。函数的默认绑定有些地方说函数在哪里调用this
就指向谁,但是这句话存在很多诟病,应该是在默认绑定
情况下,函数在哪个词法作用域
里生效
this就指向哪,让我们通过下面这个例子来理解this的默认绑定指向:
var b = 2
function foo() {
var b = 1
bar()
}
function bar() {
baz()
}
function baz(){
console.log(this.b);
}
foo()
我们先调用foo
函数,然后再调用bar
函数,最后再调用baz
函数打印this.b
,那么这个this
指向哪里呢?
baz
函数在bar
函数中被调用是不是就指向bar
函数呢,错因为this指向的是一个词法作用域
,但是bar
是一个函数只有他自己的作用域
,而没有词法作用域
,baz
函数的词法作用域
是全局
,所以this.b
最后打印的是 2
2. 隐式绑定
当函数被一个对象所拥有,再调用时,此时this会指向该对象
function foo() {
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var obj2 = {
a:3,
obj:obj
}
obj2.obj.foo()
我们通过obj2.obj.foo()
调用foo
函数。在这个例子中,foo
函数是作为 obj
对象拥有的一个属性进行调用的,因此函数内部的 this 将指向调用它的对象,也就是 obj 对象,这也被称为就近原则
,所以最终输出的结果就是 2
3. 隐式丢失
当函数被多个对象链式调用时,this指向引用函数的对象
我们上面的例子就发生了隐式丢失
,让我们继续通过这个例子来理解这个概念
function foo() {
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var obj2 = {
a:3,
obj:obj
}
obj2.obj.foo()
我们是通过对象obj2
来调用它里面的对象obj
,再通过对象obj
调用函数foo
,有没有思考过,是obj2
在调用foo
函数,还是obj
在调用foo
函数?这里我们this
的指向就相当于一种就近原则
,foo
函数被对象obj
引用,于是this
就指向对象obj
,于是就造成了对象obj2
调用的丢失,这就是this发生隐式绑定
的隐式丢失
4. 显示绑定
我们还可以通过
显示绑定
来控制this
的指向,这么做可以确保函数中的 this 指向我们所期望的对象,而不受到默认绑定规则的影响。JavaScript提供了
三种
显示绑定的方法:call、apply 或者 bind 方法
1. call方法
call 方法可以立即执行函数,并且允许我们将一个对象绑定到函数体内的 this,并传入参数列表。
function greet() {
console.log(this.name);
}
var person = { name: 'Alice' };
greet.call(person); // 输出:Alice
2. apply方法
apply 方法与 call 类似,但是它接受一个包含多个参数的数组作为参数。
function multiply(a, b) {
return this.value * a * b;
}
var context = { value: 10 };
var result = multiply.apply(context, [2, 3]); // 传入参数数组 [2, 3]
console.log(result); // 输出:60
3. bind方法
bind 方法会创建一个新的函数,称为绑定函数,它会将原始函数绑定到指定的对象上。绑定函数的 this 始终指向传入的对象,无论它如何被调用。
function foo(n,m){
console.log(this.a,n,m);
}
var obj = {
a:2
}
var bar = foo.bind(obj,100,200) //声明一个变量承载新创建的函数
bar() //调用函数 输出 2 100 200
5. new绑定
当使用 new 关键字调用构造函数时,会创建一个新的对象,并将这个新对象绑定到 this 上。
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 使用 new 关键字创建新对象并绑定构造函数
var person1 = new Person('Alice', 25);
var person2 = new Person('Bob', 30);
console.log(person1.name); // 输出:'Alice'
console.log(person2.age); // 输出:30
箭头函数 (ES6新增函数)
箭头函数是ES6引入的一种新的函数定义方式,它提供了一种更简洁的语法形式,并且在某些情况下可以改变函数内部的this绑定。
与普通函数不同,箭头函数没有this这个概念,写在箭头函数中的this也是它外层函数的this,并且无法通过 call、apply、bind 方法来改变 this 的指向。
下面是一些箭头函数的示例:
var a = 1
var baz = () => {
console.log(this.a);
}
baz()
在这段代码中,箭头函数 baz
被定义在全局作用域下,并且尝试打印 this.a
的值。
由于箭头函数
的特性,它的 this
始终指向其定义时所处的上下文(词法作用域)
,而不是运行时的上下文(函数作用域)
。在浏览器中,全局上下文中的 this
指向全局对象
(通常是 window
对象),因此在这个例子中,this.a
实际上会尝试访问全局对象的 a
属性。
因为全局范围内确实有一个名为 a
的变量被定义并赋值为 1
,所以在这种情况下,当调用 baz()
时,会打印出 1
。
总之,由于箭头函数
的特性,它不会改变 this
的指向,因此在全局范围内使用箭头函数
时,它会继承全局上下文中的this
值,而且可以正常访问全局变量
。
var obj = {
name : 'TOM',
show: function(){
var bar = () => {
console.log(this.name);
}
bar()
}
}
obj.show()
在这段代码中,对象 obj
包含一个名为 name
的属性和一个名为 show
的方法。show
方法内部定义了一个箭头函数 bar
,用于输出 this.name
的值。在 show
方法被调用时,bar
函数也被调用。
由于箭头函数 bar
被定义在 show
方法内部,它继承了 show
方法的执行上下文,因此箭头函数的 this 始终指向其定义时的外层作用域,也就是对象 obj
。因此,当 bar
函数被调用时,它能够访问到对象 obj
的 name
属性,并正确地输出 'TOM'
。
因此,当你调用 obj.show()
时,会打印出 'TOM'
。
总结
下面是我们今天内容的笔记:
# this的绑定规则
1. 默认绑定 --- 函数在哪个词法作用域里生效,this就指向哪里
2. 隐式绑定 --- 当函数被一个对象所拥有,再调用时,此时this会指向该对象
3. 隐式丢失 --- 当函数被多个对象链式调用时,this指向引用函数的对象
4. 显示绑定 --- call,apply,bind
5. new绑定 --- 当使用 new 关键字调用构造函数时,会创建一个新的对象,并将这个新对象绑定到 this 上。
# 箭头函数 (ES6新增函数)
箭头函数没有this这个概念,写在箭头函数中的this也是它外层函数的this
总之。JavaScript中的 this
可以根据执行上下文
的不同
而有不同的指向
,需要根据具体的使用场景来理解和确定它的值。对于箭头函数
来说,它会继承
外层作用域的 this
值,而不会被上述规则所影响。
到这里我们今天的文章就结束了,喜欢的小伙伴的可以点赞+关注
,作者后续会持续更新类似干货文章。