javascript中的函数(进阶4)

130 阅读4分钟

深入理解this

默认绑定

this默认指向window

1.全局环境下的this指向window

<script>
    console.log(this);//window
</script>

2.函数独立调用,函数内部的this也指向window

<script>
    function fn(){
    	console.log(this);//window    
    }
    fn();
    //window.fn();
</script>

3.被嵌套的函数独立调用时,this指向window

 var obj = {
        a:2,
        foo:function(){
            //函数当作对象的方法来调用时,this指向了当前对象obj。
            var that = this;
            console.log(that);
            function test(){
                //被嵌套的函数独立调用时,this指向window
                console.log(this);//window
                console.log(that.a);
            }
            test();
        }
  }
  obj.foo();

4.IIFE 内部的this指向window

var a=10;
function foo(){
    console.log(this)//obj
    (function test(that){
       //自执行函数 this指向window
       console.log(this);
       console.log(that.a);//2
   })(this);
}
var obj={
    a:2,
    foo:foo
}
obj.foo();

(function(){
    console.log(this);//window
})();

5.闭包中 this默认指向window

var a = 0;
var obj = {
    a:2,
    foo:function(){
        //this指向obj
        var c= this.a//2
        return function test(){
            //闭包this默认指向window
            console.log(this);
            return c;//2
        }
    }
    
}
var b = obj.foo(); 
b();//2

隐式绑定

function foo(){
    //函数当作对象的方法来调用时,this指向了它的直接对象 
    console.log(this.a);//1
}
var obj={
    a:1,
    foo:foo,
    obj2:{
    a:2,
    foo:foo
    }
    
}
//foo()函数的直接对象是obj,this指向直接对象
obj.foo();
//此时foo()函数的直接对象是obj2,this指向直接对象
obj.obj2.foo();

!!!隐式丢失

隐式丢失就是指被隐式绑定的函数丢失了绑定对象,从而默认绑定到了window上,这种情况是比较容易出错的,却又非常常见。

函数别名

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:1,
    foo:foo
}
//把obj.foo赋值给别名bar,会造成隐式丢失的情况,因为只是把obj.foo赋值给了变量bar,而bar与obj对象毫无关系。当前的this与obj没有关系
var bar=obj.foo;
bar();
//window.bar();


//等价于
var a =0;
var bar = function foo(){
    //函数独立调用,函数内部的this也指向window
    console.log(this.a);
}
bar();

参数传递

var a = 0;
function foo(){
    console.log(this.a);
}
function bar(fn){
    fn();
}
var obj = {
    a:1,
    foo:foo
}
//把obj.foo当作参数传递给bar(),有隐式的函数赋值 fn=obj.foo,只是把foo函数赋值给了fn,fn与当前的obj对象毫不相干,所以当前foo函数内部的this指向window
bar(obj.foo); 


//等价于
var a = 0;
function bar(fn){
    fn();
}
bar(function foo(){
    //内部的this指向了window
    console.log(this.a);
})

内置函数

内置函数setTimeout()&setInterval()的第一个参数(回调函数)中的this默认指向window,跟参数传递这种情况是类似的。

function foo(){
    console.log(this.a);
}
var a = 10;
var obj = {
    a:1,
    foo:foo
}
setTimeout(obj.foo,1000);

间接调用

function foo(){
    console.log(this.a);
}
var a = 2;
var obj = {
    a:3,
    foo:foo
}
var p = {
    a:4,
}
//相当于隐式绑定,函数当作对象中的方法使用,this指向当前直接对象
obj.foo();//3
//将obj.foo函数赋值给了p.foo,然后立即调用,相当于foo函数的立即调用,内部的this默认指向了window
(p.foo=obj.foo;)();//2
//将obj.foo函数赋值给p.foo函数,之后p.foo函数再执行,其实是属于p对象的方法的调用,此时this指向当前对象p
p.foo=obj.foo;
p.foo();//4

其他情况

指向了window的特殊情况

var a = 0;
var obj = {
    a:1,
    foo:foo
}
function foo(){
    console.log(this.a);
}
(obj.foo=obj.foo)();//0
(false||obj.foo)();//0
(1,obj.foo)();//0

显示绑定

call()、apply()、bind()

call()、apply()、bind()把对象绑定到this上,叫做显示绑定

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:1,
    foo:foo
};
foo();//window 0
foo.call(obj);//obj 1
foo.apply(obj);//obj 1
var fn=foo.bind(obj);//f{}
fn();//obj 1

硬绑定

是显示绑定的一个变种,使得this不能在被改变

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:1,
};
var bar = function(){
    foo.call(obj);
};
bar();//1
setTimeout(bar,1000);//1
bar.call(window);//1

数组的forEach(fn,对象),map(),filter(),some(),every(),第二个参数为绑定对象

var id = 'window';
var arr = [1,2,3];

function fn(el){
   console.log(el,this.id); 
}
var obj = {
    id:"fn",
}
arr.forEach(fn);//window
arr.forEach(fn,obj);//fn

new绑定

情况一

function fn(){
    //如果是new关键字来调用函数,相当于构造函数来实例化对象,内部的this指向了当前的实例化对象
    console.log(this);
}
var f = new fn();
console.log(f);

情况二

function fn2(){
    console.log(this);//当前的实例化对象
    //使用return关键字来返回对象时,实例化出来的对象是当前的返回对象
    return{
        name:'john',
    }
}
var f2 = new fn2();

情况三

var person = {
    fav:function(){
	return this;
    }
}
var p = new person.fav();
console.log(p,p===person);//false
//实例化出来的对象内部的属性constructor指向了当前的构造函数
console.log(p.constructor===person.fav);//true

严格模式下this的指向

独立调用的函数,内部的this指向undefined

function fn(){
    'use strict'
     console.log(this);   
}
fn();//undefined

函数apply()和call()

严格模式下,函数apply()和call()内部的this始终是他们的第一个参数

var color='red';
function showColor(){
    'use strict'
    console.log(this);
    console.log(this.color);
}
showColor.call(null);
showColor.call(undefined);

总结

  • 默认绑定
  • 隐式绑定
  • 显式绑定
  • new绑定

分别对应函数的四种调用方式

  • 独立的调用
  • 方法的调用
  • 间接调用
  • 构造函数调用

!隐式丢失

  • 函数别名
  • 函数作参数传入
  • 内置的函数
  • 间接调用