问题来源 

  本文是基于廖雪峰老师JavaScript课程中的方法一节以及阮一峰老师JavaScript 的 this 原理

所记。

  首先,我们了解一下JavaScript中的方法:在一个对象中绑定函数,称为这个对象的方法。下面,给对象xiaoming绑定一个函数:

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age;  // function xiaoming.age()
xiaoming.age(); // 28

从这段代码中,可以看到使用了this关键字。

this的介绍

  在一个方法中,this指向的就是该方法绑定的对象。其中 this.birth 就是指向对象 xiaoming 的 birth 属性。

对比出情形

  通过拆分函数,对比上面的代码。从下面的结果看出,单独调用含有this关键字的函数,返回的是NaN。

function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 28, 正常结果
getAge(); // NaN

你以为这就完了吗?下面这种写法,还是有错:

var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN

原因是:在一个对象中调用含有this关键字的函数,此时this就是指向该对象;单独调用,例如上述的 getAge() ,此时this指向的就是全局对象: window 。所以,要保证this指向正确,必须用 obj.xxx() 的形式调用!至此,我们就了解了this问题的来源。由于这是一个巨大的设计错误,要想纠正可没那么简单。ECMA决定,在strict模式下让函数的this指向undefined,因此,在strict模式下,你会得到一个错误:"Uncaught TypeError: Cannot read property 'birth' of undefined"。

'use strict';

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
}; var fn = xiaoming.age;
fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined

当你重构方法时:

'use strict';

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - this.birth;
}
return getAgeFromBirth();
}
}; xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined

结果又报错了!原因是this指针只在age方法的函数内指向xiaoming,在函数内部定义的函数,this又指向undefined了!(在非strict模式下,它重新指向全局对象window!)

  下面,廖老师给出了修复方法,用一个that变量首先捕获this

'use strict';

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
}; xiaoming.age(); //

var that = this;,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。

  下面还有一个问题来源,是阮一峰老师所写。下面写法一和写法二指向同一个函数,但是执行结果可能不一样。

var obj = {
foo: function () {}
}; var foo = obj.foo; // 写法一
obj.foo(); // 写法二
foo();

下面是结果不一样的例子:

var obj = {
foo: function () { console.log(this.bar) },
bar: 1
}; var foo = obj.foo;
var bar = 2; obj.foo(); //
foo(); //

结果不一样的原因,很简单。就是 xiaoming 那个例子中的说明:如果在对象中使用this,其指向的就是对象。这里, obj.foo(); // foo运行在obj环境,所以this指向obj,也就是 obj.bar = 1 ; foo(); // 对于foo()来说,foo运行在全局环境,所以this指向全局环境,也就是 window.bar = 2 。所以,this出现问题的情形已经很清楚了,所处对象的原因。

深层原因

  接下来,借阮一峰老师的解释,说明 JavaScript 这样处理的原理。

内存的数据结构

  首先,给一段代码: var obj = { foo: 5 }; 这里的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。即变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

  原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。注意,foo属性的值保存在属性描述对象的value属性里面。

{
foo: {
[[value]]: 5
[[writable]]: true
[[enumerable]]: true
[[configurable]]: true
}
}

  但是,属性的值可能是一个函数: var obj = { foo: function () {} }; 。这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。

{
foo: {
[[value]]: 函数的地址
...
}
}

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {};
var obj = { f: f }; // 单独执行
f(); // obj 环境执行
obj.f();

环境变量

  JavaScript 允许在函数体内部,引用当前环境的其他变量。下面代码中,函数体里面使用了变量x。该变量由运行环境提供。

var f = function () {
console.log(x);
};

函数可在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

var f = function () {
console.log(this.x);
}

上面代码中,函数体里面的this.x就是指当前运行环境的x

var f = function () {
console.log(this.x);
}; var x = 1;
var obj = {
f: f,
x: 2,
}; // 单独执行
f() // // obj 环境执行
obj.f() //

上面代码中,函数f()在全局环境执行,this.x指向全局环境window的x=1。但是在obj环境中,this就是指向obj中的x=2。

回答问题

  obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

感谢

  感谢两位老师的讲解!

  

