详解JavaScript中this关键字的用法


Posted in Javascript onMay 26, 2016

this是函数内部的一个特殊对象,this引用的是函数据以执行的环境对象(关于环境对象我们会在文章最后作补充说明),在调用函数前this的值并不确定,不同的调用方式会导致this值的改变。

window.num = 22;
var o = {num: 11};
function sayNum(){
  alert(this.num)
}
sayNum();//22
o.sayNum = sayNum;
o.sayNum();//11

记住:函数名仅仅是一个包含指针的变量而已。因此即使是在不同的环境中执行,全局的sayNum()函数与o.sayNum()指向的仍然是同一个函数。

1.全局作用域中调用函数时
全局作用域中调用,this对象引用的是window
匿名函数的执行具有全局性,因此其this对象通常也指向window

function fn1(){
  console.log(this);
}

fn1();

2.通过new操作符调用
this引用的是实例对象

function Person(name){
  this.name = name;
}
Person.prototype.printName = function(){
  alert(this.name);//Byron
};

var p1 = new Person('Byron');

3.作为对象的方法调用
this引用的是该对象

var obj1 = {
  name: 'Byron',
  fn : function(){
    console.log(this);
  }
};

obj1.fn();

4.间接调用
call和apply
每个函数都包含两个非继承而来的方法: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方法
call方法与apply方法相同,区别在于接收参数的方式不同,对于call方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。

function fn(){
   console.log(this)//windwow
   function InnerFn(){
     console.log(this)
   }
   InnerFn.call(this)//window
}
fn();
 function fn0(){
   console.log(this)//window
}
function fn1(){
   fn0.call(this);
   console.log(this);//window
}
fn1();
function fn0(){
 console.log(this)//object
}
var o = {
  fn1: function fn1(){
    fn0.call(this);
    console.log(this);//object
  }
}
o.fn1();

5.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对象的属性和方法创建的。代码载入浏览器时,全局执行环境被创建(当我们关闭网页或者浏览器时全局执行环境才被销毁)。

2.局部执行环境
每个函数都有自己的执行环境,因此局部执行环境为函数对象。当函数被调用时函数的局部环境被创建(函数内的代码执行完毕后,该环境被销毁,同时保存在其中的所有变量和函数定义也随之被销毁)。

这个执行环境以及相关的变量对象是个抽象的概念,解释如下

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调用时局部执行环境创建

详解JavaScript中this关键字的用法

二、作用域链
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

作用域链(向上搜索):内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。

var name = 'Byron';
  function fn(){
    var name = 'Csper';
    console.log(name);//Casper
  }
  fn();

   
越往内部的环境,变量权重越高。

注意:没有带var关键字直接声明的变量属于全局变量如直接声明a = 1,此时的a为全局变量。

javscript引擎在进入作用域时,会对代码分两轮处理。第一轮,初始化变量。第二轮,执行代码

var a = 1;
function prison (a) {
  console.log(a);//1
  var a;
  console.log(a);//1
}
prison(1);

三、函数执行
函数调用进入执行环境时,首先处理arguments,初始化形参(默认值为undefined),然后初始化函数内的函数声明,当代码一步一步执行时再初始化函数内的变量声明(进入环境未开始执行代码时,值为undefined)。所以函数内的初始化顺序为形参,函数声明,变量声明。可以从上图图一看出。下面我来举个例子(整个全局环境也是函数)。

