程序员鸡皮
文章 分类 评论
186 3 34

站点介绍

一名PHP全栈程序员的日常......

Javascript高级(一) 函数中的this指向

abzzp 2024-10-20 785 0条评论 前端 JavaScript

首页 / 正文
本站是作为记录一名北漂程序员编程学习以及日常的博客,欢迎添加微信BmzhbjzhB咨询交流......

发布于2024-07-04

this指向的定义

this是JavaScript中定义的关键字,作用就是谁调用指向谁,没用明确定义的对象的时候,就会指向默认对象,默认绑定全局对象。
还是对他的定义有不是很理解的地方,就让我们仔细看这篇文章吧。

this定义特点

  1. 函数在调用时,JavaScript默认给this绑定一个值
  2. this的绑定和定义的位置没有关系
  3. this的绑定和调用方式以及调用的位置有关
  4. this是在运行时被绑定的

从特点来看,this是调用函数才有,不调用就没有明确定义,在调用的时候调用方式和调用位置起到了重要作用。

this的绑定规则

既然是调用的时候才有,那么this绑定肯定有好多的绑定规则,并不是随便就能将this绑定到某个对象上。

this绑定规则又分为:

  1. 默认绑定
  2. 隐式绑定
  3. 显式绑定
  4. new绑定

下面就具体了解这四种绑定方式:

默认绑定

默认绑定是什么呢?

当独立函数的调用的时候,可以理解成函数没有绑定到某个对象上进行调用。这就是我们的默认调用

我们看几个(常见的默认绑定)例子:

// 定义函数
function foo(name) {
      console.log("foo函数:", this)
}

// 1.方式一: 直接调用
foo()

上面这个我们就是window对象了,为什么呢,因为我们foo()这个函数是在window调用的。

我们再看另外一个:

 // 定义函数
 function foo(name) {
      console.log("foo函数:", this)
 }

// 2.方式二: 通过对象调起
var obj = { name: "why" }
obj.aaa = foo

obj.aaa()

在这里我们就是obj这个对象了,因为我们是在obj这个对象调用的。

隐式绑定

也就是它的调用位置中,是通过某个对象发起的函数调用。

让我们看几个例子:

 function foo() {
      console.log("foo函数:", this)
    }

var obj = {
      bar: foo
}

obj.bar()

这里的this就是foo对象了

显式绑定

如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用的时候,我们就需要使用显式绑定了。

常用的显式绑定有call和apply,还有不常用的显式绑定bind

call

那么我们的call显式绑定如何使用呢,请看下面:

// 显式绑定
var obj = {
      name: "why"
}

function foo() {
      console.log("foo函数:", this)
}

// 执行函数, 并且强制this就是obj对象
foo.call(obj)
foo.call(123)
foo.call("abc")

这里我们如果传入参数123,或者abc这种参数的话,this会自动生成一个数组或者字符串类,并且打印出来类型。

apply

apply如何使用呢,其实apply函数和call函数使用方式差不多,单也有点区别。

// call/apply
function foo(name, age, height) {
  console.log("foo函数被调用:", this)
  console.log("打印参数:", name, age, height)
}

// ()调用
// foo("why", 18, 1.88)

// apply
// 第一个参数: 绑定this
// 第二个参数: 传入额外的实参, 以数组的形式
// foo.apply("apply", ["kobe", 30, 1.98])

// call
// 第一个参数: 绑定this
// 参数列表: 后续的参数以多参数的形式传递, 会作为实参
foo.call("call", "james", 25, 2.05)

由上面代码我们可以总结出call函数传参是foo.call("call", "james", 25, 2.05)这样传递,然而apply参数传递是foo.apply("apply", ["kobe", 30, 1.98]),传入的数组,这就是这两个的区别,相同点就是callapply都是显式绑定。

bind

在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

下面我们看一个示例:

function foo(name, age, height, address) {
  console.log("foo:", this)
  console.log("参数:", name, age, height, address)
}

var obj = { name: "why" }

// 需求: 调用foo时, 总是绑定到obj对象身上(不希望obj对象身上有函数)
// 1.bind函数的基本使用
// var bar = foo.bind(obj)
// bar() // this -> obj