JavaScript方法中this关键字使用注意的更多相关文章

  1. 在方法中new关键字的用处

    如果在类A中有M1这个方法需方法 public virtual ovid m1() { console.writeline(“我的世界”); } 那么你在类B中继承的时候可以重写这个方法,也可以不重写 ...

  2. 深入解析Javascript中this关键字的使用

    深入解析Javascript中面向对象编程中的this关键字 在Javascript中this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如: function TestFun ...

  3. 大前端学习笔记整理【五】关于JavaScript中的关键字——this

    写在前面 工作有那么一段时间了,但是在工作中,发现自己的理论知识还是有所欠缺.特别是在javascript上,很多东西其实自己属于知道要用这个,但是不知道为什么要这么用...这种情况很是尴尬了,所以写 ...

  4. javascript中this关键字详解

    不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法.强烈推荐. 以下篇幅有点长,希望读者耐心阅读. 以下内容会分为如下部分: 1.涵义 1.1:th ...

  5. JavaScript中this关键字的使用比较

    JavaScript中this关键字的使用比较 this关键字在JavaScript中,用的不能说比较多,而是非常多.那么熟悉this关键字的各种用法则显得非常关键. this有时候就是我们经常说的上 ...

  6. javascript中new关键字详解

    和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...

  7. JavaScript文件中调用AngularJS内部方法或改变$scope变量

    需要在其他JavaScript文件中调用AngularJS内部方法或改变$scope变量,同时还要保持双向数据绑定: 首先获取AngularJS application: 方法一:通过controll ...

  8. jQuery基础学习5——JavaScript方法获取页面中的元素

    给网页中的所有<p>元素添加onclick事件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...

  9. Android中通过WebView控件实现与JavaScript方法相互调用的地图应用

    在Android中通过WebView控件,可以实现要加载的页面与Android方法相互调用,我们要实现WebView中的addJavascriptInterface方法,这样html才能调用andro ...

随机推荐

  1. Hibernate 批量保存数据

    public Boolean save(Collection<Object> os) { int batchSize = 50,i=0; Session session=this.sess ...

  2. 迷你迅雷+SqlServer2008r2下载

    迷你迅雷下载 http://down.sandai.net/mini/MiniThunderInstaller3.1.1.58.exe SqlServer 2008r2下载 ed2k://|file| ...

  3. C# 用正则表达式判断字符串是否为纯数字

    Regex regex = new System.Text.RegularExpressions.Regex("^(-?[0-9]*[.]*[0-9]{0,3})$"); stri ...

  4. c/c++ socket发送http请求访问网站

    这几天课比较少,校园网上网要认证才能上网,每次必须输入学号密码,为了方便,写了一个自动登录以及如果在线,登录自服务系统强制下线的小工具. 强制下线思路:获取sessionID----------> ...

  5. ADO学习笔记之注入漏洞与参数化查询

    ADO学习笔记之注入漏洞与参数化查询 作为新手,在学习ADO程序时,使用 sql 语言查询数据时,很容易写类似如下代码: using (SqlConnection con = new SqlConne ...

  6. java 读取环境变量和系统变量的方法

    在web开发的过程中不免需要读取一些自定义的jvm系统变量或者环境变量.比如定义一些通用的log文件.或者数据库访问路径. 我们可以使用System.getProperties()读取所有的系统变量. ...

  7. Teradata 认证系列 - 2. Teradata数据库总览

    Teradata (以下简称TD) 总览本课的学习目标 描述Teradata数据库产品的功能 知晓支持的操作系统 描述Teradata的并行架构 解释线性可扩展性 列出Teradata DBA永远不需 ...

  8. HDU4417 线段树 + 离线处理

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 , 线段树(或树状数组) + 离线处理 最近看了几道线段树的题都是需要离线处理数据的,正好这块比 ...

  9. 使用Eclipse连接SAP云平台上的HANA数据库实例

    SAP云平台(Cloud Platform)上的HANA数据库实例有两种方式访问: 1. 通过SAP云平台的基于网页版的Development Tool:SAP HANA Web-Based Deve ...

  10. WordPress企业建站心得

    回头聊聊我用WordPress做企业网站的事.说是企业网站,其实就是一个小的企业展示网站.事情要从我爸开了一家自行车店开始说起,自从他开了自行车店,不但开始学着玩起了微信(因为要做微信营销),又想到了 ...