详解JavaScript中this关键字的用法
本文详细介绍JavaScript中this关键字在不同调用场景下的指向规则与原理。涵盖直接调用、对象方法、构造器、以及call、apply和bind等方法的详细用法,并通过代码演示理解其动态绑定机制。
this关键字与调用模式
this是函数内部的一个特殊对象,它引用的是函数据以执行的环境对象。在调用函数前,this的值是不确定的,不同的调用方式会导致this值的改变。
1. 全局作用域中调用函数时
在全局作用域中调用函数,this对象引用的是window。匿名函数的执行具有全局性,因此其this对象通常也指向window。
window.num = 22;
var o = {num: 11};
function sayNum(){
alert(this.num);
}
sayNum(); // 输出 22
o.sayNum = sayNum;
o.sayNum(); // 输出 11
记住:函数名仅仅是一个包含指针的变量而已。因此即使是在不同的环境中执行,全局的sayNum()函数与o.sayNum()指向的仍然是同一个函数。
2. 作为对象的方法调用
当函数作为对象的方法被调用时,this引用的是该对象。
var obj1 = {
name: 'Byron',
fn : function(){
console.log(this); // 指向obj1
}
};
obj1.fn();
3. 通过new操作符调用
使用new操作符创建新实例时,this引用的是这个新的实例对象。
function Person(name){
this.name = name;
}
Person.prototype.printName = function(){
alert(this.name);
};
var p1 = new Person('Byron');
间接调用与绑定方法(call, apply, bind)
每个函数都包含两个非继承而来的方法:call()和apply()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。也就是说,直接调用函数,调用时指定执行环境是谁。
window.color = 'red';
var o = {color: 'blue'};
function sayColor(){
alert(this.color);
}
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue
(1)apply方法接收两个参数,一个是在函数中运行函数的作用域,另一个是参数数组。
(2)call方法与apply方法相同,区别在于接收参数的方式不同,对于call方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。
function fn(){
console.log(this); // window
function InnerFn(){
console.log(this); // window
}
InnerFn.call(this); // window
}
fn();
function fn0(){
console.log(this); // object
}
var o = {
fn1: function fn1(){
fn0.call(this);
console.log(this); // object
}
}
o.fn1();
(3)bind方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。也就是说会返回一个新函数,并且使函数内部的this为传入的第一个参数。
window.color = 'red';
var o = {color : 'blue'};
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
执行环境定义与作用域链
执行环境定义了变量或者函数有权访问的其他数据,每个执行环境都有一个与之相关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。我们编写的代码无法访问这个对象,但解析器会在处理数据时在后台使用它。
第1步:执行环境的分类与生命周期
全局执行环境:在web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。代码载入浏览器时,全局执行环境被创建(当我们关闭网页或者浏览器时全局执行环境才被销毁)。
局部执行环境:每个函数都有自己的执行环境,因此局部执行环境为函数对象。当函数被调用时函数的局部环境被创建(函数内的代码执行完毕后,该环境被销毁,同时保存在其中的所有变量和函数定义也随之被销毁)。
这个执行环境以及相关的变量对象是个抽象的概念,以下是一个具体的例子:
var a = 1;
function fn(num1,num2){
var b = 2;
function fnInner(){
var c = 3;
alert(a + b + c);
}
fnInner(); // fnInner调用时局部执行环境创建
}
fn(4,5); // fn调用时局部执行环境创建
第2步:理解作用域链的工作原理
Javascript函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的。当定义一个函数时,它实际保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的作用域链。
作用域链的前端始终都是当前执行的代码所在环境的变量对象,作用域链的末端始终都是全局执行环境的变量对象。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有权访问。
var scope = 'global scope';
function checkscope(){
var scope = 'local scope';
function f(){return scope};
return f;
}
checkscope()(); // local scope
理解:当调用checkscope时,函数f被定义并作为局部变量绑定到了checkscope作用域链上,因此函数f无论在哪里调用,这种绑定依然有效,因此返回值为local scope。
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。越往内部的环境,变量权重越高。
var num1 = 1;
function Outer(){
var num2 = 2;
console.log(num1 + num2); // 3
function Inner(){
//这里可以访问num3,num2,num1
var num3 = 3;
console.log(num1 + num2 + num3); // 6
}
//这里可以访问num2,Inner(),num1但不能访问num3
Inner();
}
Outer();
console.log(num1); // 1,执行环境
//这里只能访问num1
第3步:函数执行初始化顺序
Javascript引擎在进入作用域时,会对代码分两轮处理。第一轮,初始化变量。第二轮,执行代码。函数调用进入执行环境时,首先处理arguments,初始化形参(默认值为undefined),然后初始化函数内的函数声明,当代码一步一步执行时再初始化函数内的变量声明(进入环境未开始执行代码时,值为undefined)。所以函数内的初始化顺序为形参,函数声明,变量声明。
注意:没有带var关键字直接声明的变量属于全局变量,如直接声明a = 1,此时的a为全局变量。
alert(typeof fn); // function,函数声明提前
alert(typeof fn0); // undefined, 变量声明提前但未赋值
function fn(){
// 函数表达式
}
var fn0 = function(){
// 函数定义式
}
alert(typeof fn0); // function,此时变量已被赋值
第4步:变量提升的具体表现
下面来看一个简单的例子验证变量提升现象:
var a = 1;
function prison (a) {
console.log(a); // 1
var a;
console.log(a); // 1
}
prison(1);
以上就是this关键字的用法的详细内容,更多关于JavaScript this关键字的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













