JavaScript中this指向问题

this是在运行时绑定的。函数调用方式的不同就可能使this所绑定的对象不同

一.几种绑定规则

Ⅰ.默认绑定

  • 一般情况下,若无其他规则出现则默认将this绑定到全局对象上
function foo(){
    var a = 3;
    console.log(a);
}
var a = 2;
foo();      //2

//foo()在调用时使用不带任何修饰的函数引用,只能使用默认绑定
  • this常有的集中错误理解之一就是认为this指向当前函数的词法作用域

  • this与词法作用域及作用域对象是完全不同的东西

Ⅱ.隐式绑定

  • 若在调用位置有上下文对象(可理解为此函数调用时使用一个对象.出来的),就遵循隐式绑定
function foo(){
    console.log(a);
}
var obj = {a:2, foo:foo};
var a = "opps,global";  //全局对象的属性
obj.foo();              //2     隐式绑定
var bar = obj.foo;      //这里进行了一次引用赋值
bar();                  //opps,global

如果细心的话你会发现,上边的代码出现了一个问题:隐式丢失

  • 当进行隐式绑定时,如果进行一次引用赋值或者传参操作,会造成this的丢失,使this绑定到全局对象中去。

由于第7行处进行了以此引用赋值,所以造成了bar()不具有上下文对象,故系统会自动采取默认绑定规则

我们再来举一个传参操作造成隐式丢失的例子

function foo(){
    console.log(this.a);
}
function doFoo(fn){
    fn();               //在此调用时参数传递为隐式赋值,丢失this绑定
}
var obj = {a:2, foo:foo};
var a = "opps,global";
doFoo(obj.foo);         //看似为隐式绑定,但输出的是opps,global  

所以我们需要记住:函数传递就是一种隐式赋值,而隐式赋值会造成隐式丢失

同样,js中的一些内置函数在接受一个函数作为参数时恩惠发生以上情况

function setTimeout(fn, delay){
    //等待delay毫秒数
    fn();
}

Ⅲ.显式绑定

使用apply和call为要执行的函数直接绑定this称为显式绑定

function foo(){
    console.log(this.a);
}
var obj = {a:2};
foo.call(obj);      //2
  • PS:
    • 若给call/apply传入的是一个基本类型数据,则这个基本类型数据将被转换成对应的基本包装类型
    • 此种方法仍然无法解决绑定丢失问题
1.硬绑定
function foo(){
    console.log(this.a);
}
var obj = {a:2};
var bar = function(){       //创建一个包裹函数以确保obj的绑定
    foo.call(obj);
};
bar();                      //2
setTimeout(bar, 1000);      //时隔一秒打印2
bar.call(window);           //2

通过此方法(包裹函数)可以确保obj的绑定,不过韩式不能为函数传参

function foo(something){
    console.log(this.a, something);
    return this.a + something;
}

function bind(fn, obj){     //辅助绑定函数
    return function(){
        return fn.apply(obj, arguments);
    };
}

var obj = {a:2};
var bar = bind(foo, obj);
var b = bar(3);
console.log(b);

//2 3
//5
  • ES5中内置了bind方法(其实就是个语法糖)。其返回一个硬编码的新函数,将你制定的对象绑定到调用它的函数的this上
function foo(something){
    console.log(this.a, something);
    return this.a + something;
}

function bind(fn, obj){     //辅助绑定函数
    return function(){
        return fn.apply(obj, arguments);
    };
}

var obj = {a:2};
var bar = foo.bind(obj);    //bind返回一个绑定到obj上的新函数
var b = bar(3);
console.log(b);
var a = "window's a";
foo("!");               //对原来的函数不产生影响


//2 3
//15 5
//window's a !
//"window's a!"
6.API调用参数指定this
function foo(e1){
    console.log(e1, this.id);
}
var obj = {id:"Sir"};
[1,2,3].forEach(foo, obj);      //forEach第二个参数用来设置this


//1 "Sir"
//2 "Sir"
//3 "Sir"

Ⅳ.new绑定

function foo(){
    this.a = 1;
    this.b = 2;
}
var instance = new foo();
console.log(instance.a);
发生函数的构造调用时自动执行以下操作
  • 创建一个全新的对象
  • 此对象会被执行[[Prototype]]链接
  • 此新对象会绑定到函数调用的this(new绑定)
  • 执行此函数代码
  • 若函数无返回值,则自动返回此新对象

Ⅴ.箭头函数的this

function foo(){
    return (a)=>{
        console.log(this.a);
    }
}
var obj1 = {a:2};
var obj2 = {a:3};
var bar = foo.apply(obj1);
bar.apply(obj2);        //2
  • 箭头函数是ES6中的语法。无法使用以上几种规则。
  • 其根据外层的作用域来决定this
  • 箭头函数的绑定无法修改,new绑定也不行

猜你喜欢

转载自blog.csdn.net/qq_38722097/article/details/80463433