目录
一、普通函数的this指向
1、声明式 --- 指向的是Window
2、匿名函数 / 赋值式 --- 指向的是Window
3、定义在对象中的函数 --- 指向的是对象
4、绑定的事件处理函数 --- 指向的是绑定事件处理函数的标签
5、立即执行的函数表达式 --- 指向的是Window
6、闭包 -- 指向的是Window
7、函数赋值(隐式丢失)-- 指向的是Window
8、参数赋值的情况 -- 指向的是Window
9、当函数是参数的时候,也就是作为父函数的回调函数时,函数中 this 指向是由api接口文档中指明的 -- 由api接口文档指明
10、构造函数中的this -- 指向的是实例化之后的对象
11、普通函数 this 指向的示例
二、普通函数的this指向总结(四种绑定规则)
三、普通函数this指向优先值问题
四、箭头函数的this指向
1、箭头函数中,this 的指向是父级程序的this指向。
2、默认绑定规则(独立调用的方式)对箭头函数无效
3、隐式绑定规则(对象调用的方式)对箭头函数无效
4、显示绑定规则(call/apply/bind方式)对箭头函数无效
5、new不能实例箭头函数
6、总结
7、示例
五、如何改变this指向?
属于独立调用,所以指向Window
function fn1() {
console.log(this); // Window{}
}
fn1();
属于独立调用,所以指向Window
const fn2 = function() {
console.log(this); // Window{}
}
fn2();
被obj触发函数调用,所以指向obj对象
const obj = {
fn3: function() {
console.log(this);
}
}
obj.fn3();
监听的是oDiv的点击事件,所以指向oDiv
const oDiv = document.querySelector('div');
// oDiv.onclick = function(){
// console.log(this);
// }
oDiv.addEventListener('click' , function(){
console.log(this);
})
在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的this
具有默认值,该值指向Window
对象。
属于独立调用,所以指向Window
(function (){
console.log(this);
})(); // 打印 "window" 对象
// 或者:
(function (){
console.log(this);
}()); // 打印 "window" 对象
先执行 fn() , 这时等于 function test() {} ,最后实际是独立调用test方法,所以指向Window
function fn() {
function test() {
console.log(this) // Window
}
return test
}
fn()()
bar 其实是等于 function foo(){} ,最后实际是独立调用foo方法,所以指向Window
function foo() {
console.log(this) // Window
}
var obj = {
foo: foo
}
var bar = obj.foo
bar()
在预编译的过程中,实参被赋值为形参。是个值拷贝的过程,属于浅拷贝。
可以把实参fn看成赋值成为了 function foo() {} , 最后实际是独立调用foo方法,所以指向Window
function foo() {
console.log(this) // Window
}
function bar(fn) {
fn()
}
var obj = {
foo: foo
}
bar(obj.foo)
var obj = {}
let arr = [1, 2, 3]
arr.forEach(function() {
console.log(this) // Window
})
arr.forEach(function() {
console.log(this) // obj对象
}, obj)
function Person() {
this.do = function(e) {
console.log(e)
}
console.log(this) // 指向实例化之后的对象
}
var teacher = new Person()
teacher.do('教书')
var student = new Person()
student.do('学习')
当构造函数 return 引用值的时候,会改变this的指向。所以一般不在构造函数中 return
function Person() {
this.do = function(e) {
console.log(e)
}
this.b = '111'
console.log(this) // 指向实例化之后的对象
return {
a: 1
} // return的值为引用值的时候,会改变this的指向
}
var teacher = new Person()
console.log(teacher) // { a: 1 }
console.log(teacher.b) // undefined
示例1:
const obj = {
fn1() {
console.log(this)
function getName() {
console.log(this)
}
getName()
},
name: '小草莓',
fn2(callback) {
console.log(this)
callback()
}
}
obj.fn1() // 打印obj对象 打印 Window对象
obj.fn2(function() {
console.log(this) // 打印obj对象 打印 Window对象
})
示例2:
var a = 11
function fn() {
console.log(this)
this.a = 22;
let b = function() {
console.log(this)
console.log(this.a);
};
b();
}
var x = new fn();
结果: 第一个this打印:
第二个this打印Window对象;this.a 打印 11;
解析: 因为普通函数中的this指向的是调用它的对象,如果没有直接调用对象,会指向undefined或者Window,一般都会指向Window。在严格模式下才会指向undefined。
上述的例子中,并没有明确的调用对象,而普通函数中的this并不会向上继续找对象,所以直接返回Window。
① 默认绑定:
在全局中声明的变量和函数(默认指向Window);
函数独立调用时(声明式函数、匿名函数 / 赋值式的方式、立即执行函数、闭包)(都是指向Window);
② 隐式绑定:
对象调用(也就是谁调用就是指向谁,所以就是指向调用这个函数的对象)
(存在隐式丢失的问题:函数赋值和参数赋值的情况);
绑定的事件处理函数(指向的是绑定事件处理函数的标签);
③ 显式绑定:
call / apply / bind (指向第一个参数)
文章: 如何改变this指向?_小草莓蹦蹦跳的博客-CSDN博客
④ new绑定:
构造函数中的this指向实例化出来的对象;
毋庸置疑,默认绑定的优先级肯定是最低的。其次,比较下【显式绑定】和【隐式绑定】哪个优先级更高?
function foo(b) {
console.log(this.a)
}
var obj1 = {
a: 'obj1',
foo: foo
}
var obj2 = {
a: 'obj2',
foo: foo
}
obj1.foo() // obj1
obj2.foo() // obj2
obj1.foo.call(obj2) // obj2
obj2.foo.call(obj1) // obj1
由上面程序输入可以看出, 显式绑定的优先级高于隐式绑定
接着,就是比较一下【new绑定】和【显示绑定】哪个优先级更高?
function foo(b) {
this.a = b
}
var obj = {}
var bar = foo.bind(obj)
bar(2)
// 通过显式绑定更改了foo的this指向,使其指向了obj
// 而在foo函数中,this.a赋值为2,所以obj.a就等于2
console.log(obj.a) // 2
var barZ = new bar(3)
// obj.a已经赋值了,后续没有被改,所以依旧是2
console.log(obj.a) // 2
// 通过new绑定后,this指向了实例化后的对象,也就是barZ
// 而在foo函数中,this.a赋值为3,所以barZ.a就等于3
console.log(barZ.a) // 3
根据上面的程序输入可以看出,new绑定的优先级高于显式绑定
在实际开发中,几乎不会用到这种写法。
所以,new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
箭头函数内部是没有this指向的,箭头函数的this指向父级作用域的this;如果没有,则this指向的就是Window
function foo() {
console.log(this) // obj对象
// 情况一
// function test() {
// console.log(this) // Window
// }
// test()
// 情况二
// function test() {
// console.log(this) // obj对象
// }
// test.call(this)
// 情况三
var test = () => {
console.log(this) // obj对象
}
test()
}
var obj = {
a: 1,
foo: foo
}
obj.foo()
const obj = {
sayThis: () => {
console.log(this);
}
};
// 因为JavaScript没有块作用域,所以在定义sayThis的时候,里面的this就绑到window上去了
obj.sayThis(); // window
const globalSay = obj.sayThis;
globalSay(); // window 浏览器中的global对象
const liEle = document.querySelectorAll('li');
liEle.forEach((item, key) => {
// 箭头函数的this指向的是父级程序
// forEach()的this指向window
console.log('打印1', this) // Window
item.addEventListener('click', () => {
console.log('打印2', this) // Window
})
})
虽然箭头函数的this
能够在编译的时候就确定了this
的指向,但也需要注意一些潜在的坑!!
绑定事件监听:
可以看到,我们其实是想要this
为点击的 button
,但此时this指向了Window~
const button = document.getElementById('btn');
button.addEventListener('click', () => {
console.log(this) // Window
})
在原型上添加方法:
此时this指向Window~
function Cat(title) {
this.title = title
}
Cat.prototype.sayName = () => {
console.log(this) // Window
return this.title
}
const cat = new Cat('我是标题啊');
console.log(cat.sayName()) // undefined
function foo() {
console.log(this) // obj对象
var test = () => {
console.log(this) // obj对象
}
return test
}
var obj = {
a: 1,
foo: foo
}
obj.foo()()
function foo1() {
console.log(this) // obj对象
}
var foo2 = () => {
console.log(this) // Window
}
var obj = {
a: 1,
foo1: foo1,
foo2: foo2
}
obj.foo1()
obj.foo2()
function foo1() {
console.log(this) // obj2对象
}
var foo2 = () => {
console.log(this) // Window
}
var obj = {
a: 1,
foo1: foo1,
foo2: foo2
}
var obj2 = {
a: 2
}
obj.foo1.call(obj2)
obj.foo2.call(obj2)
function foo() {
console.log(this) // Window
var test = () => {
console.log(this) // Window
}
return test
}
var obj = {
a: 1,
foo: foo
}
var obj2 = {
a: 2,
foo: foo
}
foo().call(obj)
箭头函数不允许作为构造函数来使用
var Foo = () => {
console.log(this);
};
var a = new Foo(); // 报错Foo is not a constructor
所有绑定规则,在箭头函数中都不适用。
箭头函数本身不存在this指向,this取决于父环境中的this指向。
示例1:
const obj = {
// 普通函数:this指向调用它的对象
fn1: function() {
console.log(this) // {fn1: ƒ, fn2: ƒ, fn3: ƒ}
},
// 箭头函数:this指向是父级程序的this指向
// 父级程序是obj对象,但只有函数有this,obj对象没有this
// 父级程序没有this,指向的是window
fn2: () => {
console.log(this) // Window对象
},
// fn3是一个普通函数:this指向的是obj对象
fn3: function() {
// fn4是一个箭头函数:this指向的是父级程序的this指向
// 父级程序是fn3,fn3的this指向的是obj对象,所以fn4箭头函数的this也是指向obj对象
const fn4 = () => {
console.log(this) // {fn1: ƒ, fn2: ƒ, fn3: ƒ}
}
fn4()
}
}
obj.fn1()
obj.fn2()
obj.fn3()
示例2:
var x = 11;
var obj = {
x: 22,
y: this,
say: () => {
console.log(this.x);
}
}
obj.say();
console.log(obj.y);
结果: 先输出11,后输出window对象
解析:obj 对象中的 this 指向的就是 window,也就是全局环境。所以obj.y打印的是window对象;因为箭头函数中的 this 指向父级程序的指向。从以上例子可以看出来,父级obj指向了window,所以this.x打印的是11
示例3:
var a = 11;
function fn() {
this.a = 22;
let b = () => {
console.log(this.a)
}
b();
}
var x = new fn();
解析:箭头函数中会往上寻找this,直到找到所代表的this为止。例子中,构造函数被实例化成为一个对象x,那x中的this指代的就是对象x本身,所以箭头函数this就代表x对象,x对象内部的a值为22,所以输出22。
结果: 输出22
示例4:
结果:
1 2
Window Window
Window 2 Window
1 1 2
示例5:
function Foo() {
getName = function () {
alert(1);
};
return this;
}
Foo.getName = function () {
alert(2);
};
Foo.prototype.getName = function () {
alert(3);
};
var getName = function () {
alert(4);
};
function getName() {
alert(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
解析:
预编译中声明提升:function getName(){} 属于函数声明式,提升到最前面。
所以,全局的getName被替换成为function(){ alert(4) }
执行完Foo()后,函数返回了this,this指向window。所以Foo().getName()相当于window.getName(),而函数中的getName是全局的,执行函数的时候,替换掉了之前的输出为4的getName,当前就是输出1
结果:
2 4 1 1
2 3 3
如何改变this指向?_小草莓蹦蹦跳的博客-CSDN博客
如何改变this指向?_小草莓蹦蹦跳的博客-CSDN博客