百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 行业知识 > 正文

广州蓝景分享—前端开发JavaScript中的“this”,你了解多少?

citgpt 2024-11-25 10:02 4 浏览 0 评论

介绍

在前端面试过程中,面试官经常会问一些关于this关键字的问题,即使是工作多年的人也可能知之甚少。今天,广州蓝景小编继续跟大家分享对this关键字的详细分析,以便他人深入了解。

广州蓝景分享—前端开发JavaScript中的“this”,你了解多少?

什么是"this"?

调用函数时,将创建一个执行环境,this 在运行时根据函数的执行环境绑定。它允许函数在内部引用上下文中的执行变量,使函数编程更加优雅和简洁。

看看下面的代码,想想为什么不同的调用方法会打印出不同的结果。

var a = 10

const obj = {
  a: 20,
  foo: function() {
    console.log(this.a)

    return function() {
      console.log(this.a)
    }
  }
}

obj.foo()() // 20 10
const fn = obj.foo
fn() // 10

其实很简单,因为不同的调用方法的this指向不同的点。为什么这指向不同的函数调用方法?是什么决定了这一点?现在让我们开始带着问题深入了解这个问题!

“this”的约束规则

默认绑定

默认绑定规则下,函数的运行环境为全局环境,this默认指向Window。

默认绑定规则如下:

1、this指向Window的全局函数

在全局函数中直接打印 this 时,可以看到 this 指向 Window。

2、独立函数调用this指向Window

独立的函数调用,即直接调用函数,如 foo()。

function foo() {
  console.log(this);
}

foo()

这里的foo默认链接到Window,相当于window.foo()。根据函数的隐式绑定规则,谁调用谁就指向谁。这里的 this 指向 Window。

同样,如果嵌套函数中直接调用的函数也是独立的函数调用,那么this也指向Window:

var a = 10
var obj = {
  a: 20,
  foo: function() {
    console.log(this); // {a: 20, foo: ?}
    console.log(this.a); // 20

    function son() {
      console.log(this); // Window
      console.log(this.a); // 10
    }
    // Independent function call
    son() 
  }
}

obj.foo()

在上面的代码中,子函数son也嵌套在对象obj的方法foo中。当直接调用子方法时,子里面的this指向Window,所以子函数里面的this.a结果是全局变量a,也就是10。

如果要在子函数中使用 obj 中的变量 a 怎么办?只需将 this 对象分配给另一个变量,并在子方法中引用此变量:

var a = 10
var obj = {
  a: 20,
  foo: function() {
    const that = this

    function son() {
      console.log(that.a); // 20
    }
    son() 
  }
}

obj.foo()

3、对于自执行函数调用,this指向window

自执行函数,顾名思义,就是定义函数后自动调用的函数,自执行函数的this指向如下代码:

 // exp1
(function() {
  console.log(this); // Window
})()

// exp2
var a = 10 
function foo() {
  (function son(that) {
    console.log(this); // Window
    console.log(that); // {a: 20, foo: ?}
  })(this)
}

var obj = {
  a: 20,
  foo: foo,
}

obj.foo()

上面代码中的foo函数内部嵌套了一个自执行函数son,而son内部的this指向Window。这里this指向的原理类似于独立函数调用,即先声明一个son方法,然后通过son()执行该函数。如果要获取son中上层对象obj的变量,可以在调用时将this点作为参数传递给自执行函数son。

4、闭包里面的this指向Window

闭包可以理解为一个函数内部定义的函数,可以访问其他函数的内部变量。当我们查看闭包中的 this 点时,我们可以看到 this 指向 Window。

var a = 10

var obj = {
  a: 20,
  foo: function() {
    var sum = this.count + 10

    console.log(this.a); // 20
    return function() {
      console.log(this.a); // 10
      return sum
    }
  }
}

obj.foo()()

上面代码中,foo函数的第一个this.a的this指向obj对象,所以结果是20。return函数调用的this指向Window,结果是10。obj.foo ()() 可以理解为:

const fn = obj.foo()
fn()

fn 是 obj.foo() 返回的函数。fn 函数是独立调用的,this 指向 Window。

隐式绑定

当函数作为方法调用时,this指向函数的直接父对象,称为隐式绑定。

在隐式绑定规则中,认为 this 指向调用函数的人,并将指向函数的直接父对象。比如obj.foo()中foo函数里面的this指向obj,而obj1中的foo函数。obj2.foo() 指向 obj2。

var a = 10
function foo () {
  console.log(this.a);
}

var obj = {
  a: 20,
  foo: foo,
  obj2: {
    a: 30,
    foo: foo
  }
}

// exp1
foo() // 10

// exp2
obj.foo() // 20

// exp3 
obj.obj2.foo() // 30

上面的代码也是对 foo 函数的调用。调用方法不同,结果不同。

‘exp1’中的foo直接被独立函数调用,所以,this指向Window,结果为10;‘exp2’中的调用方法是obj。foo(),foo函数的this指向上级调用对象obj;结果是 20。'exp3' 中 foo 函数的直接上级对象是 obj2,所以结果是 30。

