JavaScript方法中this关键字使用注意
问题来源
本文是基于廖雪峰老师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关键字使用注意的更多相关文章
- 在方法中new关键字的用处
如果在类A中有M1这个方法需方法 public virtual ovid m1() { console.writeline(“我的世界”); } 那么你在类B中继承的时候可以重写这个方法,也可以不重写 ...
- 深入解析Javascript中this关键字的使用
深入解析Javascript中面向对象编程中的this关键字 在Javascript中this关键字代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.比如: function TestFun ...
- 大前端学习笔记整理【五】关于JavaScript中的关键字——this
写在前面 工作有那么一段时间了,但是在工作中,发现自己的理论知识还是有所欠缺.特别是在javascript上,很多东西其实自己属于知道要用这个,但是不知道为什么要这么用...这种情况很是尴尬了,所以写 ...
- javascript中this关键字详解
不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法.强烈推荐. 以下篇幅有点长,希望读者耐心阅读. 以下内容会分为如下部分: 1.涵义 1.1:th ...
- JavaScript中this关键字的使用比较
JavaScript中this关键字的使用比较 this关键字在JavaScript中,用的不能说比较多,而是非常多.那么熟悉this关键字的各种用法则显得非常关键. this有时候就是我们经常说的上 ...
- javascript中new关键字详解
和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...
- JavaScript文件中调用AngularJS内部方法或改变$scope变量
需要在其他JavaScript文件中调用AngularJS内部方法或改变$scope变量,同时还要保持双向数据绑定: 首先获取AngularJS application: 方法一:通过controll ...
- jQuery基础学习5——JavaScript方法获取页面中的元素
给网页中的所有<p>元素添加onclick事件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...
- Android中通过WebView控件实现与JavaScript方法相互调用的地图应用
在Android中通过WebView控件,可以实现要加载的页面与Android方法相互调用,我们要实现WebView中的addJavascriptInterface方法,这样html才能调用andro ...
随机推荐
- GUI的最终选择 Tkinter(四):Entry、Listbox、Scrollbar和Scale组件
Entry组件 Entry组件就是平时所说的输入框.输入框是程序员用到的最多的一个程序,例如在输入账号和密码的时候需要提供两个输入框,用于接收密码的输入框还会有星号将实际输入的内容隐藏起来. Tkin ...
- centos 6 设置无密码登录ssh 不成功问题
由于需要配置一台git 服务器,所以当时就建立了个git 帐号,之后执行mkdir .ssh 之后在客户端 scp 了自己的pub公钥到.ssh 目录下,但是怎么都还是需要自己再次输入密码,将这 ...
- Storm概念学习系列之事务
不多说,直接上干货! 事务 这里的事务是专门针对Topology提出来的,是为了解决元组在处理失败重新发送后的一系列问题的.简而言之,事务拓扑(transactional topology)就是指St ...
- Python解析CSV中的多维字典
CSV文件结构如下,其中字段A为唯一 代码如下,Python27 with open(file_obj+'TEST.CSV','r') as f: #转为字典 Reader=csv.DictReade ...
- ssm(Spring、Springmvc、Mybatis)实战之淘淘商城-第十三天(非原创)
文章大纲 一.课程介绍二.SolrCloud介绍与搭建三.工程部署四.参考资料下载五.参考文章 一.课程介绍 一共14天课程(1)第一天:电商行业的背景.淘淘商城的介绍.搭建项目工程.Svn的使用 ...
- AJPFX关于面向对象之封装,继承,多态 (上)
Java是一种面向对象的语言,这是大家都知道的,他与那些像c语言等面向过程语言不同的是它本身所具有的面向对象的特性--封装,继承,多态,这也就是传说中的面向对象三大特性 一:从类和对象开始说起: Oo ...
- 【前端】Chrome DevTools 笔记
1. 查看网络耗时 timeline 生命周期按照以下类别显示花费的时间: Queuing Stalled 如果适用:DNS lookup.initial connection.SSL handsha ...
- npm warn weex @1.0.0 no repository field
玩weex出现nmp安装问题总是包这个错,但是其实是安装成功的 npm warn weex@1.0.0 no repository field. 看字面意思大概是package.json里缺少repo ...
- 零基础逆向工程31_Win32_05_提取图标_修改标题
在程序中使用图标 1.加载图标 HICON hIcon; hIcon = LoadIcon (hAppInstance, MAKEINTRESOURCE (IDI_ICON)); hAppInstan ...
- https验证新发现-老知识
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); 可以设置https全局的域名校验规则 HttpsURLConnecti ...