持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
经常会被问到this的指向问题,我们今天就来一探究竟吧
为什么要有this关键字呢?
- 在java等一些编程语言中,this的关键字一般指向类,且只能在类中使用
- js中,this的使用则更加灵活 我们先来看个栗子,看下有没有this有什么不同
var obj = {
name:'juejin',
eat:function(){
console.log(this.name+'在吃饭')
}
}
obj.eat() // juejin在吃饭
上述代码执行会打印一个juejin在吃饭,那我们如果把代码稍微做一个修改,把this改为obj
var obj = {
name:'juejin',
eat:function(){
console.log(obj.name+'在吃饭')
}
}
obj.eat() // juejin在吃饭
这个时候我们会发现,依然会打印juejin在吃饭,这就说明即使没有this,我们所需要的需求依旧可以实现。 那么为什么还需要this关键字呢,那是因为如果没有this关键字我们的实现就会复杂难读了许多
那么this到底指向什么呢?
this在全局作用域里的指向
我们先来看个简单的,this在全局作用域下的指向是什么呢
// 1.在全局下的指向
console.log(this)
这个其实可以分为浏览器环境和nodejs环境来看
- 浏览器下,this指向window对象
- 而在nodejs下则会指向一个空对象({})
this在函数中的指向
然而我们的this很少在全局中被使用,更多的是被使用在函数中
this的生成
- 所有的函数在被调用的时候都会生成一个执行上下文
- 这个执行上下文记录着函数的调用栈,AO对象
- this就是其中的一条记录
this的绑定规则
我们先来看一个小小的栗子
// 定义一个函数
function foo () {
console.log(this)
}
// 1.在window中调用
foo()
// 2.将foo放入一个对象的变量中进行调用
var obj = {
fn:foo
}
obj.fn()
// 3.通过call和apply调用
foo.call('111')
来看下它的执行结果
我们会发现,不同的调用方式,会有不同的结果。
由上述的栗子我们可以知道
- 在函数被调用时,js会默认给this绑定一个值
- this的绑定和它编写的位置没有关系
- this的绑定和函数的调用方式以及函数的调用位置有关
- this是在函数运行时被绑定的
那么函数的绑定规则到底是怎样的呢,我们一起来学习下吧
它的调用规则大概分为这么四种,接下来我们一一的来对其进行解释
- 默认绑定
- 隐式绑定
- 显式绑定
- new关键字绑定
默认绑定
定义
函数调用时无任何调用前缀的情景,也就是函数直接自己执行
在默认绑定下的this默认指向全局对象
栗子
举个栗子
// 定义一个函数
function foo () {
console.log(this)
}
// 默认绑定
foo()
它的执行结果
几个题目
- 题目1
// 定义一个函数
function foo () {
console.log(this)
}
// 默认绑定
// foo()
// 默认绑定栗子1
var obj = {
fn:foo
}
var foo1 = obj.fn
foo1()
foo1的打印结果就是全局变量,因为它是独立执行的
- 题目2
// 定义一个函数
function foo () {
console.log(this)
}
function bar(){
foo()
}
bar()
来看下执行结果依然是全局对象
隐式绑定
定义
隐式绑定是通过函数的调用来进行绑定的,也就是说如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上
栗子
// 定义一个函数
function foo () {
console.log(this)
}
var obj = {
name:'kaka',
fn:foo
}
obj.fn()
根据定义它应该指向的是obj对象,也就是谁直接调用它,this就指向谁,来看下它的执行结果
再来看个栗子,当有多次调用时的情况
// 定义一个函数
function foo () {
console.log(this)
}
var obj = {
fn:foo
}
var obj2 = {
name: 'messi',
sonobj: obj
}
obj2.sonobj.fn()
因为fn由sonobj调用的,而sonobj又是obj对象,所以this指向obj
显示绑定
定义
因为隐式绑定有一个前提条件,必须在函数内部有一个对函数的引用,比如一个对象,然后通过这个引用间接的this绑定到这个对象上,如果没有这个引用的话,调用函数就会报找不到该函数的错误
因此,隐式绑定会有一些弊端
如果我们想让自己指定函数的this指向,就应该用到显示绑定来自己指定this的指向,通常会用到下面三个方法
- call
- apply
- bind
这个三个方法是js内置的方法,可以帮我们改变this的指向
栗子
// 定义一个函数
function sum (a,b) {
console.log(a+b)
console.log(this)
}
var obj = {
name:'kaka'
}
sum.call(obj, 100,200)
这个栗子的执行结果
call方法改变了this的指向,将其从原本的window对象指向了obj对象,call的第一个参数就是你要指定的this的指向,后续的参数就是原函数的变量,挨个传入
三个方法的区别
上面我们说过,call,apply和bind都是改变this的指向的,它们区别是什么呢
-
call和apply的区别就是后边参数的传入方式
- call是直接挨个传入
javascript sum.call(obj,100,200) - apply是把函数的参数当数组传入第二个参数
sum.apply(obj,[100,200])来看下执行结果
- call是直接挨个传入
-
bind函数的返回值是个函数,所以需要执行一下才能改变指向
-
bind的函数传参方式和apply相同
sum.bind(obj, 100,200)()
来看下运行结果
new关键字绑定
定义
- javascript中的函数可以当作一个类的构造函数来使用,也就是使用new关键字
- 使用函数的new关键字来进行调用的时候会执行如下操作
- 创建一个全新的对象
- 这个新对象会被执行prototype连接
- 这个新对象会被绑定到函数调用的this上(this绑定在这个时候完成)
- 如果函数没有返回其他对象,函数会返回这个新对象
栗子
// 定义一个函数
function person (name,age) {
this.name = name
this.age = age
this.fn = function () {
console.log(this)
}
}
const kaka = new person('kaka', 22)
kaka.fn()
来看下运行结果