详解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 相关文章推荐
Javascript和Ajax中文乱码吐血版解决方案
Dec 21 Javascript
基于Unit PNG Fix.js有时候在ie6下不正常的解决办法
Jun 26 Javascript
Javascript中判断对象是否为空
Jun 10 Javascript
JavaScript中文件上传API详解
Apr 01 Javascript
JS实现图片的不间断连续滚动的简单实例
Jun 03 Javascript
jQuery禁用快捷键例如禁用F5刷新 禁用右键菜单等的简单实现
Aug 31 Javascript
javascript比较语义化版本号的实现代码
Sep 09 Javascript
jQuery 表单序列化实例代码
Jun 11 jQuery
Layui实现主窗口和Iframe层参数传递
Nov 14 Javascript
vue css 引入asstes中的图片无法显示的四种解决方法
Mar 16 Javascript
vscode自定义vue模板的实现
Jan 27 Vue.js
微信小程序实现聊天室功能
Jun 14 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
如何提高MYSQL数据库的查询统计速度 select 索引应用
2007/04/11 PHP
php CI框架插入一条或多条sql记录示例
2014/07/29 PHP
php+ajax实现无刷新分页的方法
2014/11/04 PHP
thinkphp5使用无限极分类
2019/02/18 PHP
php设计模式之适配器模式实例分析【星际争霸游戏案例】
2020/04/07 PHP
javascript引用对象的方法代码
2007/08/13 Javascript
fckeditor 获取文本框值的实现代码
2009/02/09 Javascript
JavaScript flash复制库类 Zero Clipboard
2011/01/17 Javascript
Jquery实现鼠标移上弹出提示框、移出消失思路及代码
2013/05/19 Javascript
在JavaScript中使用timer示例
2014/05/08 Javascript
javascript中数组array及string的方法总结
2014/11/28 Javascript
Javascript字符串浏览器兼容问题分析
2014/12/01 Javascript
js操作css属性实现div层展开关闭效果的方法
2015/05/11 Javascript
jQuery实现可用于博客的动态滑动菜单完整实例
2015/09/17 Javascript
Bootstrap按钮下拉菜单组件详解
2016/05/10 Javascript
点击按钮出现60秒倒计时的简单js代码(推荐)
2016/06/07 Javascript
js 函数式编程学习笔记
2017/03/25 Javascript
Vue.js结合Ueditor富文本编辑器的实例代码
2017/07/11 Javascript
jQuery中ajax获取数据赋值给页面的实例
2017/12/31 jQuery
js保留两位小数方法总结
2018/01/31 Javascript
Vue组件通信的四种方式汇总
2018/02/08 Javascript
Vue中使用vee-validate表单验证的方法
2018/05/09 Javascript
Vue中props的使用详解
2018/06/15 Javascript
浅谈vue方法内的方法使用this的问题
2018/09/15 Javascript
Vue创建头部组件示例代码详解
2018/10/23 Javascript
微信小程序如何引用外部js,外部样式,公共页面模板
2019/07/23 Javascript
详解vue 中 scoped 样式作用域的规则
2020/09/14 Javascript
使用Python写个小监控
2016/01/27 Python
用Python实现KNN分类算法
2017/12/22 Python
python3使用scrapy生成csv文件代码示例
2017/12/28 Python
python中的随机函数random的用法示例
2018/01/27 Python
Python中的单继承与多继承实例分析
2018/05/10 Python
python脚本调用iftop 统计业务应用流量的思路详解
2019/10/11 Python
巴西化妆品商店:Lojas Rede
2019/07/26 全球购物
自荐信如何制作?
2014/02/21 职场文书
裁员通知
2015/04/25 职场文书