详解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 相关文章推荐
在线游戏大家来找茬II
Sep 30 Javascript
javascript 面向对象继承
Nov 26 Javascript
工作中常用到的JS表单验证代码(包括例子)
Nov 11 Javascript
js检测网络是否具体连接功能的代码
May 23 Javascript
jquery控制表单输入框显示默认值的方法
May 22 Javascript
javascript之Array 数组对象详解
Jun 07 Javascript
windows下vue.js开发环境搭建教程
Mar 20 Javascript
vue 表单之通过v-model绑定单选按钮radio
May 13 Javascript
环形加载进度条封装(Vue插件版和原生js版)
Dec 04 Javascript
JavaScript中的类型检查
Feb 03 Javascript
JavaScript队列结构Queue实现过程解析
Mar 07 Javascript
mapboxgl区划标签避让不遮盖实现的代码详解
Jul 01 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
基于PHP常用文件函数和目录函数整理
2017/08/17 PHP
Yii2.0使用阿里云OSS的SDK上传图片、下载、删除图片示例
2017/09/20 PHP
Laravel使用Queue队列的技巧汇总
2019/09/02 PHP
JSON 编辑器实现代码
2009/12/06 Javascript
Knockoutjs的环境搭建教程
2012/11/26 Javascript
在图片上显示左右箭头类似翻页的代码
2013/03/04 Javascript
深入理解JavaScript系列(50):Function模式(下篇)
2015/03/04 Javascript
jquery简单实现图片切换效果的方法
2015/05/12 Javascript
jquery中表单 多选框的一种巧妙写法
2015/09/06 Javascript
JS代码实现根据时间变换页面背景效果
2016/06/16 Javascript
jQuery版AJAX简易封装代码
2016/09/14 Javascript
利用vue写todolist单页应用
2016/12/15 Javascript
JS计算两个时间相差分钟数的方法示例
2018/01/10 Javascript
详解在不使用ssr的情况下解决Vue单页面SEO问题
2018/11/08 Javascript
vue 开发之路由配置方法详解
2019/12/02 Javascript
python 示例分享---逻辑推理编程解决八皇后
2014/07/20 Python
python实现简单购物商城
2016/05/21 Python
简单了解python变量的作用域
2019/07/30 Python
关于python中plt.hist参数的使用详解
2019/11/28 Python
python 利用已有Ner模型进行数据清洗合并代码
2019/12/24 Python
基于python实现对文件进行切分行
2020/04/26 Python
python打开文件的方式有哪些
2020/06/29 Python
利用css3径向渐变做一张优惠券的示例
2018/03/22 HTML / CSS
HTML5之SVG 2D入门10—滤镜的定义及使用
2013/01/30 HTML / CSS
canvas仿写贝塞尔曲线的示例代码
2017/12/29 HTML / CSS
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
Belstaff英国官方在线商店:Belstaff.co.uk
2021/02/09 全球购物
PHP面试题附答案
2015/11/28 面试题
饭店工作计划书
2014/01/10 职场文书
服务员岗位职责
2014/01/29 职场文书
办公室文员工作职责
2014/01/31 职场文书
酒店大堂副理的职责范文
2014/02/13 职场文书
船舶工程技术专业求职信
2014/08/07 职场文书
幼儿园中班教师个人工作总结
2015/02/06 职场文书
两行代码解决Jupyter Notebook中文不能显示的问题
2021/04/24 Python
nginx实现多geoserver服务的负载均衡
2022/05/15 Servers