javascript中this关键字详解


Posted in Javascript onDecember 12, 2016

不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法。强烈推荐。

以下篇幅有点长,希望读者耐心阅读。

以下内容会分为如下部分:

1.涵义

1.1:this涵义

1.2:this指向的可变性

2.使用场合

2.1:全局环境

2.2:构造函数

2.3:对象的方法

3.使用注意点

3.1:避免多层嵌套this

3.2:避免数组处理方法中的this

3.3:避免回调函数中的this

1.涵义

1.1:this涵义

在我写的一篇关于 构造函数与new关键字 的关系的博文中谈及,new关键字总是会返回一个对象。这个对象可以是new调用构造函数时返回的空对象,也可以是在构造函数中使用return语句返回的复杂数据类型(包括对象,数组等)。

同样,与new关键字相同,this关键字总是返回一个对象。再说的详细一些,就是属性或方法“当前”所在的对象。

var Keith = {
  firstName: 'Chou',
  describe: function() {
   return this.firstName;
  }
 };
console.log(Keith.describe()); //'Chou'

上面代码中,this.firstName表示describe方法当前所在对象的firstName属性。也就是说,在全局作用域下调用describe方法时,describe方法所在的当前对象是Keith,所以就是调用Keith.firstName。

1.2:this指向的可变性

由于对象的属性可以赋给另外一个对象,所以属性所在的当前对象是可变的。也就是说,this的指向是可变的。

var Keith = {
  firstName: 'Chou',
  describe: function() {
   return this.firstName;
  }
 };
 var Rascal={
  firstName: 'King'
 }
 Rascal.describe=Keith.describe;
 console.log(Rascal.describe()); //'King'

上面代码中,Keith对象中的describe属性赋给Rascal,于是describe方法中当前所在的对象就是Rascal,所以this.firstName就指向Rascal。因为是传址传递,所以修改firstName会对原对象有影响。这个例子可能不便于理解,再看看下面这个例子。

function f(){
  return this.firstName;
 }
 var Keith = {
  firstName: 'Chou',
  describe:f
 };
 var Rascal={
  firstName: 'King',
  describe:f
 }
 console.log(Keith.describe()); //'Chou'
 console.log(Rascal.describe()); //'King'

上面代码中,把方法移到全局作用域下,函数f内部使用了this关键字。随着f所在的对象不同,this指向也就不同。

在全局作用域下this关键字会指向顶层对象(也就是window对象)。

var name='keith';
 function person(){
  var name='rascal';
  return this.name;
 }
console.log(person()); //'keith'

上面代码中,返回的是keith而不是rascal。原因在于this指向的是全局作用域。在全局作用域中定义一个函数,默认是指向window对象,而不是函数本身。但是,如果在函数内部不使用var来声明一个局部变量,那结果也会不同。

var name='keith';
 function person(){
  name='rascal';
  return this.name;
 }
console.log(person()); //'rascal'

上面代码中,在函数内部没有使用var来声明一个局部变量,那么此时函数内部的name属性不是局部变量,而是全局变量。所以会覆盖掉前面的name属性。如果对于局部变量和全局变量不了解,可以访问 这篇文章 。 

只要函数被赋给另外一个变量,this的指向会发生改变。

var Keith={
  name:'keith',
  describe:function(){
   return this.name;
  }
 }
var name='rascal';
var f=Keith.describe;
console.log(f()) //'rascal'

上面代码中,返回的是rascal,而不是keith。因为Keith.describe被赋值给了f变量,而全局作用域下有一个name变量,所以Keith内部的函数的this指向就会指向f运行时所在的对象(顶层对象,也就是window对象)

总结一下:

1.javascript语言中,一切皆为对象(除了 undefined 和 null 之外),运行环境也是对象,所以函数都是在某个对象之中运行,this就是这个对象(环境)。

2.this的指向是动态的。如果函数在全局作用域中,那么this就会指向全局环境;如果函数位于某个对象中,那么this就会指向该对象。

2.使用场合

this的使用场合可以分为以下几个场合。

2.1:全局环境(全局作用域)

在全局作用域中使用this对象,它指向的就是顶层对象,也就是window对象。

function keith() {
  return (this === window)
  }
console.log(keith()) //true

上面代码中,不管是不是在函数内部,只要在全局作用域下运行,this就是指向顶层对象window。

2.2:构造函数

构造函数中的this,指向的是将要创建的对象实例。

function Keith() {
  this.sex = 'boy';
 }
 var person = new Keith();
 console.log(person.sex); //'boy'

上面代码中,在全局作用域下定义了Keith构造函数,然后调用构造函数并赋值给person对象实例。

构造函数创建的三个基本要求:函数名首字母大写;构造函数内部使用this关键字来指向即将生成的对象实例;使用new关键字来调用构造函数并返回对象实例。