alert(typeof fn);//function,函数声明提前
alert(typeof fn0);//undefined,变量声明提前但未赋值
function fn(){
//函数表达式
}
var fn0 = function(){
//函数定义式
}
alert(typeof fn0);//function,此时变量已被赋值
Javascript 相关文章推荐
类之Prototype.js学习
Jun 13 Javascript
javascript提取URL的搜索字符串中的参数(自定义函数实现)
Jan 22 Javascript
jquery模拟SELECT下拉框取值效果
Oct 23 Javascript
js返回上一页并刷新的多种实现方法
Feb 26 Javascript
jQuery瀑布流插件Wookmark使用实例
Apr 02 Javascript
JQuery实现的购物车功能(可以减少或者添加商品并自动计算价格)
Jan 13 Javascript
脚本div实现拖放功能(两种)
Feb 13 Javascript
Angular 2父子组件数据传递之局部变量获取子组件其他成员
Jul 04 Javascript
Angular实现预加载延迟模块的示例
Oct 12 Javascript
vue+eslint+vscode配置教程
Aug 09 Javascript
nuxt.js写项目时增加错误提示页面操作
Nov 05 Javascript
JavaScript函数柯里化
Nov 07 Javascript
ashx文件获取$.ajax()方法发送的数据
May 26 #Javascript
js操作数据库实现注册和登陆的简单实例
May 26 #Javascript
js判断主流浏览器类型和版本号的简单实现代码
May 26 #Javascript
轻松掌握JavaScript中的Math object数学对象
May 26 #Javascript
JS表格组件神器bootstrap table详解(强化版)
May 26 #Javascript
JS在一定时间内跳转页面及各种刷新页面的实现方法
May 26 #Javascript
JavaScript的String字符串对象常用操作总结
May 26 #Javascript
You might like
虹吸壶煮咖啡26个注意事项
2021/03/03 冲泡冲煮
静态的动态续篇之来点XML
2006/12/23 Javascript
图片上传即时显示缩略图的js代码
2009/05/27 Javascript
页面中body onload 和 window.onload 冲突的问题的解决
2009/07/01 Javascript
jquery 延迟执行实例介绍
2013/08/20 Javascript
jquery上传插件fineuploader上传文件使用方法(jquery图片上传插件)
2013/12/05 Javascript
JavaScript中如何通过arguments对象实现对象的重载
2014/05/12 Javascript
初始Nodejs
2014/11/08 NodeJs
jquery ezUI 双击行记录弹窗查看明细的实现方法
2016/06/01 Javascript
深入浅析search 搜索框的写法
2016/08/02 Javascript
React实现点击删除列表中对应项
2017/01/10 Javascript
微信小程序 MD5的方法详解及实例代码
2017/03/10 Javascript
Bootstrap响应式导航由768px变成992px的实现代码
2017/06/15 Javascript
Vue源码学习之初始化模块init.js解析
2017/11/02 Javascript
Vue2.0仿饿了么webapp单页面应用详细步骤
2018/07/08 Javascript
AngularJS上传文件的示例代码
2018/11/10 Javascript
vue使用canvas实现移动端手写签名
2020/09/22 Javascript
[01:01:52]DOTA2-DPC中国联赛正赛 iG vs LBZS BO3 第一场 3月4日
2021/03/11 DOTA
Python函数的周期性执行实现方法
2016/08/13 Python
PyCharm代码整体缩进,反向缩进的方法
2018/06/25 Python
python sort、sort_index方法代码实例
2019/03/28 Python
python批量处理txt文件的实例代码
2020/01/13 Python
将pytorch转成longtensor的简单方法
2020/02/18 Python
PyTorch中model.zero_grad()和optimizer.zero_grad()用法
2020/06/24 Python
Python爬虫基于lxml解决数据编码乱码问题
2020/07/31 Python
python中HTMLParser模块知识点总结
2021/01/25 Python
CSS Grid布局教程之网格单元格布局
2014/12/30 HTML / CSS
CSS3解析抖音LOGO制作的方法步骤
2019/04/11 HTML / CSS
数据管理员的自我评价分享
2013/11/15 职场文书
董事长岗位职责
2013/11/30 职场文书
卖车协议书范例
2014/09/16 职场文书
晚会开幕词
2015/01/28 职场文书
学校勤俭节约倡议书
2015/04/29 职场文书
初中政治教学反思
2016/02/23 职场文书
python 自动刷新网页的两种方法
2021/04/20 Python
PostgreSQL自动更新时间戳实例代码
2021/11/27 PostgreSQL