JS中this指向问题
-this四种绑定方式 #
- 1.默认绑定规则
- 2.隐式绑定规则
- 3.显示绑定规则bind、call、apply
- 4.new 绑定
优先级:4 > 3 > 2 > 1
1、默认绑定规则
默认绑定规则this指向window
1.1 全局环境
<script type="text/javascript">
console.log(this === window); //true
console.log({} === {}) //false
</script>
1.2 函数独立调用
this指向window
1.2.1 普通函数
<script type="text/javascript">
//函数独立调用
function test(){
console.log(this === window); //true
}
test();//等价于window.test()
</script>
1.2.2 立即执行函数
所有立即执行函数this指向window(执行环境是浏览器环境)
(function(){
console.log(this);//window
})()
(function(){
console.log(this);//window
}())
2、隐式绑定规则
隐式绑定规则谁调用this指向谁
2.1 普通隐式绑定
- 每一个函数执行,都会产生自身的this,
- this编译时不产生,执行时产生
- 他们的指向可能相同,但不是同一个this
<script type="text/javascript">
var a =0;
var obj = {
a:2,
foo:function(){
console.log(this); //obj
function test(){
console.log(this); //window
}
test(); //函数独立调用
}
}
obj.foo(); //obj调用foo()
</script>
2.2 闭包中this指向
产生闭包:当函数执行的时候,导致函数被定义,并抛出
<script type="text/javascript">
var a =0;
var obj = {
a:2,
foo:function(){
//console.log(this); //obj
function test(){
console.log(this); //window
}
return test;
}
}
obj.foo()();
//obj.foo() => return test();
//obj.foo()(); => test()//函数独立调用
</script>
2.3 隐式丢失
<script type="text/javascript">
var a =0;
function foo(){
console.log(this); //window
}
var obj = {
a:2,
foo:foo
}
obj.foo(); //obj
//隐式丢失或者理解为独立调用
var bar = obj.foo;
bar(); //window
// var bar = foo;
// bar();
</script>
2.4 函数赋值
s<script type="text/javascript">
var a =0;
function foo(){
console.log(this); //window
}
function bar(fn){
console.log(this); //window
fn(obj);
// fn.bind(obj)();
// new fn();
}
//父函数是有能力决定子函数的this指向的
var obj = {
a:2,
foo:foo
}
//预编译的过程中,实参被赋值为形参:(值的拷贝过程,浅拷贝)
bar(obj.foo);
</script>
2.5 父函数是有能力决定子函数的this指向的
父函数api中定义的
var arr = [1,2,3]
//回调函数
arr.forEch(function(item,index,arr){
console.log(this) //window
})
arr.forEch(function(item,index,arr){
console.log(this) //obj
},obj)
3、显示绑定规则
- bind、call、apply
<script type="text/javascript">
var a = 0;
function foo(a,b,c,d){
// console.log(this);
console.log(a,b,c,d);
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo;
obj.foo(1,2,3,4);
bar.call(obj,1,2,3,4);
bar.apply(obj,[1,2,3,4]);
bar.bind(obj)(1,2,3,4);
bar.call(undefined,2,3,4); //window
bar.apply(null,[1,2,3,4]); //window
</script>
4、new 绑定
<script type="text/javascript">
function Person(){
var this = {};
this.a = 1;
return this; //指向person
}
var person = new Person();
// console.log(person);
</script>
5、4种方式优先级
- 1.默认绑定规则
- 2.隐式绑定规则
- 3.显示绑定规则bind、call、apply
- 4.new 绑定
- 4 > 3 > 2 > 1
<script type="text/javascript">
function foo(b){
this.a = b;
}
var obj1 = {};
var bar = foo.bind(obj1); //foo的this指向obj1
bar(2); //obj1的this.a=2
console.log(obj1.a) //2
var baz = new bar(3);//baz是foo的浅拷贝
//new 用来实例化构造函数
//返回了一个新实例对象baz,baz的this指向自己,这个实例和obj1没有关系
console.log(obj1.a); //2
console.log(baz.a); //3
</script>
6、箭头函数this指向
箭头函数本身是没有this的,其this指向由其外层作用域决定,它里面的this其实是拿的父级作用域的this
所有绑定规则全部不适用于箭头函数
var a = 0
function foo(){
console.log(this) //obj
var test = ()=>{
console.log(this) //obj
}
test()
}
var obj1 = {
a:1,
foo:foo
}
var obj = {
a:2,
foo:foo
}
obj1.foo()
obj1.foo()()//默认绑定对箭头函数无效
var bar = foo().call(obj2) //window
箭头函数不允许作为构造函数使用
var foo = ()=>{
console.log(this)
}
new foo()
//报错,箭头函数不允许作为构造函数使用
7、this指向的使用
code
<script>
var name = 'window'
var obj1 = {
name: '1',
fn1: function () {
console.log(this.name)
},
fn2: () => console.log(this.name),
fn3: function () {
return function () {
console.log(this.name)
}
},
fn4: function () {
return () => console.log(this.name)
}
}
var obj2 = {
name: "2"
}
obj1.fn1(); //1
obj1.fn1.call(obj2) //2
obj1.fn2(); //window
obj1.fn2.call(obj2) //window
obj1.fn3()() //window
obj1.fn3().call(obj2) //2
obj1.fn3.call(obj2)() //window
obj1.fn4()() //1
obj1.fn4().call(obj2) //1
obj1.fn4.call(obj2)() //2
</script>
code
<script>
function getName() {
//全局方法在编译时被提升到最上面,之后被4覆盖
console.log(5)
}
function Foo(){
getName = function (){
console.log(1)
};
return this
}
Foo.getName = function () {
console.log(2)
};
Foo.prototype.getName = function () {
console.log(3)
};
var getName = function() { //表达式
console.log(4)
}
// function getName() { //函数声明
// //函数声明编译时被提升到最上面,之后被4覆盖
// console.log(5)
// }
console.log(Foo())
Foo.getName(); //2
getName(); // 4 5=>4(先走5最后4覆盖了5)
Foo().getName(); //全局的getName被重写成1 5=>4=>1
getName();//1 5=>1
new Foo.getName(); //2
new Foo().getName() //3
new new Foo().getName() //3
</script>