彻底搞懂上下文this😉
在讲this判断规则之前,我们先来理清一下this是什么??
1.1、this是什么🙃
面试题开始:
this是什么?到底this表达了什么?为什么要有this这个东西
我们先不直接回答这个问题,我们先看一个例子
代码一
<script>
var xiaoming = {
shuxue : 50,
yuwen : 89,
yingyu : 10
}
function calcTotalScore(o){
return o.shuxue + o.yuwen + o.yingyu;
}
var totalScore = calcTotalScore(xiaoming)
alert(totalScore)
</script>
我们定义了一个对象xiaoming,然后我们希望计算他的总分,于是我们定义了一个函数方法,可以实现这一需求。
>149
然后我们思考一个问题,通过上面这个例子,对象和函数是什么关系呢?。
你并不需要想得很复杂,对象被作为参数传入函数,函数从外面接受信息,主要依靠参数
问题是:不靠参数函数能不能从外部获取信息???
答案:其实这就是我们讲的上下文
上下文:就是使信息进入函数内部的一种手段
升级一下代码1
代码2
<script>
var xiaoming = {
shuxue : 50,
yuwen : 89,
yingyu : 10,
calcTotalScore:calcTotalScore
}
function calcTotalScore(){
return this.xue + this.yuwen + this.yingyu;
}
var score = xioaming.calcTotalScore();
alert(score)
</script>
这时候我们不通过参数,而是用的this,而用this取到xiaoming的值。于是我们定义了calcTotalScore:calcTotalScore
,再通过xioaming.calcTotalScore();
也就是判断上下文的规则2:对象打点,函数的this就是这个对象(至于其他规则,在后面会说明),当然结果是和代码1一样的。
>149
现在,我们改变了一下写法,函数没有参数了,现在信息可以通过this,这个词语进入到函数的内部,this就是函数的上下文。所以说,函数的上下文,是除了参数,最常用的使信息进入函数内部的手段
1.2、this 怎么判断??😎
首先要注意:function定义的函数,this是什么,要看如何调用,而不是看如何定义,调用方式决定了this
举个例子:
代码3
<script>
var obj = {
a : 3,
b : 4,
fn () {
alert(this.a);
}
}
</script>
这里我们定义了一个对象,包含a,b常量和一个函数弹出this.a
,现在问你this
是啥?
这个时候你可千万千万别乱了阵脚,我们上面已经着重强调了要看如何调用,而不是看如何定义,调用方式决定了this
这个函数在没有被调用之前,谁都不知道this
是啥
有的同学可能会说:这this
不是定义在对象里头吗,应该是输出3,其实不对不对。
来看下面
代码4
<script>
var obj = {
a : 3,
b : 4,
function () {
alert(this.a);
}
}
var a =8;
var f = obj.fn;
f();
</script>
我们在全局又定义了一个a = 8
,再定义一个f
去接收obj.fn
,并执行f()
输出结果
>8
写到这里我想我们应该清楚的是,在看面试题的过程中,当读到this时,我们不能急,要看下面如何调用。在上面的例子里,我们后面吧函数提出来,存入变量,之后 直接用f()
调用,此时上下文是window
1.3、 7个this 判断规则😏
规则1:函数直接用圆括号运行,上下文是window对象
fn(); //上下文是window
规则2:对象打点调用函数,上下文是这个对象
obj.fn(); //上下文是对象
就根据上面两个规则,让我们来看看一道题输出什么呢 3还是5反正不可能是4
<script>
var obj = {
a : 3,
b : 4,
fun : function () {
var a = 5;
return function(){
alert(this.a)
}
}
}
//一定别激动,必须看清楚函数如何被调用
obj.fun()();
</script>
但还会输出什么呢,我们来看看结果
>undefined
undefined
:为什么是输出这个,因为这里obj.fun()()
,回忆一下规则1:
函数直接用圆括号运行,上下文是
window
对象
此时的this上下文是window,但代码里并没有定义a的值,因此是undefined
当然如果我们在外面定义一个 a = 7
那么它弹出的就是7。
那什么时候应用规则2呢,还是上面那个代码,我们改改
<script>
var obj = {
a : 3,
b : 4,
fun : (function () {
var a = 5;
return function(){
alert(this.a)
}
})()
}
obj.fun();
</script>
此时用的就是我们的规则2了。
规则3:数组(类数组对象)中枚举出函数,上下文是这个数组
arr[idx]() 上下文是这个arr
举例:
<script>
function fun1(fn) {
//类数组对象中枚举出函数,然后运行。
//上下文是这个类数组对象
arguments[0](3,4);
}
function fun2() {
//fun2函数里面的this是fun1的实际参数列表
//实参长度是5
alert(this,length)
}
fun1(fun2,5,6,7,8)
</script>
此时this的上下文其实是 arguments
这个类数组对象,那它的长度为5
如果希望它弹出2的话应该是
alert(arguments.length) //输出2
规则4:定时器调用函数,上下文是window
规则5:被当做了事件处理函数,上下文是触发事件的DOM元素
规则6:用new调用函数,上下文是函数体内秘密创建的空白对象
用new调用函数,是经过四步走
- 秘密创建空对象
- 将this绑定到这个空对象中去
- 执行语句
- 返回这个空对象
规则7:用apply,call执行上下文
fn.apply(obj,[arg1,arg2]); 上下文obj
fn.call(obj,arg1,arg2); 上下文obj
写在后面👍👍👍
恭喜你看到后面,相信你一定收获了一些有关this的知识,笔者也是在学习,希望有不好的地方大家可以指正。
最后一道面试题结束:
<script>
function Foo() {
function getName() {
alert(1);
}
return this;
}
Foo.getName = function () {
alert(2);
}
foo.prototype.getName = function () {
alert(3);
}
var getName = function () {
alert(4);
}
function getName() {
alert(5)
}
//下面分别输出
getName();
Foo().getName();
new Foo().getName()
new Foo.getName()
new new Foo().getName();
</script>
1、getName();
//排除第一个getName()因为是闭包,所以轮不上它,因此只有4,5可能
//函数先提升,变量后提升,执行函数的时候,先执行变量=4
//变量只提升定义,不提升值
//程序执行的时候,函数是不解析
2、Foo().getName();
//规则1:函数直接运行,上下文是window
//window的getName()===window.getName()答案和上一题一样 =4
//Foo()返回上下文
3、new Foo().getName()
//考察:原型链查找,对象能够沿着原型链,访问自己的构造函数prototype上的属性方法
//new的优先级非常高
//先执行new Foo()
//1、创建空白对象
//2、返回空白对象this
4、new Foo.getName()
//new 调用一个普通函数 =2
5、new new Foo().getName();
//new Foo()优先
//.getName()
//new
//写在原型身上的函数也不是构造函数,是普通函数=3
正确答案是 4 4 3 2 3