this指向的定义
this
是JavaScript中定义的关键字,作用就是谁调用指向谁,没用明确定义的对象的时候,就会指向默认对象,默认绑定全局对象。
还是对他的定义有不是很理解的地方,就让我们仔细看这篇文章吧。
this定义特点
- 函数在调用时,JavaScript默认给this绑定一个值
- this的绑定和定义的位置没有关系
- this的绑定和调用方式以及调用的位置有关
- this是在运行时被绑定的
从特点来看,this是调用函数才有,不调用就没有明确定义,在调用的时候调用方式和调用位置起到了重要作用。
this的绑定规则
既然是调用的时候才有,那么this绑定肯定有好多的绑定规则,并不是随便就能将this绑定到某个对象上。
this绑定规则又分为:
- 默认绑定
- 隐式绑定
- 显式绑定
- 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])
,传入的数组,这就是这两个的区别,相同点就是call
和apply
都是显式绑定。
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()
从上面代码中我们可以看到,打印的foo
是aaa
,而不是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面试题这篇文章。
感谢大家观看,我们下次见
十天看一部剧,还可以吧
@梦不见的梦 行,谢谢提醒,我优化一下
网站的速度有待提升,每次打开都要转半天还进不来呢
@React实战爱彼迎项目(二) - 程序员鸡皮 哪里有问题了,报错了吗?
@Teacher Du 那是怕你们毕不了业,我大学那会儿给小礼品
我们大学那会,献血还给学分~
@ab 我想去学网安,比如网警,但分也贼高😕
@夜 加油,你一样也可以成为程序员的,需要学习资料可以V我
佬发布的好多技术文章似乎我只能评论这篇,真正的程序员!!哇塞 我也想去献血,过两年就成年了可以去献血了