写在前面:本帖用于this指向问题的编程题目,验证阶段性的学习成果,欢迎大家批评指正!
知识点:
- this指向问题要与原型/方法等知识点进行联系,需要针对不同情况进行学习。
- 箭头函数中的this会将上层的this继承下来,指向其最近的外层函数作用域的 this 所指对象
- 对象不构成单独的作用域。
- 最外层的执行对象是全局作用域window
- 立即执行函数的执行对象是window
- 调用其他函数时,若没有特殊指定,默认执行对象是window
- 严格模式下与非严格模式下有区别,注意区分
apply方法
- 作用,改变this指向,立即执行
- 参数会放在数组中,调用方式为 fun.apply(obj,[param1,param2,...])
call方法
- 作用,改变this指向,立即执行
- 参数以逗号分隔,调用方式为 fun.apply(obj,param1,param2,...)
bind方法
- 作用,改变this指向,需要再次调用
- fun.bind(obj,param1,param2,...);
- 多次bind只会使用第一次bind的结果。
new方法
- 作用,使用一个构造函数,创建一个新的实例对象并返回
- 工作流程,新建一个实例,将构造函数的this指向新的实例并执行,返回这个实例。
第1题:
知识点:函数嵌套调用
function foo() {
console.log( this.a );
}
function doFoo() {
foo();
}
var obj = {
a: 1,
doFoo: doFoo
};
var a = 2;
obj.doFoo()
2
在执行doFoo函数时会调用foo方法,在调用其他方法时且未特殊指定,this指向会全局,故打印this.a时会打印全局的a的值,即14行的2.
第2题
知识点:箭头函数
var a = 10
var obj = {
a: 20,
say: () => {
console.log(this.a)
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
10
10
此题涉及到箭头函数,执行上下文是window->obj.say,say箭头函数的this绑定到了父级所处的上下文,即window对象。
当执行8时,打印window.a 10
当执行11时,window->obj.say,this指向父级window,无法被bind改变,打印window.a 10,this指向不会改变。
变形1,箭头函数变为普通函数
var a = 10
var obj = {
a: 20,
say(){
console.log(this.a)
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
20
30
普通函数的this指向会发生改变
8:window->obj.say,this指向obj,打印obj.a 20
11:window->obj.say,apply改变了this指向,其上下文改变成了window->anotherObj.say,打印anotherObj.a 30
变形2,对象中调用外层函数
var a = 10
foo(){
console.log(this.a)
}
var obj = {
a: 20,
say: foo
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
20
30
正常输出,结果不变,
9:执行上下文是window->obj.say,this指向obj,打印obj.a
12:window->obj.say,apply改变this,window->anotherObj.say,打印anotherObj.a
但是,如果改为函数嵌套的形式,就变为了对象的方法中调用外层的方法,执行上下文就是window对象
var a = 10
function foo(){
console.log(this.a)
}
var obj = {
a: 20,
say(){
foo();
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
10
10
11:window->obj.say,函数中调用了foo方法,this指向变为了window,最终打印window.a 10
14: window->obj.say通过apply改变了上下文,window->anotherObj.say,但最后函数中调用了foo方法,this指向变为了window,最终打印window.a 10
如果此时的foo方法是箭头函数呢?
var a = 10
let foo = ()=>{
console.log(this.a)
}
var obj = {
a: 20,
say(){
foo();
}
}
obj.say()
var anotherObj = { a: 30 }
obj.say.apply(anotherObj)
10
10
第3题
知识点:new调用
function foo(){
console.log(this.name);
}
var obj = {
name: 'cuggz',
fun: foo
}
obj.fun() // cuggz
new obj.fun() // undefined
第九行的打印比较好理解,关键在于new关键字,会将fun作为构造函数来使用,会将foo的this绑定在新的object实例对象中,由于新建的实例并没有name属性,故打印undefined
第4题
知识点:对象嵌套调用
var obj = {
say: function() {
var f1 = () => {
console.log("1111", this);
}
f1();
},
pro: {
getPro:() => {
console.log(this);
}
}
}
var o = obj.say;
o();
obj.say();
obj.pro.getPro();
1111 window对象
1111 obj对象
window对象
15:window.say->f1,f1是箭头函数,this指向父级的上下文window
16:window->obj.say->f1,f1指向父级的obj,this指向obj
17:window->obj.pro.getPro,getPro箭头函数this指向外层的上下文,外层是pro对象,但是对象不构成上下文,所以this绑定在全局上下文window中。
如果f1是普通函数呢?
var obj = {
say: function() {
var f1 = function (){
console.log("1111", this);
}
f1();
},
pro: {
getPro:() => {
console.log(this);
}
}
}
var o = obj.say;
o();
obj.say();
obj.pro.getPro();
1111 window对象
1111 window对象
window对象
可以看到,由于在函数中调用函数,this指向全局对象window
变形1,在函数内部调用新的函数
var obj = {
say: function() {
var f1 = () => {
console.log("1111", this);
}
f1();
},
pro: function(){
let getPro = function(){
console.log(this);
}
getPro();
}
}
var o = obj.say;
o();
obj.say();
obj.pro();
1111 window对象
1111 obj对象
window对象
pro方法中调用getPro,上下文默认是window
变形2:在函数内部调用箭头函数
var obj = {
say: function() {
var f1 = () => {
console.log("1111", this);
}
f1();
},
pro: function(){
let getPro = ()=>{
console.log(this);
}
getPro();
}
}
var o = obj.say;
o();
obj.say();
obj.pro();
1111 window对象
1111 obj对象
obj对象
window->obj.pro.getPro,getPro的this指向在调用时确定,指向父级window
第5题
知识点:立即执行函数
var myObject = {
foo: "bar",
func: function() {
var self = this;
console.log(this.foo);
console.log(self.foo);
(function() {
console.log(this.foo);
console.log(self.foo);
}());
}
};
myObject.func();
bar bar undefined bar
13:window->myObject.func,func是普通函数,this指向myObject,此时的this和self指向myObject,打印bar
当执行立即执行函数时,this指向window,打印undefined,而self依旧指向myObject打印bar
当立即执行函数是一个箭头函数呢?
var myObject = {
foo: "bar",
func: function() {
var self = this;
console.log(this.foo);
console.log(self.foo);
(() => {
console.log(this.foo);
console.log(self.foo);
})();
}
};
myObject.func();
bar bar bar bar
看来箭头函数还是那么强大,此时的this并不是默认指向window全局对象。Window->myObject.func->立即执行,立即执行函数构成了单独的作用域,使用箭头函数则跳过了单独的作用域,指向父级的myObject,最后this和self都指向myObject.
第6题
知识点:立即执行函数
window.number = 2;
var obj = {
number: 3,
db1: (function(){
console.log(this);
this.number *= 4;
return function(){
console.log(this);
this.number *= 5;
}
})()
}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number); // 15
console.log(window.number); // 40
13:db1指向立即执行函数返回的函数.立即执行函数中的this指向全局作用域window,window.number*=4,变为8
14:执行db1,相当于在全局作用域下执行window.db1,this指向window,则window.number*=5,变为40
15:window->obj.db1,this指向obj,obj.number*=5,变为15
16:打印obj.number 15
17:打印window.number 40
变形1,立即执行函数返回的是箭头函数
window.number = 2;
var obj = {
number: 3,
db1: (function(){
// console.log(this);
this.number *= 4;
return ()=>{
// console.log(this);
this.number *= 5;
}
})()
}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number); // 3
console.log(window.number); // 200
13:跟之前分析一致,立即执行函数中this指向全局作用域,window,window.number*=4,变为8
14:跟之前分析一致,相当于在全局作用域下执行window.db1,this指向window,则window.number*=5,变为40
15:window->obj.db1,由于是箭头函数,this指向父级的Window,window.number*=5,变为200
16:打印obj.number 3
17:打印window.number 200
第7题
知识点:arguments参数列表
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
14:window->obj.method,由于函数内部调用了fn(),且fn是普通函数,默认执行上下文是window,打印window.length 10
10:arguments的下标0是fn方法,下标1是参数1,总共有两个参数,调用方法时,this指向arguments,打印arguments.length 2
第8题
知识点:函数嵌套调用
var a = 1;
function printA(){
console.log(this.a);
}
var obj={
a:2,
foo:printA,
bar:function(){
printA();
}
}
obj.foo(); // 2
obj.bar(); // 1
var foo = obj.foo;
foo(); // 1
13:window->obj.printA,由于printA函数是普通函数,打印obj.a 2
14:window->obj.bar,在bar函数中调用了printA函数,默认执行上下文是window,this指向window,打印window.a 1
16:默认执行上下文是window,this指向window,打印window.a 1
第9题
知识点:立即执行函数
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x = 5;
return function() {
return this.x;
}();
},
getY: function() {
var y = 7;
return this.y;
}
}
console.log(obj.getX()) // 3
console.log(obj.getY()) // 6
17:window->obj.getX,由于getX最后返回的是立即执行函数,所以需要先计算。在立即执行函数中,this默认指向window,返回window.x 3
18:window->obj.getY,getY是普通函数,this指向obj,最后返回obj.y 6
第10题
知识点:立即执行函数
var a = 10;
var obt = {
a: 20,
fn: function(){
var a = 30;
console.log(this.a)
}
}
obt.fn(); // 20
obt.fn.call(); // 10
(obt.fn)(); // 20
9:window->obt.fn,fn是普通函数,this指向obt,打印obt.a 20
10:call不传递值时默认是window,则打印window.a 10
11:加了括号,相当于是obt.fn();依旧打印obt.a 20
第11题
知识点:全局变量冲突
function a(xx){
this.x = xx;
return this
};
var x = a(5);
var y = a(6);
console.log(x.x) // undefined
console.log(y.x) // 6
8:执行5时,执行上下文window.a,this指向window,window.x=5,返回window。此时var x = a(5)相当于window.x=window,此处有些绕,大家仔细回味一下。window.x指向window,x.x==window.x.x
x===x.x===window.x 都指向window
9:执行6时,执行上下文window.a,this指向window,window.x=6,返回window,此时y指向window
最后打印结果时,x的值已经被改变为5,y指向window,所以x.x指向undefined,y.x为6
第12题
知识点:new函数
function foo(something){
this.a = something
}
var obj1 = {
foo: foo
}
var obj2 = {}
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
var bar = new obj1.foo(4)
console.log(obj1.a); // 2
console.log(bar.a); // 4
11:window->obj1.foo,this指向obj1,obj1.a 2
14:call改变了foo函数的指向,同理,obj2.a 3
17:new函数会暂时将foo方法绑定在新创建的实例上,为bar添加a属性,值为4。obj1本身的值不会改变
第13题
知识点:
function foo(something){
this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3
7:bind函数会绑定函数到该对象上,返回新的函数,此时bar的this指向obj1
8:window->obj1.foo,obj1.a 2
11:new暂时将this指向新创建的baz实例上,为baz添加新的属性a 3
12:obj1.a不受影响 2
13:baz.a 3
此时的foo的this指向哪里呢?
function foo(so){
this.a = so
}
var obj1 = {}
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
bar(3);
console.log(obj1.a); // 3
var baz = new bar(3);
console.log(obj1.a); // 3
console.log(baz.a); // 3
bar(4);
console.log(obj1.a); // 4
可见,foo的this依旧指向obj1