this

this 关键字,虽然它们之间关联不大,但是它们一起使用却容易让人产生疑惑。下面列出了使用 thisthis 是 JavaScript 的关键字,指函数执行时的上下文,跟函数定义时的上下文无关。随着函数使用场合的不同,this 的值会发生变化。但是有一个总的原则,那就是 this

全局上下文

this

// 在浏览器中,this 指代全局对象 window
console.log(this === window);  // true

函数上下文

this

this
function f1(){
    return this;
}

console.log(f1() === window); // true

f1(),相当于为 window 对象定义了一个属性。直接执行函数 f1(),相当于执行 window.f1()。所以函数 f1() 中的 this 指代调用函数的那个对象,也就是 window

function f2(){
    "use strict"; // 这里是严格模式
    return this;
}

console.log(f2() === undefined); // true

this 关键字指向全局对象(在浏览器环境中也就是 window 对象),this 的值将维持 undefined

this
var o = {
    name: "stone",
    f: function() {
        return this.name;
    }
};

console.log(o.f()); // "stone"

o 中包含一个属性 name 和一个方法 f()。当我们执行 o.f() 时,方法 f() 中的 this指代调用函数的那个对象,也就是对象 o,所以 this.name 也就是 o.namethis 的行为,我们也可以首先定义函数,然后再将其附属到 o.f。这样做 this

var fun = function() {
    return this.name;
};

var o = { name: "stone" };
o.f = fun;

console.log(o.f()); // "stone"

this 的绑定只受最靠近的成员引用的影响。在下面的这个例子中,我们把一个方法 g() 当作对象o.b 的函数调用。在这次执行期间,函数中的 this 将指向 o.b。事实上,这与对象本身的成员没有多大关系,最靠近的引用才是最重要的。

o.b = {
    name: "sophie"
    g: fun,
};

console.log(o.b.g()); // "sophie"
eval() 方法中的 this

eval() 方法可以将字符串转换为 JavaScript 代码,使用 eval() 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval() 方法,调用者的执行环境中的 this 就被 eval()

// 全局上下文
function f1(){
    return eval("this");
}
console.log(f1() === window); // true

// 函数上下文
var o = {
    name: "stone",
    f: function() {
        return eval("this.name");
    }
};
console.log(o.f()); // "stone"
call() 和 apply() 方法中的 this

call() 和 apply() 是函数对象的方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this

var x = 0;  
function f() {    
    console.log(this.x);  
}  
var o = {};  
o.x = 1;
o.m = f;  
o.m.apply(); // 0

call() 和 apply() 的参数为空时,默认调用全局对象。因此,这时的运行结果为 0,证明 this

o.m.apply(o); // 1

1,证明了这时 this 指代的是对象 o

bind() 方法中的 this

Function.prototype.bind。调用 f.bind(someObject) 会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久地被绑定到了 bind

function f() {
    return this.a;
}

var g = f.bind({
    a: "stone"
});
console.log(g()); // stone

var o = {
    a: 28,
    f: f,
    g: g
};
console.log(o.f(), o.g()); // 28, stone
this

addEventListener,被用作事件处理函数时,它的 this

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
    <button id="btn" type="button">click</button>
    <script>
        var btn = document.getElementById("btn");
        btn.addEventListener("click", function(){
            this.style.backgroundColor = "#A5D9F3";
        }, false);
    </script>
</body>
</html>

attachEvent ,被用作事件处理函数时,它的 this 却指向 window。如下代码所示:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
    <button id="btn" type="button">click</button>
    <script>
        var btn = document.getElementById("btn");
        btn.attachEvent("onclick", function(){
            console.log(this === window);  // true
        });
    </script>
</body>
</html>
this

this

<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>

alert 会显示 button,注意只有外层代码中的 this 是这样设置的。如果 this

<button onclick="alert((function(){return this})());">
  Show inner this
</button>

this 被包含在匿名函数中,相当于处于全局上下文中,所以它指向 window

关卡

仔细想想,下面代码块会输出什么结果呢?

// 挑战一
function func1() {
    function func2() {
        console.log(this)
    }
    return func2;
}
func1()();  // ???
// 挑战二
scope = "stone";

function Func() {
    var scope = "sophie";

    function inner() {
        console.log(scope);
    }
    return inner;
}

var ret = Func();
ret();    // ???
// 挑战三
scope = "stone";

function Func() {
    var scope = "sophie";

    function inner() {
        console.log(scope);
    }
    scope = "tommy";
    return inner;
}

var ret = Func();
ret();    // ???
// 挑战四
scope = "stone";

function Bar() {
    console.log(scope);
}

function Func() {
    var scope = "sophie";
    return Bar;
}

var ret = Func();
ret();    // ???
// 挑战五
var name = "The Window";  
var object = {    
    name: "My Object",
    getNameFunc: function() {      
        return function() {        
            return this.name;      
        };    
    }  
};  
console.log(object.getNameFunc()());    // ???
// 挑战六
var name = "The Window";  
var object = {    
    name: "My Object",
    getNameFunc: function() {      
        var that = this;      
        return function() {        
            return that.name;      
        };    
    }  
};  
console.log(object.getNameFunc()());