如果想更进一步深入了解构造函数与new关键字的关系,请移步至 这篇文章 。

2.3:对象的方法

当A对象的方法被赋予B对象,该方法中的this就从指向A对象变成指向B对象。所以要特别小心,将某个对象的方法赋值个另外一个对象时,会改变this的指向。

var keith = {
  sex: 'boy',
  foo: function() {
   return this.sex;
  }
 };
 var rascal = {
  sex: 'girl'
 };
 rascal.foo = keith.foo;
 console.log(keith.foo()); //'boy'
 console.log(rascal.foo()); //'girl'

上面代码中,把keith的foo函数赋值给了rascal,那么this的指向就从keith变成了rascal。

如果某个方法位于多层对象的内部,这时为了简化书写,把该方法赋值给一个变量,往往会得到不一样的结果。

var a = {
  b: {
   p: 'keith',
   c: function() {
    return this.p;
   }
  }
 };
 var person = a.b.c;
 console.log(person()); //undefined

上面代码中,c是两层对象里面的一个方法。为求简便,将其赋值给全局变量person,结果调用时,this指向了顶层对象window。而在window中变量p默认值为undefined。

要解决这个问题,可以只将c所在的对象赋值给person变量,或者是直接调用。

var person = a.b;
console.log(person.c()); //'keith'
console.log(a.b.c()); //'keith'

3.使用注意点

3.1:避免多层嵌套this

当在闭包中使用多层this,则this都会指向window。

function keith() {
  console.log(this);
  return function() {
   return this;
  }
 }
 keith(); //window
 keith()(); //window

上面代码中,在一个函数中返回另外一个匿名函数是闭包的特点之一,可以看出,当在闭包中使用this对象都会指向全局作用域中的window对象。

如果在函数外包含一个对象,则内部this指向全局作用域,而外部this对象指向当前作用域。

var o = {
  f1: function() {
   console.log(this);
   (function() {
    console.log(this)
   })();
  }
 };
 o.f1(); //Object , Window

上面代码包含两层this,结果运行后,第一层指向当前对象,第二层指向全局对象。

实际执行的是如下代码。

function keith() {
  console.log(this);
 }
 var o = {
  f1: function() {
   console.log(this);
   var f2 = keith();
  }
 };
 o.f1(); //Object , Window

要实现多层this嵌套,有两种解决方法:

一是在第二层中改用一个指向外层this的变量。

var o = {
  f1: function() {
   console.log(this);
   var that = this;
   (function() {
    console.log(that);
   })();
  }
 };
 o.f1(); //Object , Object

上面代码中,定义了局部变量that,固定指向了外层的this,然后在内层中使用that,就不会发生this指向的改变。但是如果函数外部内有嵌套一个对象,this还是会指向全局。

二是Javascript中的严格模式。在严格模式下,如果内部函数的this指向了window对象,就会报错。

var a = {
  count: 0,
  fun: function() {
   'use strict';
   return this.count++;
  }
 }
 var f = a.fun;
 console.log(f()) //'TypeError: this is undefined'

上面代码中,fun方法使用严格模式声明。把a对象中的fun方法赋值给全局变量f,那么this此时指向window对象,在严格模式下,就会报错。如果函数外部没有嵌套一个对象,那么不会报错,而是会返回undefined。

3.2:避免数组处理方法中的this

  数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this

var keith = {
  a: 'Hello',
  b: ['b1', 'b2'],
  c: function() {
   this.b.forEach(function(item) {
    console.log(this.a + ' ' + item);
   })
  }
 };
 keith.c();
 //undefined b1
 //undefined b2

上面代码中,forEach方法的回调函数中的this,其实指向的是window对象,因此取不到keith.a的值,同上也属于避免多层嵌套this。也就是说,内层的this不指向外部函数,而是指向顶层对象。

要解决这个方法,可以使用that变量来代替回调函数中的this。

var keith = {
  a: 'Hello',
  b: ['b1', 'b2'],
  c: function() {
   var that = this;
   this.b.forEach(function(item) {
    console.log(that.a + ' ' + item);
   })
  }
 };
 keith.c();
 //Hello b1
 //Hello b2

另外一种方法,就是让this做为forEach方法的第二个参数,来固定它的运行环境。

var keith = {
  a: 'Hello',
  b: ['b1', 'b2'],
  c: function() {
   this.b.forEach(function(item) {
    console.log(this.a + ' ' + item);
   }, this)
  }
 };
 keith.c();
 //Hello b1
 //Hello b2

3.3:避免回调函数中的this

回调函数中的this往往会改变指向。

var o = {
  f: function() {
   console.log(this === o);
  }
 };
 o.f(); // true;

上面代码中,调用o对象的f方法,返回true。

