Javascript-this到底该如何理解

谈及 Javascript 中的 this,竟然让人觉得头疼,它不像 Java,C++ 中的 this 指向调用 this 的该对象。

在函数中 this 到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了。

因为 this 的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。当你在代码中使用了 this,这个 this 的值就直接从执行的上下文中获取了,而不会从作用域链中搜寻。


关于 this 的取值,大体上可以分为以下八种情况:


情况一:全局 & 调用普通函数


在全局环境中,this 永远指向 window。


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

普通函数在调用时候(注意不是构造函数,前面不加 new),其中的 this 也是指向 window。


但是如果在严格模式下调用的话会报错:


var x = 10;

function foo(){

    console.log(this);    // undefined

    console.log(this.x);  // Uncaught TypeError: Cannot read property 'x' of undefined

}

foo();

情况二:构造函数


所谓的构造函数就是由一个函数 new 出来的对象,一般构造函数的函数名首字母大写,例如像 Object,Function,Array 这些都属于构造函数。


function Foo(){

    this.x = 10;

    console.log(this);    //Foo {x:10}

}

var foo = new Foo();

console.log(foo.x);      //10


上述代码,如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象。

但是如果直接调用 Foo 函数,而不是 new Foo(),那就变成情况1,这时候 Foo() 就变成普通函数。

function Foo(){

    this.x = 10;

    console.log(this);    //Window

}

var foo = Foo();

console.log(foo.x);      //undefined

情况三:对象方法

如果函数作为对象的方法时,方法中的 this 指向该对象。

var obj = {

        x: 10,

    foo: function () {

        console.log(this);        //Object

        console.log(this.x);      //10

    }

};

obj.foo();

注意:若是在对象方法中定义函数,那么情况就不同了。

var obj = {

    x: 10,

    foo: function () {

        function f(){

            console.log(this);      //Window

            console.log(this.x);    //undefined

        }

        f();

    }

}

obj.foo();

可以这么理解:函数 f 虽然是在 obj.foo 内部定义的,但它仍然属于一个普通函数,this 仍指向 window。(这是个坑,要记牢)

在这里,如果想要调用上层作用域中的变量 obj.x,可以使用 self 缓存外部 this 变量。

var obj = {

    x: 10,

    foo: function () {

        var self = this;

        function f(){

            console.log(self);      //{x: 10}

            console.log(self.x);    //10

        }

        f();

    }

}

obj.foo();

如果 foo 函数不作为对象方法被调用:

var obj = {

    x: 10,

    foo: function () {

        console.log(this);      //Window

        console.log(this.x);    //undefined

    }

};

var fn = obj.foo;

fn();

obj.foo 被赋值给一个全局变量,并没有作为 obj 的一个属性被调用,那么此时 this 的值是 window。

情况四:构造函数 prototype 属性

function Foo(){

    this.x = 10;

}

Foo.prototype.getX = function () {

    console.log(this);        //Foo {x: 10, getX: function}

    console.log(this.x);      //10

}

var foo = new Foo();

foo.getX();

在 Foo.prototype.getX 函数中,this 指向的 foo 对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前对象的值。

情况五:函数用 call、apply或者 bind 调用。

var obj = {

    x: 10

}

function foo(){

    console.log(this);    //{x: 10}

    console.log(this.x);  //10

}

foo.call(obj);

foo.apply(obj);

foo.bind(obj)();

当一个函数被 call、apply 或者 bind 调用时,this 的值就取传入的对象的值。

情况六:DOM event this

在一个 HTML DOM 事件处理程序里,this 始终指向这个处理程序所绑定的 HTML DOM 节点:

function Listener(){ 

    document.getElementById('foo').addEventListener('click', this.handleClick);    //这里的 this 指向 Listener 这个对象。不是强调的是这里的 this

}

Listener.prototype.handleClick = function (event) {

    console.log(this);    //<div id="foo"></div>

}

var listener = new Listener();

document.getElementById('foo').click();

这个很好理解,就相当于是给函数传参,使 handleClick 运行时上下文改变了,相当于下面这样的代码:

var obj = {

    x: 10,

    fn: function() {

        console.log(this);        //Window

        console.log(this.x);      //undefined

    }

};

function foo(fn) {

    fn();

}

foo(obj.fn);

你也可以用通过 bind 切换上下文:

function  Listener(){

    document.getElementById('foo').addEventListener('click',this.handleClick.bind(this));     

}

Listener.prototype.handleClick = function (event) {

    console.log(this);    //Listener {}

}

var listener = new Listener();

document.getElementById('foo').click();

前六种情况总结一句话为: this 指向调用该方法的对象。

情况七:箭头函数中的 this

当使用箭头函数的时候,情况就有所不同了:箭头函数内部的 this 是词法作用域,由上下文确定。

var obj = {

    x: 10,

    foo: function() {

        var fn = () => {

            return () => {

                return () => {

                    console.log(this);      //Object {x: 10}

                    console.log(this.x);    //10

                }

            }

        }

        fn()()();

    }

}

obj.foo();

现在,箭头函数完全修复了 this 的指向,this 总是指向词法作用域,也就是外层调用者 obj。

如果使用箭头函数,以前的这种 hack 写法:

var self = this;

就不再需要了。

var obj = {

    x: 10,

    foo: function() {

        var fn = () => {

            return () => {

                return () => {

                    console.log(this);    // Object {x: 10}

                    console.log(this.x);  //10

                }

            }

        }

        fn.bind({x: 14})()()();

        fn.call({x: 14})()();

    }

}

obj.foo();

由于 this 在箭头函数中已经按照词法作用域绑定了,所以,用 call()或者 apply()调用箭头函数时,无法对 this 进行绑定,即传入的第一个参数被忽略。

补充说明:

this 为保留字,你不能重写 this。

function test(){

    var this = {};    //Uncaught SyntaxError: Unexpected token this

}

宿主对象:

一门语言在运行的时候,需要一个环境,叫做宿主环境。

对于JavaScript,宿主环境最常见的是 web 浏览器,浏览器提供了一个 JavaScript 运行的环境,这个环境里面,需要提供一些接口,好让 JavaScript 引擎能够和宿主环境对接。

JavaScript 引擎才是真正执行 JavaScript 代码的地方,常见的引擎有 V8(目前最快 JavaScript 引擎、Google 生产)、JavaScript core。

在浏览器或者服务端( nodejs )都有自己的 JS 引擎,在浏览器中,全局对象为 window,而在 nodejs 中,全局对象为 global。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容