程序员鸡皮

文章 分类 评论
60 3 9

站点介绍

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

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

abzzp 2024-10-20 110 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)

最新评论

  • abzzp

    十天看一部剧,还可以吧[[呲牙]]

  • ab

    @梦不见的梦 行,谢谢提醒,我优化一下

  • 梦不见的梦

    网站的速度有待提升,每次打开都要转半天还进不来呢

  • abzzp

    @React实战爱彼迎项目(二) - 程序员鸡皮 哪里有问题了,报错了吗?[[微笑]]

  • abzzp

    @Teacher Du 那是怕你们毕不了业,我大学那会儿给小礼品[[发呆]]

  • Teacher Du

    我们大学那会,献血还给学分~

  • @ab 我想去学网安,比如网警,但分也贼高😕

  • ab

    @夜 加油,你一样也可以成为程序员的,需要学习资料可以V我

  • 佬发布的好多技术文章似乎我只能评论这篇,真正的程序员!!哇塞 我也想去献血,过两年就成年了可以去献血了

日历

2024年11月

     12
3456789
10111213141516
17181920212223
24252627282930

文章目录

推荐关键字: vue JavaScript React Golang 观后感 ES6 读后感

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