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 相关文章推荐
JS中style属性
Oct 11 Javascript
javascript 面向对象,实现namespace,class,继承,重载
Oct 29 Javascript
JavaScript和ActionScript的交互实现代码
Aug 01 Javascript
js向上无缝滚动,网站公告效果 具体代码
Nov 18 Javascript
javascript中return,return true,return false三者的用法及区别
Nov 17 Javascript
JavaScript算法系列之快速排序(Quicksort)算法实例详解
Sep 04 Javascript
js面向对象实现canvas制作彩虹球喷枪效果
Sep 24 Javascript
AngularJS中的按需加载ocLazyLoad示例
Jan 11 Javascript
js 实现获取name 相同的页面元素并循环遍历的方法
Feb 14 Javascript
jQuery实现input[type=file]多图预览上传删除等功能
Aug 02 jQuery
详解Nuxt.js 实战集锦
Nov 19 Javascript
JavaScript单线程和任务队列原理解析
Feb 04 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 之入门篇
2006/12/04 PHP
php中通过数组进行高效随机抽取指定条记录的算法
2013/09/09 PHP
PHP的变量类型和作用域详解
2014/03/12 PHP
用Javascript 和 CSS 实现脚注(Footnote)效果
2009/09/09 Javascript
js 数据类型转换总结笔记
2011/01/17 Javascript
JS中Date日期函数中的参数使用介绍
2014/01/02 Javascript
使用typeof方法判断undefined类型
2014/09/09 Javascript
javascript实现避免页面按钮重复提交
2015/01/08 Javascript
深入探讨javascript中的数据类型
2015/03/04 Javascript
微信小程序 富文本转文本实例详解
2016/10/24 Javascript
AngularJS实现动态添加Option的方法
2017/05/17 Javascript
详解Javascript获取缓存和清除缓存API
2017/05/25 Javascript
Vue.js实现价格计算器功能
2020/03/30 Javascript
JS中Map和ForEach的区别
2018/02/05 Javascript
jQuery实现的简单对话框拖动功能示例
2018/06/05 jQuery
详解Vue项目在其他电脑npm run dev运行报错的解决方法
2018/10/29 Javascript
webpack4.x CommonJS模块化浅析
2018/11/09 Javascript
vue中typescript装饰器的使用方法超实用教程
2019/06/17 Javascript
[01:00:14]DOTA2官方TI8总决赛纪录片 真视界True Sight
2019/01/16 DOTA
Python实现的使用telnet登陆聊天室实例
2015/06/17 Python
python列表操作之extend和append的区别实例分析
2015/07/28 Python
Python对文件操作知识汇总
2016/05/15 Python
python绘制直线的方法
2018/06/30 Python
python分批定量读取文件内容,输出到不同文件中的方法
2018/12/08 Python
python使用zip将list转为json的方法
2018/12/31 Python
flask利用flask-wtf验证上传的文件的方法
2020/01/17 Python
css3新增颜色表示方式分享
2014/04/15 HTML / CSS
韩国CJ食品专卖网:CJonmart
2016/09/11 全球购物
终端业务员岗位职责
2013/11/27 职场文书
公司员工离职证明书
2014/10/04 职场文书
迁户口计划生育证明
2014/10/19 职场文书
民主评议党员工作总结
2014/10/20 职场文书
2015年度销售个人工作总结
2015/03/31 职场文书
综治目标管理责任书
2015/05/11 职场文书
2015大学党建带团建工作总结
2015/07/23 职场文书
python 学习GCN图卷积神经网络
2022/05/11 Python