但是,如果将f方法指定给某个按钮的click事件,this的指向就变了。

$('button').on('click',o.f);

上面代码中,使用了jquery方法来获取button元素,并绑定click事件。点击按钮后控制台会显示false。原因是此时this不再指向o对象了,而是指向按钮的DOM对象,因为f方法是在按钮对象的环境中被调用的。

总结一下:

a:如果想要多层嵌套this关键字,最常用的解决方法就是使用that变量,固定指向外层的this,然后在内层中使用that变量。就不会发生内层this指向全局的问题。

b:如果在回调函数中使用this关键字,注意this的指向问题。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
asp.net中System.Timers.Timer的使用方法
Mar 20 Javascript
js实现杯子倒水问题自动求解程序
Mar 25 Javascript
在js文件中写el表达式取不到值的原因及解决方法
Dec 23 Javascript
ECMAScript6中Set/WeakSet详解
Jun 12 Javascript
angularjs 源码解析之injector
Aug 22 Javascript
利用vue实现模态框组件
Dec 19 Javascript
Bootstrap导航中表单简单实现代码
Mar 06 Javascript
JS中SetTimeout和SetInterval使用初探
Mar 23 Javascript
初探JavaScript 面向对象(推荐)
Sep 03 Javascript
小程序实现多选框功能
Oct 30 Javascript
10个最受欢迎的 JavaScript框架(推荐)
Apr 24 Javascript
JS寄快递地址智能解析的实现代码
Jul 16 Javascript
深入理解javascript函数参数与闭包
Dec 12 #Javascript
smartupload实现文件上传时获取表单数据(推荐)
Dec 12 #Javascript
Javascript中call,apply,bind方法的详解与总结
Dec 12 #Javascript
微信小程序开发之圆形菜单 仿建行圆形菜单实例
Dec 12 #Javascript
深入理解javascript中concat方法
Dec 12 #Javascript
js仿微信语音播放实现思路
Dec 12 #Javascript
解析JavaScript数组方法reduce
Dec 12 #Javascript
You might like
PHP的Socket网络编程入门指引
2015/08/11 PHP
PHP编程实现csv文件导入mysql数据库的方法
2017/04/29 PHP
JavaScript Object的extend是一个常用的功能
2009/12/02 Javascript
JQuery中对Select的option项的添加、删除、取值
2013/08/25 Javascript
jQuery的load()方法及其回调函数用法实例
2015/03/25 Javascript
JavaScript使用Math.Min返回两个数中较小数的方法
2015/04/06 Javascript
详解为Angular.js内置$http服务添加拦截器的方法
2016/12/20 Javascript
angular directive的简单使用总结
2017/05/24 Javascript
node.js文件上传重命名以及移动位置的示例代码
2018/01/19 Javascript
AngularJS中ng-options实现下拉列表的数据绑定方法
2018/08/13 Javascript
vue侧边栏动态生成下级菜单的方法
2018/09/07 Javascript
[54:33]2018DOTA2亚洲邀请赛小组赛 A组加赛 Liquid vs Optic
2018/04/03 DOTA
Python如何实现MySQL实例初始化详解
2017/11/06 Python
简单实现python进度条脚本
2017/12/18 Python
浅析python实现scrapy定时执行爬虫
2018/03/04 Python
人脸识别经典算法一 特征脸方法(Eigenface)
2018/03/13 Python
Python使用matplotlib绘制余弦的散点图示例
2018/03/14 Python
Python3.6日志Logging模块简单用法示例
2018/06/14 Python
带你认识Django
2019/01/15 Python
python实现字符串完美拆分split()的方法
2019/07/16 Python
opencv设置采集视频分辨率方式
2019/12/10 Python
python3.9实现pyinstaller打包python文件成exe
2020/12/13 Python
佐卡伊官网:中国知名珠宝品牌
2017/02/05 全球购物
美国转售二手商品的电子商务平台:BLINQ
2018/12/13 全球购物
意大利辅助药品、药物和补品在线销售:FarmaEurope
2020/04/29 全球购物
教师个人的自我评价分享
2014/01/02 职场文书
幼儿园中秋节活动方案
2014/02/06 职场文书
物资采购方案
2014/06/12 职场文书
应届生求职信范文
2014/06/30 职场文书
小学教师学习党的群众路线教育实践活动心得体会
2014/10/31 职场文书
党的群众路线教育实践活动心得体会范文
2014/11/05 职场文书
大学生英文求职信范文
2015/03/19 职场文书
2015年销售员工作总结范文
2015/04/07 职场文书
教师节晚会主持词
2015/06/30 职场文书
2016年村党支部公开承诺书
2016/03/24 职场文书
python中的class_static的@classmethod的巧妙用法
2021/06/22 Python