JS之this的指向理解

1.this是什么?

this是对象自动生成的一个内部对象,是在运行时基于函数的执行环境绑定的,因为函数的调用场合不同,this的值也有变化。
this指向什么,完全取决于 什么地方以什么方式调用,而不是 创建时 。这句话目前也只能说在ES5中才是正确的,而在ES6的箭头函数中,this的指向就是在定义的时候就确定的。

2. this的绑定规则

this的绑定总共差不多有下面五种:

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new绑定
  5. ES6箭头函数中的this
2.1.默认绑定
function foo(){
    console.log(this.a);    // 10
}
var a = 10;
foo();

作为独立函数的调用,this指向的是全局对象window,而在严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined。只有函数运行在非严格模式下,默认绑定才能绑定到全局对象。

2.2.隐式绑定
function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
foo();  //undefined
obj.foo(); // 2

foo() 这个就是默认绑定,等价于打印 window.a ,故输出 undefined ;
obj.foo(),函数有上下文对象,即obj,这种情况下, 函数里的this默认绑定为上下文对象 ,等价于打印 obj.a ,故输出2 。如果是链性的关系,比如 xx.yy.obj.foo(); , 上下文取函数的直接上级,即紧挨着的那个,或者说对象链的最后一个。
再看下一个情况:

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名
var a =,10; // a是全局对象的属性
bar(); // 10

虽然bar为obj.foo的引用,但实际上引用的是foo函数本身,此时this指向为默认绑定,指向的window,所以输出10

2.3.显示绑定

显示绑定就是call apply bind,call和apply它们的作用都是改变函数的this指向 , 第一个参数都是 设置this对象 。
两个函数的区别:
call从第二个参数开始所有的参数都是 原函数的参数。
apply只接受两个参数,且第二个参数必须是数组,这个数组代表原函数的参数列表。

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2
};
foo.call( obj ); // 2  调用foo时强制把foo的this绑定到obj上

bind只有一个函数,且不会立刻执行,只是将一个值绑定到函数的this上,并将绑定好的函数返回。

function foo(){
    console.log(this.a);
}
var obj = { a : 10 };
foo = foo.bind(obj);
foo();                    // 10
2.4.new绑定

js中的只要用new修饰的 函数就是’构造函数’,准确来说是 函数的 构造调用 ,因为在js中并不存在所谓的’构造函数’。
那么用new 做到函数的 构造调用 后,js帮我们做了什么工作呢:

function createNew() 
    let obj = new Object()    // 创建一个空的对象
    let Con = [].shift.call(arguments)    // 获得构造函数
    obj.__proto__ = Con.prototype    // 链接到原型
    let result = Con.apply(obj, arguments)    // 绑定 this,执行构造函数
    return typeof result === 'object' ? result : obj    // 确保 new 出来的是个对象
}

1、创建(或者说构造)一个新对象。
2、这个新对象会被执行[[Prototype]]连接。
3、这个新对象会绑定到函数调用的this。
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

function foo(){
    this.a = 10;
    console.log(this);
}
foo();                    // window对象
console.log(window.a);    // 10   默认绑定
var obj = new foo();      //// 等价于 foo { a : 10 };  var obj = foo;                         
console.log(obj.a);       // 10    new绑定

特别注意 : 如果原函数返回一个对象类型,那么将无法返回新对象,你将丢失绑定this的新对象,例:

function foo(){
    this.a = 10;
    return new String("啊哈哈");
}
var obj = new foo();
console.log(obj.a);       // undefined
console.log(obj);         // "啊哈哈"
2.5 ES6箭头函数中的this

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改(new也不行)。

function foo() {
    return (a) => {         //返回一个箭头函数
        console.log( this.a );        // this继承自foo()
    };
}
var obj1 = {
    a: 2
};
var obj2 = {
    a: 3
}
var bar = foo.call( obj1 );
bar.call( obj2 );        // 2,不是3!

3. 优先级

new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

4. 总结

1.如果函数是在某个 上下文对象 下被调用:this绑定的是那个上下文对象,例 : var obj = { foo : foo }; obj.foo(); foo 中的 this 就是 obj
2.如果函数是使用 call,apply,bind 来调用的:this绑定的是 call,apply,bind 的第一个参数.例: foo.call(obj); , foo 中的 this 就是 obj
3.如果函数被 new 修饰:this绑定的是新创建的对象,例:var bar = new foo(); 函数 foo 中的 this 就是一个叫foo的新创建的对象 , 然后将这个对象赋给bar
4.如果都不是,则是默认绑定

5.常见题目分析

var x = 10;
var obj = {
    x: 20,
    f: function(){
        console.log(this.x);        //   20
                                            //隐式绑定,this指向上下文obj
        var foo = function(){ 
            console.log(this.x);    
            }
        foo();                      //10  默认绑定 指向的是window对象
    }
};
obj.f();
function foo(arg){
    this.a = arg;
    return this
};
var a = foo(1);
var b = foo(10);
console.log(a.a);    // ?undefined
console.log(b.a);    // ?10

分析:foo(1)执行,应该不难看出是默认绑定吧 , this指向了window,函数里等价于 window . a = 1,return window;
var a = foo(1) 等价于 window . a = window , 很多人都忽略了 var a 就是window.a ,将刚刚赋值的 1 替换掉了。
所以这里的 a 的值是 window , a . a 也是window , 即window . a = window ; window . a . a = window;
foo(10) 和第一次一样,都是默认绑定,这个时候, 将window.a 赋值成 10 ,注意这里是关键,原来window.a = window ,现在被赋值成了10,变成了值类型,所以现在 a.a = undefined。(验证这一点只需要将var b = foo(10);删掉,这里的 a.a 还是window)
var b = foo(10); 等价于 window.b = window;
本题中所有变量的值,a = window.a = 10 , a.a = undefined , b = window , b.a = window.a = 10;

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3;
        (function() {
            console.log(this.num); //默认绑定  指向的window   输出1
            this.num = 4;
        })();
        console.log(this.num);   
    },
    sub: function() {
        console.log(this.num)
    }
}
myObject.add();         
console.log(myObject.num); 
console.log(num);
var sub = myObject.sub;
sub();

依次输出 1 3 3 4 4

猜你喜欢

转载自blog.csdn.net/bangbanggangan/article/details/84779270
今日推荐