《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
答案
obj
是通过 var
定义的, obj
会挂载到 window
之上的, obj.foo()
就相当于 window.obj.foo()
,这也印证了 this永远指向最后调用它的那个对象 规则。
题目2.2:对象链式调用
感觉上面总是空谈链式调用的情况,下面直接来看一个例题:
var obj1 = {
a: 1,
obj2: {
a: 2,
foo(){
console.log(this.a)
}
}
}
obj1.obj2.foo() // 2
3.隐式绑定的丢失
隐式绑定可是个调皮的东西,一不小心它就会发生绑定的丢失。一般会有两种常见的丢失:
-
使用另一个变量作为函数别名,之后使用别名执行函数
-
将函数作为参数传递时会被隐式赋值
隐式绑定丢失之后, this
的指向会启用默认绑定。
具体来看题目:
题目3.1:取函数别名
a = 1
var obj = {
a: 2,
foo() {
console.log(this.a)
}
}
var foo = obj.foo;
obj.foo();
foo();
JavaScript
对于引用类型,其地址指针存放在栈内存中,真正的本体是存放在堆内存中的。
上面将 obj.foo
赋值给 foo
,就是将 foo
也指向了 obj.foo
所指向的堆内存,此后再执行 foo
,相当于直接执行的堆内存的函数,与 obj
无关, foo
为默认绑定。笼统的记, 只要fn前面什么都没有,肯定不是隐式绑定 。
答案
不要把这里理解成 window.foo
执行,如果 foo
为 let/const
定义, foo
不会挂载到 window
上,但不会影响最后的打印结果
题目3.2:取函数别名
如果取函数别名没有发生在全局,而是发生在对象之中,又会是怎样的结果呢?
var obj = {
a: 1,
foo() {
console.log(this.a)
}
};
var a = 2;
var foo = obj.foo;
var obj2 = { a: 3, foo: obj.foo }
obj.foo();
foo();
obj2.foo();
obj2.foo
指向了 obj.foo
的堆内存,此后执行与 obj
无关(除非使用 call/apply
改变 this
指向)
答案
题目3.3:函数作为参数传递
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
用函数预编译的知识来解答这个问题:函数预编译四部曲前两步分别是:
-
找形参和变量声明,值赋予
undefined
-
将形参与实参相统一,也就是将实参的值赋予形参。
obj.foo
作为实参,在预编译时将其值赋值给形参 fn
,是将 obj.foo
指向的地址赋给了 fn
,此后 fn
执行不会与 obj
产生任何关系。 fn
为默认绑定。
答案
Window {…}
2
题目3.4:函数作为参数传递
将上面的题略作修改, doFoo
不在 window
上执行,改为在 obj2
中执行
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
console.log(this)
obj2.doFoo
xxx.fn
doFoo
this
obj2
{a: 3, doFoo: ƒ}
fn()
: 没有于obj2
产生联系,默认绑定,打印2
答案
{a: 3, doFoo: ƒ}
2
题目3.5:回调函数
下面这个题目我们写代码时会经常遇到:
var name=‘zcxiaobao’;
function introduce(){
console.log('Hello,My name is ', this.name);
}
const Tom = {
name: ‘TOM’,
introduce: function(){
setTimeout(function(){
console.log(this)
console.log('Hello, My name is ',this.name);
})
}
}
const Mary = {
name: ‘Mary’,
introduce
}
const Lisa = {
name: ‘Lisa’,
introduce
}
Tom.introduce();
setTimeout(Mary.introduce, 100);
setTimeout(function(){
Lisa.introduce();
},200);
setTimeout
是异步调用的,只有当满足条件并且同步代码执行完毕后,才会执行它的回调函数。
Tom.introduce()执行
console
setTimeout
this
window
Mary.introduce
setTimeout
题目3.3
this
Lisa.introduce
setTimeout
xxx.fn
this
答案
Window {…}
Hello, My name is zcxiaobao
Hello,My name is zcxiaobao
Hello,My name is Lisa
所以如果我们想在 setTimeout
或 setInterval
中使用外界的 this
,需要提前存储一下,避免 this
的丢失。
const Tom = {
name: ‘TOM’,
introduce: function(){
_self = this
setTimeout(function(){
console.log('Hello, My name is ',_self.name);
})
}
}
Tom.introduce()
题目3.6:隐式绑定丢失综合题
name = ‘javascript’ ;
let obj = {
name: ‘obj’,
A (){
this.name += ‘this’;
console.log(this.name)
},
B(f){
this.name += ‘this’;
f();
},
C(){
setTimeout(function(){
console.log(this.name);
},1000);
}
}
let a = obj.A;
a();
obj.B(function(){
console.log(this.name);
});
obj.C();
console.log(name);
本题目不做解析,具体可以参照上面的题目。
答案
javascriptthis
javascriptthis
javascriptthis
undefined
4.显式绑定
显式绑定比较好理解,就是通过 call()、apply()、bind()
等方法,强行改变 this
指向。
上面的方法虽然都可