在第二章运行上下文(Execution Context)中咱们提升运行上下文的结构:java
function ExecutionContext() {
this.LexicalEnvironment = undefined;
this.VariableEnvironment = undefined;
this.ThisBinding = undefined;
}
复制代码
这篇就来聊聊这个ThisBinding。当前运行上下文的ThisBinding其实就是在当前运行上下文上执行代码里this的值。因此你在代码执行的时候遇到"this",就会来找当前运行上下文的这个ThisBinding做为this的值。那么,ThisBinding的值是多少呢?浏览器
要知道ThisBinding的值是多少,ExecutionContext何时被建立,建立时ThisBinding被设置为何。bash
而咱们又知道JS代码在三种状况下会建立ExecutionContext:app
可运行代码(Executable Code)
ECMAScript 5 规范,定义了三类可运行代码(Executable Code) ,运行这些代码时候会建立运行上下文(Execution Contexts):函数
- global code:就是js整个“程序”,就是源代码文件中全部不在function体中的代码。
- function code:就是函数体中的代码,除了内嵌函数体中的代码之外
- eval code : 就是传给内置eval函数的代码字符串
只要咱们了解运行这三种代码时候,建立了ExecutionContext的ThisBinding被设置为多少。就知道运行在该运行上下文的this的值了。ui
这个咱们提过:this
当JS引擎开始要进行global code代码运行以前,会先建立一个全局运行上下文(global execution context),并放入运行栈中:spa
//建立一个空的运行上下文 var globalExecutionContext = new ExecutionContext(); //建立全局词法环境 GlobalEnvironment = creatGlobalEnvironment(globalobject)//能够看做是浏览器环境下的window //设置运行上下文 globalExecutionContext.LexicalEnvironment = GlobalEnvironment; globalExecutionContext.VariableEnvironment = GlobalEnvironment; globalExecutionContext.ThisBinding = globalobject; Runtime.push(globalExecutionContext); //这时的Runtime是这样的: Runtime = { executionContextStack: [globalExecutionContext]; }; 复制代码
在进入程序代码以前,建立了全局运行上下文,其中的ThisBinding被设置为了全局对象:globalExecutionContext.ThisBinding = globalobject;prototype
因此在全局代码中的this的值为为全局对象(浏览器下为window)。code
在function code里this的值就比较多变了,咱们在函数调用一篇中提到,用不一样的调用方式调用函数后,进入function code以前会设置不一样thisArg,并传递到function code代码中 。进入function code后,会建立function的运行上下文,且设置其ThisBinding为thisArg。所以函数里的this的值,就和调用关系又很大的关系。
这里强调一下,函数中的this和函数的调用方式相关而与在哪被建立无关。与函数的词法环境的"静态"相比它是动态的。它之和当前运行上下文的ThisBings相关。
咱们在函数运行讲过:
那这五种调用方式,在进入函数代码运行以前,携带进去的,要做为this的"东西"都是啥呢?
- 带undefined 进去的:函数调用functionName();和 当即调用函数表达式(function(){})(),(function functionName(){})();
- 带对象进去的:
- 方法调用:如someObj.method() : 带someObj进去
- new functionName() 方式的调用:建立一个新对象 newObject,带进去
- functionName.call和functionName.apply:把call和apply指定thisArg带进去
判断携带进来的thisArg的值:
- 若是是strict,使barExecutionContext.ThisBinding = thisArg;
- 不是strict
- 若是thisArg是undefined,使barExecutionContext.ThisBinding = globalobject;
- 若是thisArg不是undefined,使barExecutionContext.ThisBinding = toObject(thisArg);
所以函数中this的值就有几种状况:
由于普通函数调用,包括调用functionName();和 当即调用函数表达式(function(){})(),(function functionName(){})();等,传到函数里的thisAarg是undefined。
所以,若是是非strict,则ThisBinding = globalobject;也就是:
针对如someObj.method() : someObj做为 thisArg传进函数代码里,在建立函数运行上下文的时候,ThisBinding = someObj。
所以在方法调用模式someObj.method()中的this,为someObj。
var a = 2;
var someObj = {
a:1
print:function(){
console.log(this.a)
}
}
var outPrint = someObj.print;
someObj.print(); //1
outPrint();//2
复制代码
outPrint()调用时,传进函数的thisArg是undefined,所在outPrint()的运行上下文中,ThisBinding被设置为全局对象,因此这时,this.a就是全局对象上的var a = 2;
而someObj.print()调用时,传进传进函数的thisArg是someObj,因此在someObj.print()运行上下文中,ThisBinding被设置someObj,因此这时this.a就是someObj上a:1。
注意,函数(箭头函数除外)里的this只和调用方式相关和在哪调用,函数在哪建立无关。
- new functionName() 方式的调用:建立一个新对象 newObject,带进去
在new方式调用的函数运行上下文中,ThisBinding = newObject,所以在new方式调用的函数中,this值就是新建立的对象newObject。
function Point(x, y) {
this.x = x;
this.y = y;
}
var p = Point(7, 5); // 没有new,普通调用,this为全局对象,在全局对象上建立x=7 y=5
var point = new Point(7, 5); //使用new调用,this为新建立的对象,在新对象上建立x=7,y=5
console.log(x); // 7
console.log(y); // 5
复制代码
call和apply调用,可以显示的传递给函数thisArg。其实不仅是Function.prototype.call和Function.prototype.apply这个两个函数能够给调用的函数显示传递thisArg参数,下列的方法都给函数调用显示传递提供thisArg:
须要注意的是若是传递给thisArg的null或者undefined,在非严格模式ixia函数中this仍是全局对象,记得咱们提到过,进入到函数代码,建立运行上下文以前的判断吗:
判断携带进来的thisArg的值:
- 若是是strict,使barExecutionContext.ThisBinding = thisArg;
- 不是strict
- 若是thisArg是undefined,使barExecutionContext.ThisBinding = globalobject;
- 若是thisArg不是undefined,使barExecutionContext.ThisBinding = toObject(thisArg);
var obj = {
a:1
};
function print() {
console.log(this);
}
print.call(null);//window
print.call(undefined);//window
print.call(obj);//obj
复制代码
Function.prototype.bind,的只要功能是建立一个和原函数同样body和[[scope]]的函数,可是它的this则绑定为bind函数的第一个参数。在新函数中,不管函数如何被使用,this都会永久绑定到bind的第一个参数。
function f() {
return this.a;
}
var g = f.bind({a: 'azerty'});
console.log(g()); // azerty
复制代码
箭头函数是ES6的新内容,这里讲到this,顺便提一下。 箭头函数的this保存为它被建立时运行上下文的this的值。在global下建立箭头函数,为箭头函数this为global,保持为它被建立时的this值。在function里建立也同样,它的this保持为在箭头函数被建立时function运行上下文的this的值。
这个和咱们前面提到的函数都不同,前面提到的函数都是和调用时的运行上下文相关,箭头函数的this却和建立时的运行上下文相关。
还有一点就是,对箭头函数调用call apply 和 bind 来绑定this是无效,没有效果。
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
var obj = {func: foo};
console.log(obj.func() === globalObject); // true
console.log(foo.call(obj) === globalObject); // true
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
复制代码
分两种状况:
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
function bluify(e) {
console.log(this);
}
var elements = document.getElementsByTagName('button');
elements[0].addEventListener('click', bluify, false);
复制代码
JS三种可运行代码,咱们已经提到过global、function,只上下eval还没提到。由于eval比较少用,咱们也不细究。这里大概讲下,进入eval代码之后,运行上下文的建立过程。
分两种状况:
eval("var definedInEval = 2;console.log(this);");//window
console.log(definedInEval); //2
var obj = {
method: function () {
eval('console.log(this)'); // obj
}
}
obj.method();
复制代码
var evalcopy = eval;
evalcopy("console.log(this);");
var obj = {
method: function () {
evalcopy('console.log(this)'); //window
}
}
obj.method();
复制代码