// 2.bind函数的其他参数(了解)
var bar = foo.bind(obj, "kobe", 18, 1.88)
bar("james")

从上面我们可以看到,bind种是将一个函数绑定到了内存中,然后重新将这块内存赋值到一个新的变量,this指向直接指向了obj这个对象。

new绑定

JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。下面我们来看一个例子:

/*
  1.创建新的空对象
  2.将this指向这个空对象
  3.执行函数体中的代码
  4.没有显示返回非空对象时, 默认返回这个对象
*/
function foo() {
  this.name = "why"
  console.log("foo函数:", this)
}

new foo()

那么上面这个this是绑定在那里呢?是我们的foo函数,为什么呢?因为是我们new了一个foo()函数,发起者是new foo()这个动作,假如我们改成下面这种写法:

/*
  1.创建新的空对象
  2.将this指向这个空对象
  3.执行函数体中的代码
  4.没有显式返回非空对象时, 默认返回这个对象
*/
function foo() {
  this.name = "why"
  console.log("foo函数:", this)
}

foo()

当我们没有new这个关键词的时候,当然就是指向我们的window对象了。大家明白了吧,那么我们这些个绑定方式优先级是什么样的呢?

规则优先级

this指向的优先级是:

1.默认规则的优先级最低
2.显式绑定优先级高于隐式绑定
3.new绑定优先级高于隐式绑定
4.new绑定优先级高于bind

总结来说就是,new绑定>显式绑定>隐式绑定>默认绑定。

如何来验证规则的优先级顺序呢?

显式绑定高于隐式绑定

function foo() {
   console.log("foo:", this)
}

// 1.显式绑定绑定的优先级高于隐式绑定
// 1.1.测试一:apply高于默认绑定
var obj = { foo: foo }
obj.foo.apply("abc")
obj.foo.call("abc")

上面我们可以看出, this 将会是 "abc",所以显式绑定大于隐式绑定。

比如我们的apply/call/bind函数,这里我们注意bind函数,他将是返回一个新的函数。

bind高于默认绑定

function foo() {
    console.log("foo:", this)
}
// 1.2.测试二:bind高于默认绑定
var bar = foo.bind("aaa")
var obj = {
 name: "why",
 baz: bar
}
obj.baz()

从上面代码中我们可以看到,打印的fooaaa,而不是window对象,所以bind是高于我们的默认绑定的。

这里包括后面我们都将充分利用这种方式来验证this的绑定优先级。

new绑定优先级高于隐式绑定

// 2.new绑定优先级高于隐式绑定
var obj = {
   name: "why",
   foo: function() {
      console.log("foo:", this)
      console.log("foo:", this === obj)
   }
}
new obj.foo()

这里打印出来的第一个this是我们的obj对象,接着判断当前环境是不是和obj完全相等,结果打印false,这里我们就可以断定new obj.foo()所执行的代码并不是隐式绑定所绑定的foo对像,而是重新生成了一个对象。所以,new绑定是高于隐式绑定的。

new优先级高于bind

我们先看下面这个例子:

function foo() {
  console.log("foo:", this)
}
var bindFn = foo.bind("aaa")
new bindFn()

执行我们会发现打印出的内容是{},而不是aaa,说明我们执行new绑定是要高于bind绑定的,我们如果将new去掉,可以发现打印出来的是aaa,这可以说明bind绑定是大于默认绑定

bind优先级高于apply/call

function foo() {
      console.log("foo:", this)
}
var bindFn = foo.bind("aaa")
bindFn.call("bbb")

从上面代码打印来看,打印内容是aaa,所以我们可以得出bind绑定优先级是要大于call\apply的,所以我们的优先级更加详细的来讲应该是:new绑定>显式绑定(其中bind>aplay/call)>隐式绑定>默认绑定。

这样我们的this绑定相关的就已经基本完善了,假如还是感觉有点迷惑,可以看JavaScript面试题这篇文章。

感谢大家观看,我们下次见

评论(0)

文章目录

