this一定会指向自身吗?
以前我也是认为this一定会指向函数本身,直到看到这个例子
function foo(){
console.log(this.a)
}
foo.a = 2;
var a=3;
foo()
咋一看,很简单啊,不就是输出2吗?其实不是的。试了一遍才知道是输出3。那如果a在函数里面呢?会不会输出2呢?
function foo(){
var a = 2;
console.log(this.a)
}
var a=3;
foo()
其实也不会。this只会查找全局作用域中的a。
this一定会指向函数作用域吗?
也不会,仔细看上面那个例子就知道了。a在函数里面,但是this.a输出的还是全局作用域中的a。当然,这样说法也不是很准确。因为作用域存在于JavaScript引擎内部,无法通过代码来访问。
this到底是怎么回事呢?
一定要知道this是在运行时绑定的,而不是在编写时绑定的。它的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
this到底如何判断呢?
1. 要明确函数的调用位置到底是在哪里?
例如:
function foo(){
// 当前调用位置是全局作用域
var a=4
foo.a="foo"
console.log(this.a)
boo()
}
function boo(){
var a=3
// 当前调用位置是foo
console.log(this.a)
boo.a="boo"
baz()
}
function baz(){
// 当前调用位置是boo
console.log(this.a)
}
var a= 2;
foo()
这个输出全都是 2 2 2。调用位置就是在当前正在执行的函数的前一个调用中。那么为什么全都输出2呢? 是因为foo()中的this绑定到了全局对象上,而后面一个函数又绑定到了前一个函数上,所以全都隐式绑定到了全局对象上。
2. 知道调用位置后,this的绑定规则又是什么
2.1 第一条:默认绑定
看例子
function foo(){
console.log(this.a)
}
var a = 2
foo()
这种函数前面没有调用对象的就是独立调用函数。此时,默认为window.foo()。this默认绑定为全局对象。但要注意,如果函数本身是严格模式,则this绑定为undefined。
2.2 第二条:隐式绑定
看例子
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo
}
var a=3;
obj.foo()
此时,输出结果是2。很明显,this没有绑定到全局对象,而是绑定到obj身上。很简单是吧。就是谁调用就绑定到谁身上。但是也不一定全都是这样的。例如下面这个
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo
}
var obj2={
a:3,
obj
}
var a=4;
obj2.obj.foo()
咋一看,很简单。谁调用就绑定谁。肯定输出为3,绑定到obj2上去了。但输出结果却是2,this还是被绑定到obj上去了。这是因为对象属性引用链中只有上一层或者说最后一层的调用位置会起作用。就是只看最后是谁调用的,上边的一律不管。当然,这个一律不管也包括当所要输出的变量并不存在于当前对象中时,也不会向上查找,而是输出undefined。 还有一种情况要考虑,比如下边这种:
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo
}
var a=1;
var aa = obj.foo
aa()
这种情况下,看似是this绑定到obj上,所以输出结果为2。实则是还是绑定到了全局对象,输出结果为1。为啥呢?仔细看这个var aa= obj.foo,此时,aa就等同于foo函数本身,因为对象赋值时是引用复制,所以此时相当于var aa=foo。然后就是独立函数调用,默认绑定到全局对象。这种情况就和上边的多个对象调用链一样。
2.3 第三条:显示绑定
顾名思义,就是明确的去刻意绑定this对象。主要方法有call,apply,bind。这三个方法的第一个参数都是指定的this绑定对象。用法就不一一介绍。要注意一点,当把null,undefined作为第一参数,则会默认绑定到全局对象。这里实现一下简易版bind函数
function foo(something){
return something + this.a
}
function bind(fn,obj){
return function(){
return fn.apply(obj,arguments)
}
}
let obj={
a:2
}
let baz = bind(foo,obj)
let sum = baz(2)
console.log(sum) // 4
2.3 第四条:new绑定
使用工厂函数或构造函数的时候没少用过。首先,使用new创建对象的时候,第一步是创建新的对象,第二步是这个新对象会被执行[[prototype]]链接,第三步这个对象会被绑定到函数的this,第四步如果这个函数没有返回对象,则自动把这个新对象返回出去。知道这四步骤后,就大概知道new绑定是个啥了。
这四种this绑定规则的优先级呢?
- 默认绑定优先级最低
- 显示绑定大于隐式绑定 例子:
function foo(){
console.log(this.a)
}
var obj={
a:2,
foo
}
var obj2={
a:3
}
obj.foo() //2 隐式绑定
obj.foo.call(obj2) //3 显示绑定能修改隐式绑定
- new绑定大于隐式绑定 例子:
function foo(a){
this.a=a
}
var obj={
foo
}
// console.log(obj.foo(3))
obj.foo(3)
console.log(obj.a) //3
var baz = new obj.foo(4)
console.log(baz.a) //4
- new绑定大于显示绑定
function foo(a){
this.a=a
}
var obj={}
var baz = foo.bind(obj) //显示绑定
baz(3)
console.log(obj.a) //3
var bar = new baz(4) //new 绑定
console.log(bar.a) //4
箭头函数中的this呢?
箭头函数中的this不会按照上面四种规则,而是根据外层函数来绝对this绑定对象。而且,这个this一旦绑定后就不能再进行修改,即没有优先级啥的,绑定完就不能再修改。看这个例子:
function foo(a){
return (a) => {
console.log(this.a)
}
}
var obj={
a:2
}
var obj2={
a:3
}
var baz = foo.call(obj)
baz() //2
baz.call(obj) //2
有哪里不对的,欢迎指出来。加油加油