隐式绑定丢失

隐式绑定丢失意味着隐式绑定的函数丢失了它的绑定对象,所以默认绑定到Window。这种方法在我们的项目中很容易导致错误,但也很常见。

1、隐式绑定的函数被分配为没有 this 指向的变量。

在下面的代码中,obj下的foo值实际上是foo函数的地址信息,并不是真正的foo函数。当 obj.调用 foo() 时, this 的 this 隐式绑定到 obj。当 var fn=obj.foo 为 fn 分配一个函数时。相当于把foo函数的地址赋给fn。这时候fn没有和obj关联,所以这里fn()的运行环境就是全局环境,this指向Window,this的结果a 是 10。

var a = 10
var obj = {
  a: 20,
  foo: function () {
    console.log(this.a);
  }
}
function bar (fn) {
  fn()
}

bar(obj.foo) // 10

2、隐式绑定的函数作为参数传给函数,丢失了this点。

当一个隐式绑定的函数直接作为参数传递给另一个函数时,这个绑定会丢失,从而指向全局Window。obj.foo作为参数传给bar函数后,this.a的结果是10。这里bar(obj.foo)等价于var fn=obj.foo; bar(fn)。

var a = 10
var obj = {
  a: 20,
  foo: function () {
    console.log(this.a);
  }
}
function bar (fn) {
  fn()
}

bar(obj.foo) // 10

3、内置对象setTimeout和setInterval函数的隐式绑定丢失

内置函数 setTimeout 和 setInterval 的 this 默认指向 Window。

// exp1
setTimeout(function() {
  console.log(this); // Window
}, 1000)

// exp2
var a = 10
var obj = {
  a: 20,
  foo: function () {
    console.log(this.a); // 10
  }
}

setTimeout(obj.foo, 1000)

对了,当setTimeout或者setInterval的第一个参数是箭头函数时,this会指向上层的函数执行环境。代码如下:

var a = 10
var obj = {
  a: 20,
  foo: function () {
    console.log(this.a); // 20

    setTimeout(() => {
      console.log(this.a); // 20
    }, 1000)

    setTimeout(function() {
      console.log(this.a); // 10
    }, 1000);
  }
}

obj.foo()

显式绑定

当我们要将函数绑定到指定对象时,可以使用call、apply、bind等方法手动改变this的方向,即显式绑定。

在下面的代码中,将 foo 显式绑定到 p 对象的方法分别使用 call、apply 和 bind 来举例说明。显式绑定 call 和 apply 的方法会在显式绑定后直接调用,而显式绑定 this 到 bind 的方法需要手动调用。

var obj = {
  a: 20,
  foo: function () {
    console.log(this.a);
  }
}
var p = {
  a: 30,
}

obj.foo() // 20
obj.foo.call(p) // 30
obj.foo.apply(p) // 30

const fn = obj.foo.bind(p)
fn() // 30

关于硬装订

显式绑定可以帮我们把this改成指定对象,但是不能解决隐式绑定缺失的问题,比如:

var a = 10
function foo() {
  console.log(this.a);
}

var obj = {
  a: 20,
  foo: foo
}

var p = {
  a: 30
}

function func(fn) {
  fn()
}

func.call(p, obj.foo) // 10

在上面的代码中,调用是绑定 this 指向 p 对象,但最终 this 指向的是 Window。此时,我们可以通过硬绑定来解决这个问题。

var a = 10
function foo() {
  console.log(this.a);
}

var obj = {
  a: 20,
  foo: foo
}

var p = {
  a: 30
}

function func(fn) {
  fn()
}

let bar = function () {
  foo.call(p)
}
bar() // 30

“new”绑定

new绑定是我们常用的方法。事实上,我们可以创建一个构造,然后新建一个实例对象。这时候this指向了new出来的实例对象。

当我们彼此认识时,我们主要做以下事情:

  • a、创建一个新对象
  • b、让这一点指向新对象并执行构造体
  • c、将新对象的 proto 属性设置为指向构造的原型对象
  • d、判断构造的返回类型。如果是,则返回新对象。如果它是引用类型,它将返回此类型的对象。

首先,创建了“Person”的构造,然后,通过“new”创建了一个“zhangsan”的实例对象。在“zhangsan”的“foo”函数中,“this”指向“zhangsan”的实例。

function Person(name, age) {
  this.name = name
  this.age = age
  this.foo = function () {
    console.log(this.name);
  }
}

const zhangsan = new Person('zhangsan', 18)
console.log(zhangsan) // {name: 'zhangsan', age: 18, foo: ?}
zhangsan.foo() // zhangsan

在严格模式下,“this”指向一个问题。

1.独立调用函数的内部“this”是“undefined”

function foo() {
  "use strict"
  console.log(this); undifined
}
foo()

2. “call()”和“apply()”中的this总是他们的第一个参数

var a = 10
var obj = {
  a: 20,
  foo: function () {
    "use strict"
    console.log(this);
  }
}