最新评论

  • 什么是作用域链以及作用域链在javascript中的作用是什么? - 程序员鸡皮-前端程序员|PHP程序员|全栈程序员

    [...]回顾如何定义作用域链如何定义'作用域'?说明不同类型的作用域作用域链Scope Chain是JavaScript中的一个基本概念,它属于确定当前执行代码的上下文变量的查找和访问机制。在作用域的构建基于词法作用域的解构,即变量和函数的可见性由它们在源代码中的位置决定。在JavaScript中,每个执行上下文如函数执行上下文都绑有一个与之关联的作用域链。这个作用域就是一个包含多个环境记录Environ[...]

  • Hary

    哈喽,你的SSL好像过期喽

  • abzzp

    @秋风于渭水 确实[[微笑]]

  • 通常会采取哪些措施来确保网站或者应用在不同的浏览器上的兼容性? - 程序员鸡皮-前端程序员|PHP程序员|全栈程序员

    [...]不同的浏览器存在兼容性问题的核心原因是不同的浏览器可能使用的是不同的浏览器内核。在现代化开发中,大多数的浏览器兼容性问题是可以通过工程化中的配置选项来解决的。1.比如browserslist可以配置目标的浏览器或者Node环境,然后在不同的工具中起作用,比如autoprefixer/babel/postess preset env等,在进行了正确的配置后,开发的Vue或者React项目在进行打包时[...]

  • BFC的作用是什么呢? - 程序员鸡皮-前端程序员|PHP程序员|全栈程序员

    [...]在BFC中,box会在垂直方向上一个挨着一个的排布垂直方向的间距由margin属性决定在同一个BFC中,相邻两个box之间的margin会折叠(collapse)在BFC中,每个元素的左边缘是紧挨着包含块的左边缘的然后我们再看一下官方文档中如何说明的?总结BFC是什么?W3C文档讲:在标准流中,我们所有的盒子,不管是块级盒子还是行内盒子,它们都属于某一个FC格式化上下文,块级盒子属于BFC`块级格[...]

  • 什么是FC呢?他是用来干什么的? - 程序员鸡皮-前端程序员|PHP程序员|全栈程序员

    [...]什么是FC呢这里我们给出W3C给出的文档,FC文档FC的全称是FormattingContext,元素在标准流里面都是属于一个FC的。那么什么又是IFC,BFC呢?IFC行内元素的布局都属于Inline Formatting,inline level box都是在IFC中布局的BFCBFC英文全称是Block Formatting Context,也就是block level box都是在BFC中[...]

  • 秋风于渭水

    这确实是一个盲点,这个还是很有必要的,处理不好会导致网页内的元素出现抖动问题。

  • defer属性在javascript标签中有什么作用? - 前端程序员,PHP程序员,全栈程序员-程序员鸡皮

    [...]我们知道,当浏览器在执行到script标签的时候,首先会停止构建DOM树,然后下载Javascript文件并且执行,当JavaScript脚本执行完毕之后才会继续解析HTML标签构建DOM树。为什么Javascript程序会这样做呢?原因是我们的Javascript的作用就是操作DOM并且可以修改DOM。如果我们等到HTML执行完成之后再去执行JavaScript就会造成严重的回流和重绘,尤其是现[...]

  • async属性是什么?它有什么作用? - 前端程序员,PHP程序员,全栈程序员-程序员鸡皮

    [...]async属性和defer属性目标一样它也是为了不让js阻塞DOM树的构建。不过他们两个还是有区别的。async让js脚本的下载和执行是独立的。浏览器不会因为async属性的script脚本的执行而阻塞,这一点和defer属性类似。然而async属性比较任性,只要脚本被浏览器下载完成之后就会立即执行,不会等待在DOMContentLoaded之前执行。所以它不能保证是在DOMContentLoad[...]

  • 城市教堂

    我热爱 旅游专栏。令人惊艳了解路线。

日历

2026年04月

   1234
567891011
12131415161718
19202122232425
2627282930  

站点公告
本站是作为记录一名北漂程序员编程学习以及日常的博客,欢迎添加微信BmzhbjzhB咨询交流......
点击小铃铛关闭
配色方案