你不知道的this(1)
关于this
this
关键字是JavaScript
中最复杂的机制之一,同时,它也是JavaScript
中最重要的机制之一。但是,即使是非常有经验的JavaScript
开发者也很难说清楚它到底是什么。
我们为什么需要 this
如果对于有经验的开发者来说this
都是一个非常复杂的机制,那么它到底有用在哪里?真的值得我们付出那么大的代价来学习吗?
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call(this);
console.log(greeting);
}
var me = {
name: 'kyle';
}
var you = {
name: 'Reader'
}
identify.call(me); // kyle
identify.call(you); // Reader
speak.call(me); // Hello, 我是 Kyle
speak.call(me) // Hello, 我是 Reader
这段代码可以在不同的上下文对象(me
和you
)中重复使用函数identify()
和speak()
,不用针对每个对象编写不同版本的函数
如果不使用this
,那就需要给identify()
和speak()
显示传入一个对象。
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify(context);
console.log(greeting);
}
var me = {
name: 'kyle';
}
var you = {
name: 'Reader'
}
identify(you);
speak(me);
this
提供了一种更优雅的方式来隐式传递一个对象的引用,因此可以将API
设计更加简洁且易于复用。
随着使用模式越来越复杂,显示传递上下文对象会让代码变得越来越混乱,使用this
则不会这样。当我们介绍到原型和对象的时候,我们就会明白函数可以自动医用合适的上下文对象多么重要。
误解
我们介绍this
到底如何工作之前,要解除一些关于this
的错误认知。如果太拘泥于this
字面的意思就会产生一些误解。
指向自身
人们很容易把this
理解未指向函数本身体,这个推断从英语的角度上是说得通的。那么为什么需要从函数内部引用函数自身呢?常见的原因是递归(从函数内部调用这个函数)或者我们写一个在第一次被调用后自己可以接触绑定的事件处理器。Javascript
新手开发者通常会认为,既然把函数看作是一个对象,那就可以在调用函数时存储状态(属性的值)。
function foo(num) {
console.log("foo: " + num);
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(foo.count); // 0
执行 foo.count = 0
的时候,的确向函数对象foo
添加了一个属性count
,但是函数内部代码this.count
中的this
并不是指向那个函数对象,所以虽然属性名相同,根对象却并不相同。
负责的开发一定会问 :"如果我增加的count
属性和预期的不一样,那我增加的是哪个count
?"
实际上,如果他深入探索的话,他就会发现这段代码在无意间创建了一个全局变量count
,他的值为NaN
。当然,如果他发现了这个奇怪的结果,一定会问 :“为什么时全局,为什么它的值是NaN而不是其他更适合的值”
function foo(num) {
console.log('foo: ' + num);
// 记录foo被调用的次数
data.count++;
}
var data = {
count: 0;
};
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(data.count); // 4
从某种角度上来说这个方法解决了问题,但可惜它忽略了真正的问题————无法理解this
的含义和工作原理————而是返回了舒适区,使用了一种我们熟悉的技术:词法作用域。
它的作用域
第二种常见的误解是,this
指向函数的作用域。这个问题有点复杂,因为在某种情况下它是正确的,但是在其他情况下它却是错误的。需要明确的是,this
在任何情况下都不指向函数的词法作用域。在JavaScript
内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域"对象"无法通过JavaScript
代码访问,它存在于JavaScript
引擎内部。思考一下下面的代码,它试图(但是没有成功)跨越边界,使用this
来隐式引用函数的词法作用域:
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo(); // ReferenceError: a is not defined
首先,这段代码试图通过this.bar()
来引用bar()
函数。这是绝对不可能成功的,我们之后会解释原因。调用bar()
最自然的方法是省略前面的this
,直接使用词法引用标识符。此外,编写这段代码的开发者还试图使用this
联通foo()
和bar()
的词法作用域,从而让bar()
可以访问foo()
作用域里的变量a
。这是不可能实现的,你不能使用this
来引用一个词法作用域内部的东西。每当你想要把this
和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的
this 到底是什么
this
是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。
this
的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this
就是记录其中的一个属性,会在函数执行的过程中用到。
小结
对于那些没有投入时间学习 this
机制的JavaScript
开发者来说,this
的绑定一直是一件非常令人困惑的事。this
是非常重要的,但是猜测、尝试并出错和盲目地从Stack Overflow
上复制和粘贴答案并不能让你真正理解this
的机制。学习this
的第一步是明白this
既不指向函数自身也不指向函数的词法作用域,你也许被这样的解释误导过,但其实它们都是错误的。this
实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 尝鲜使用微众银行WeCross实现基于哈希时间锁定的跨链转账
- Flutter 富文本第三方库 rich_text_widget
- 程序员的数学:线性代数之可视化
- 基于七牛SDK构建的Vue单页图片管理应用
- [Electron]仿写一个课堂随机点名小项目
- SyntaxError: (unicode error) 错误解决
- 理解CSS布局和块格式化上下文
- 基于后端云的吉他谱小程序开发
- 10个酷炫CMD命令
- Hog图像特征提取算法,HOG
- Win10设置Python定时任务
- 在 istio 中使用 namespace 进行资源/租户隔离
- 跨域(CORS)产生原因分析与解决方案,这一次彻底搞懂它
- 一文讲清楚动物模型中的母体效应
- storm安装教程