两年前端却不知this为何许?

       说来惭愧,再讲一个故事吧。带着2年前端经验的我去面试,被闭包问住了,尴尬了整个面试气氛。第二次,OK,失败了总要知道为什么吧,于是闭包被我简单的攻略了。面试官问我闭包时,拿起笔就写了一个简易版的闭包。好啊,既然你知道闭包,那你说说在你写的这个闭包的这一段代码里,这里,这里,这里this分别是什么?啊啊啊啊,老天,Are you kidding me?
    Q:

// (1)这里this指代什么?
function a(){
    // (2)这里this指代什么?
    var c = 2;
    function b(){
        // (3)这里呢?this又指代什么?
        return c;
    }
    return b;
}
a()();
A:(1)处指window,(2)处指的是a,(3)处指的是b。
面试官:嗯?是吗?你不好好想想吗?

我的内心:啊啊啊,哪里有洞。。。我是有多菜。

是,你没有看错,我确实是这么回答的,很可笑啊,我一直都觉得这真的是耻辱啊,我想我一辈子都不会忘记。不过,这个面试官很nice,给我道出了正确答案,还告诉我为什么。这场面试下来,我所有的自信心都被摧毁得破碎不堪,但它却是我最有收获的一次面试,也成为我前端路上的一次转折点。
        正确的应该是:这三处均指的是window对象。

       This,何意?这,这里,这个。在JavaScript中,它是关键字,既不是变量,也不是属性名,所以它是不允许被赋值的。那它是什么关键字呢?在JavaScript中,它指的是函数调用的上下文(context)。
        根据ECMAScript3和非严格的ECMAScript5对函数调用的规定,this的值是全局对象。在严格模式下,则是undefined。和变量不同,关键字没有作用域的限制,嵌套函数(闭包)不会从调用它的函数中继承this。若想访问外部函数的this值,需要将它的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。可看下面代码:

var o = {
    m: function(){
	var self = this;
	console.log(this === o); // true, this就是这个对象o
	f();
		
	// 定义一个嵌套函数f()
	function f(){
	    console.log(this === o); // false,this的值是全局对象或者undefined
	    console.log(self === o); // true, self值外部函数的this值
	}
    }
};
o.m();

       this是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,随着函数使用场合不同,this的值可能发生变化。我们知道,在JavaScript中,调用函数有4种方式:

  • 普通的函数调用
  • 作为对象的方法调用
  • 作为构造函数调用
  • 通过它们的call()和apply()间接调用

那么就从它的调用方式简单分析一下,如有错误,烦请批评指正:

(1)作为函数

这是最通常的函数用法,属于全局性调用,因此this等于全局对象。

function f(){
    window.x = 1;
    console.log(this.x);
}
f(); // 1

对代码做些改变再看,

var x = 1;
function f(){
    console.log(this.x);
}
f(); // 1
console.log(window.x); // 1

window.x与this.x值相等。再变一下,

var x = 1;
function f(){
    this.x = -1;
    console.log(this.x);
}
f(); // -1
console.log(x); // -1

在f()中给this.x赋值为-1,结果x的值跟着改变为-1,所以this等于全局对象。

(2)作为方法

当函数作为某个对象的方法调用时,这时this的值就是这个对象。

function f(){
    console.log(this.x);
}
var o = {
    x: -1,
    m: f
};
o.m(); // -1
o.x = 1;
o.m(); // 1

对象的属性值改变,f()里面this.x跟着改变。可以在f()里面打印一下this,看看结果呢!

(3)作为构造函数

作为构造函数调用时,使用new关键字初始化一个实例对象,这时this等于这个实例对象。

function f(){
    this.name = "zrn";
}
var o = new f();
console.log(o.name); // "zrn"

运行结果为”zrn”,表示这并非全局对象。变一下代码,

var name = "window";
function f(){
    this.name = "zrn";
}
var o = new f();
console.log(o.name); // "zrn"
console.log(name);  // "window"

name的值互不干涉。

(4)通过call()或者apply()调用

call()和apply()的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this的值。第一个参数(context)是函数运行的作用域,第二个参数是参数数组(argArr),不过使用call()时,要将这些参数逐个列举出来。如果未传递context,则context是全局对象(啊,说的有点多了)。

var name = "window";
function f(){
    console.log(this.name);
}
var o = {name: "zrn"};
f.call(); // "window", this指的是window
f.apply(o); // "zrn", this指的是对象o
结束语:以上就是我对this的基础理解了,不过理解与正确地使用又差一大截,所以只有不断地实践,不断地积累,才能够对this的使用信手拈来,达到炉火纯青的地步,所以,come on!
ps: call()和apply()方法是很强大的方法,期待更深入的学习它们。
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值