深入理解函数内部原理(3)——动态的this

在阅读本博客之前先阅读:

深入理解函数之函数定义、调用、解析、执行 解剖http://blog.csdn.net/wmaoshu/article/details/60469571
深入理解函数之对一个函数实例进行深入的分析
http://blog.csdn.net/wmaoshu/article/details/60766030
本博客主要是深入讨论下js中this相关的东西。


JS中this是什么

正如在前面博客 执行环境 http://blog.csdn.net/wmaoshu/article/details/60466990
中介绍的那样,其实this的本质是作为执行环境的一部分存在的,又因为执行环境是在进入可执行代码区域开始执行代码的时候创建的,所以,this也就和代码的执行有关了,与代码的结构没有关系,所以this具有动态的特性,this的值是在执行的过程中确定的。
每一个执行环境与一个可执行代码块相关联,所以在这个可执行代码块中的所有出现的this标识符,最终解析成执行环境中this这个属性的值。
其实,this的相关内部原理已经在第一篇博客中深入的说明了,这里只不过总结一下。


不同环境下this值怎么判断

情况一:通过标识符形式函数调用

function add(a,b){
        return (a+b);
    };
add(1,2);

通过前面几篇博客想必这个函数调用的原理明白了,那么,我就从add(1,2);执行开始进行分析,对于在这条语句只想内部干了什么不清楚的可以看之前的博客。

当执行到add(1,2);时,是一个函数调用表达式,开始对调用运算符”()”之前的操作对象add进行标识符的解析,调用GetIdentifierReference函数,传入参数为 当前执行环境的此法环境 全局环境、查找的标识符字符串 add、是否严格查找 false。开始进行在作用域链中查找,最终返回一个引用{base:全局词法环境的对象式环境记录项,name:add,strict:false} 为Ref。
通过对Ref进行GetValue操作的到在全局对象中add的值为一个函数对象 F。并且检查一下是个函数。
然后重点来了,开始确定this。经过检查,Ref确实是一个引用类型,所以就开始取base值进行查看,发现是一个 对象式环境记录项,通过ImplicitThisValue得到的this值为undefined(因为全局对象中provideThis为false)。
调用[Call]方法后,传入this为undefined,和1,2,创建这个函数add 的执行环境addCTX,进入函数。
开始确定addCTX中this绑定(thisBind)的值,在这里对于是否在严格环境下对上一步传入来的this值进行调整。如果严格模式的话不做处理,直接这个thisBind的值为undefined,否则的话会转化为全局对象(window)。

<script>
/*"use strict";*/
    function add(a,b){
        console.log(this);/*严格模式下:window非严格模式下:undefined*/
        return (a+b);
    };
    add(1,2);
</script>

情况二:通过属性访问表达式的形式函数调用

<script>
var o = {
    "add": function(a,b){
        return (a+b);
    }
};
o.add(1,2);/*属性访问表达式两种不同的等价的方式,这种.的方式会最终转化成[]访问的方式*/
o["add"](1,2);
</script>

解析o[“add”]的时候,最终返回的是一个引用{base:o,name:add,strict:false};通过getValue的方式调用add属性值为F,确定F为一个函数之后,确定this。
如果得知base为一个对象,所以就会将这个对象作为this的值,也就是说在这一步this就是o。
然后调用[Call]传入this为o、1,2作为参数,创建一个F相关的执行环境,再严格模式和非严格模式下都是o,,所以这个执行环境的this绑定未o.

<script>
var o = {
    "add": function(a,b){
        console.log(this);/*Object {add: function}add: function (a,b)__proto__: Object*/
        return (a+b);
    }
};
o["add"](1,2);
</script>

情况三:通过除属性表达式之外的其他表达式(比如赋值表达式、函数表达式)形式调用

之前两种方式相比大家都耳熟能详了,接下来介绍这种方式掌握了就说明知道了js中this的本质。先上来介绍几个例子。

<script>
"use strict";  /*注意是严格模式下*/
(function(a,b){
        console.log(this); /*????*/
        return (a+b);
})(1,2);
</script>
<script>
"use strict";  /*注意是严格模式下*/
var o = {
    "add": function(a,b){
        console.log(this); /*????*/
        return (a+b);
    }
};
var a = null;
//这些分别是多少
(a = o["add"])(1,2);
(o["add"])(1,2);
(null,null,o["add"])(1,2);
(o?o["add"]:false)(1,2);
(o["add"]||false)(1,2);
</script>

其实这些本质就是取决于函数调用运算符“()”的操作数经过解析执行后是否是一个引用类型,如果是一个引用类型的可能为一个对象,如果不是引用类型的话在严格模式下必然是undefined,在非严格模式下为window。
对于第一个操作数是一个函数表达式来说,这个返回的是一个函数对象不是一个引用,通过getValue的方式的到的也是这个函数对象所以this为undefined。
对于下面的赋值运算符返回的也是一个赋值右边的值也是一个函数对象,还有逗号表达式也是,同样还有选择表达式和逻辑运算都是返回的值,如果有机会看ECMAScript官方文档的话,会发现这些表达式的计算步骤中都会将这些标识符通过getValue已经取值过了,所以都返回的是一个值而不是一个引用,只有(o[“add”])(1,2);重组表达式不一样,对于()运算目的是提高优先级并没有计算的意思,所以这种方式可o“add”;一样返回的人是一个引用{base:o,name:add,strict:false};

情况四:this与构造器相关,这个将会单独在下一篇博客 (通过new声明一个对象后都干了什么?)中介绍

再一个题目:

function add(){
    var value = 12;
    fun((function s(){  console.log(value)}) );
    s = null;/*防止内存泄漏*/
};
function fun(f){
    var value = -32;
    f();
}
add();

答案:12

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值