// When null | undefined, in non-strict mode, this points to window
obj.foo.call(null) // null
obj.foo.call(undefined) // undefined
obj.foo.apply(null) // null
obj.foo.apply(undefined) // undefined
var fn = obj.foo.bind(null)
fn()

总结

这是一个比较复杂的知识点。当然,如果我们真的理解了this的原理,那么遇到this所指出的问题就很简单了。

希望通过本文的详细介绍,能帮助大家以后的发展与学习。

相关推荐

外贸网站建设要多少钱?外贸网站建设周期要多久

大家好,我是【无锡柠萌网络lemon56.com】的小美,今天分享:外贸网站建设要多少钱?外贸网站建设周期要多久您关注的问题·FAQ外贸网站建设中遇到的常见问题,让您少走弯路,提高效率!...

500块搭建一个可以卖货的跨境电商独立站,包括服务器和域名吗?

用500元搭建一个可以卖货的跨境电商独立站,虽然有一定挑战性,但通过精打细算和选择合适的工具和服务,仍然是有可能的。以下是一些建议,帮助你在预算范围内实现这个目标,包括服务器和域名的选择。...

无锡网络公司设计搭建阀门网站一般要多少钱?

无锡网络公司设计搭建阀门网站一般要多少钱?这是一个涉及多方面因素的复杂问题,其费用因网站规模、功能需求、设计复杂度及后续维护等因素而异。首先,从基础成本出发,域名注册是搭建网站的第一步,费用通常在几十...

定制化网站开发大概多少钱

定制化网站开发的价格因项目的复杂性、功能需求、设计要求和开发时间等因素而异。以下是一些常见的定制化网站开发价格范围,供参考:1.简单的定制化网站开发:一般来说,一个简单的定制化网站开发项目可能需要花...

9月安卓手机性能排行榜出炉:前十名差距仅为7%

时间已经进入9月,按照安兔兔目前的安卓手机性能榜单显示,红魔9SPro+依旧力压其他热销手机成为榜首,可见游戏定位的这款手机,在性能上确实非常的出色。不过,笔者也发现了一个问题,其中前十名中得分最高...

装修公司怎么在网络平台接单?实现高效获客

装修行业正经历着深刻的变革,随着消费者需求的日益多样化与个性化,传统的线下获客方式已难以满足装修公司的业务需求。因此,装修公司必须紧跟时代步伐,充分利用网络平台实现高效获客。1、入驻装修接单平台:壹品...

企业如何利用二维码进行线上营销?

#企业如何利用二维码进行线上营销?#企业利用二维码进行线上营销,可以从以下几个方面入手:?1.设计创意二维码?:...

家电销售该怎么线上拓客

社交媒体平台:利用微信、微博、抖音等平台,定期发布家电产品的信息、使用心得、促销活动等内容。引流靠手动一定是不行的,所以一般使用点软件肯定没错,最近用了款比较冷门的APP,"里德助手Plus...

pop社交软件,脱单软件排行榜

在交友软件里面脱单是非常明确的一件事,各年龄各社交平台上的女人都是有所不同的,具体问题具体分析一下。接下来就说说又哪些比较优质的恋爱脱单软件。...

手机处理器最新跑分排行榜,你的手机什么水平?

手机处理器最新跑分排行榜,你的手机什么水平?第1名:天玑9300第2名:骁龙8Gen3第3名:A17Pro第4名:天玑9200第5名:骁龙8Gen2第6名:骁龙8Gen1...

继番茄小说后,字节再推免费网文 App“蛋花小说”和“常读小说”

据Tech星球报道,字节跳动公司近期推出了两款全新的免费网文App,分别是“蛋花小说”和“常读小说”。这两款产品的开发公司分别为湖北福瑞兴网络科技有限公司和湖北聚合润网络科技有限公司,均为字节跳动的1...

全世界最好用的AI软件排名是?

Hey小伙伴们,今天咱们来聊聊那些让人爱不释手的AI神器!在这个智能化时代,谁还没几个拿得出手的AI软件呢?别急,我这就给你盘点一波全球超火的AI软件,保证让你大开眼界!...

手机root软件哪个成功率高?手机root软件排行榜2025

作为游戏玩家,欲在手游中畅玩无阻,root权限不可或缺,可优化画质、启用辅助。2025年已至,究竟哪些手机root软件能脱颖而出?哪款软件的root成功率会独占鳌头?一、手机root软...

独立站新手教程引流篇:如何优化谷歌广告投放效果?

随着谷歌广告单价的持续上涨,如何在提升投放效果的同时,降低推广费用成为每一个独立站卖家的必修课。因此新手卖家在完成初步的广告投放流程后,最重要的就是了解一下谷歌广告优化的基础策略。设置转化跟踪即利用G...

云媒易:干货知识分享!海外推广的渠道有哪些,如何正确的选择?

越来越多的海外企业或国内的跨境商家认识到海外的网络营销的作用,开展线上外贸营销,渠道是非常的重要,那么海外的推广渠道有哪些?企业应该如何选择呢?1、社交媒体推广社交媒体推广是现在海外推广方式中最热门的...

取消回复欢迎 发表评论: