Javascript this理解

来源:互联网 发布:js 99乘法表的思路 编辑:程序博客网 时间:2024/06/05 08:29

this 关键字

谈到作用域和闭包就不得不说 this 关键字,虽然它们之间关联不大,但是它们一起使用却容易让人产生疑惑。下面列出了使用 this 的大部分场景,带大家一探究竟。

this 是 JavaScript 的关键字,指函数执行时的上下文,跟函数定义时的上下文无关。随着函数使用场合的不同,this 的值会发生变化。但是有一个总的原则,那就是 this 指代的是调用函数的那个对象。

全局上下文

在全局上下文中,也就是在任何函数体外部,this 指代全局对象。

// 在浏览器中,this 指代全局对象 windowconsole.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.name

注意,在何处定义函数完全不会影响到 this 的行为,我们也可以首先定义函数,然后再将其附属到 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

ECMAScript 5 引入了 Function.prototype.bind。调用 f.bind(someObject) 会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久地被绑定到了 bind 的第一个参数,无论这个函数是如何被调用的。如下代码所示:

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

DOM 事件处理函数中的 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>

但在 IE 浏览器中,当函数使用 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 指向监听器所在的 DOM 元素。如下代码所示:

<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()()); 
0 0
原创粉丝点击