详解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编程语言的编码规范
Oct 21 Javascript
jquery实现鼠标滑过小图查看大图的方法
Jul 20 Javascript
如何实现JavaScript动态加载CSS和JS文件
Dec 28 Javascript
原生js模拟淘宝购物车项目实战
Nov 18 Javascript
jquery实现ajax加载超时提示的方法
Jul 23 Javascript
Bootstrap作品展示站点实战项目2
Oct 14 Javascript
浅谈React 属性和状态的一些总结
Nov 21 Javascript
JavaScript调试之console.log调试的一个小技巧分享
Aug 07 Javascript
JS中Swiper的使用和轮播图效果
Aug 11 Javascript
基于Swiper实现移动端页面图片轮播效果
Dec 28 Javascript
原生js实现无缝轮播图
Jan 11 Javascript
解决vue打包报错Unexpected token: punc的问题
Oct 24 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过滤危险html代码
2008/08/18 PHP
PHP判断数组是否为空的常用方法(五种方法)
2017/02/08 PHP
php数据库的增删改查 php与javascript之间的交互
2017/08/31 PHP
js 幻灯片的实现
2011/12/06 Javascript
Javascript模块化编程(一)AMD规范(规范使用模块)
2013/01/17 Javascript
js控制表单奇偶行样式的简单方法
2013/07/31 Javascript
js+css实现的简单易用兼容好的分页
2013/12/30 Javascript
js 中将多个逗号替换为一个逗号的代码
2014/06/07 Javascript
JS对字符串编码的几种方式使用指南
2015/05/14 Javascript
jquery超简单实现手风琴效果的方法
2015/06/05 Javascript
JS实现仿FLASH效果的竖排导航代码
2015/09/15 Javascript
jquery.qtip提示信息插件用法简单实例
2016/06/17 Javascript
AngularJS ng-mousedown 指令
2016/08/02 Javascript
easyUI实现(alert)提示框自动关闭的实例代码
2016/11/07 Javascript
微信小程序 自己制作小组件实例详解
2016/12/22 Javascript
微信小程序 es6-promise.js封装请求与处理异步进程
2017/06/12 Javascript
关于TypeScript中import JSON的正确姿势详解
2017/07/25 Javascript
详解Vue一个案例引发「内容分发slot」的最全总结
2018/12/02 Javascript
JS中实现浅拷贝和深拷贝的代码详解
2019/06/05 Javascript
解决Layui当中的导航条动态添加后渲染失败的问题
2019/09/25 Javascript
Element Backtop回到顶部的具体使用
2020/07/27 Javascript
Python+tkinter使用80行代码实现一个计算器实例
2018/01/16 Python
Python 元类实例解析
2018/04/04 Python
django启动uwsgi报错的解决方法
2018/04/08 Python
Python3爬虫教程之利用Python实现发送天气预报邮件
2018/12/16 Python
Python中无限循环需要什么条件
2020/05/27 Python
打印机墨盒:123Inkjets
2017/02/16 全球购物
英国时尚首饰品牌:Missoma
2020/06/29 全球购物
酒店个人培训自我鉴定
2013/12/11 职场文书
个人授权委托书范本
2014/04/03 职场文书
护理专业毕业生自荐书
2014/05/24 职场文书
党小组鉴定意见
2015/06/02 职场文书
幼儿园秋季开学通知
2015/07/16 职场文书
iPhone13再次曝光
2021/04/15 数码科技
MySQL中distinct和count(*)的使用方法比较
2021/05/26 MySQL
go select编译期的优化处理逻辑使用场景分析
2021/06/28 Golang