## 2.1 关于this
### 2.1.1 为什么要用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(you); // Hello, 我是 READER
~~~
如果不使用this,那就需要给identify() 和speak() 显式传入一个上下文对象。
~~~
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify(context);
console.log(greeting);
}
identify(you); // READER
speak(me); //hello, 我是KYLE
~~~
### 2.1.2 误解
**1. 指向自身**
~~~
function foo(num) {
console.log("foo: " + num);
// 记录foo 被调用的次数
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
// foo 被调用了多少次?
console.log(foo.count); // 0 -- ???
~~~
console.log 语句产生了4 条输出,证明foo(..) 确实被调用了4 次,但是foo.count 仍然是0。显然从字面意思指向函数自身来理解this 是错误的。
如果要从函数对象内部引用它自身,那只使用this 是不够的。一般来说需要通过一个指向函数对象的词法标识符(变量)来引用它。
思考一下下面这两个函数:
~~~
function foo() {
foo.count = 4; // foo 指向它自身
}
setTimeout( function(){
// 匿名(没有名字的)函数无法指向自身
}, 10 );
~~~
第一个函数被称为具名函数,在它内部可以使用foo 来引用自身。但是在第二个例子中,传入setTimeout(..) 的回调函数没有名称标识符(这种函数被称为匿名函数),因此无法从函数内部引用自身。
另一种方法是强制this 指向foo 函数对象:
~~~
function foo(num) {
console.log("foo: " + num);
// 记录foo 被调用的次数
// 注意,在当前的调用方式下(参见下方代码),this 确实指向foo
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
// 使用call(..) 可以确保this 指向函数对象foo 本身
foo.call(foo, i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 4
~~~
**2. this指向函数的词法作用域**
**this 在任何情况下都不指向函数的词法作用域。**在JavaScript 内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript 引擎内部。
~~~
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); // ReferenceError: a is not defined
~~~
以上代码试图(但是没有成功)跨越边界,使用this 来隐式引用函数的词法作用域。
* 首先,这段代码试图通过this.bar() 来引用bar() 函数。这是绝对不可能成功的。(调用bar() 最自然的方法是省略前面的this,直接使用词法引用标识符。)
* 此外,还试图使用this 联通foo() 和bar() 的词法作用域,从而让bar() 可以访问foo() 作用域里的变量a。这是不可能实现的,你不能使用this 来引用一个词法作用域内部的东西。
**3. this到底是什么**
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。**this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。**
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的其中一个属性,会在函数执行的过程中用到。
- 前言
- 第一章 JavaScript简介
- 第三章 基本概念
- 3.1-3.3 语法、关键字和变量
- 3.4 数据类型
- 3.5-3.6 操作符、流控制语句(暂略)
- 3.7函数
- 第四章 变量的值、作用域与内存问题
- 第五章 引用类型
- 5.1 Object类型
- 5.2 Array类型
- 5.3 Date类型
- 5.4 基本包装类型
- 5.5 单体内置对象
- 第六章 面向对象的程序设计
- 6.1 理解对象
- 6.2 创建对象
- 6.3 继承
- 第七章 函数
- 7.1 函数概述
- 7.2 闭包
- 7.3 私有变量
- 第八章 BOM
- 8.1 window对象
- 8.2 location对象
- 8.3 navigator、screen与history对象
- 第九章 DOM
- 9.1 节点层次
- 9.2 DOM操作技术
- 9.3 DOM扩展
- 9.4 DOM2和DOM3
- 第十章 事件
- 10.1 事件流
- 10.2 事件处理程序
- 10.3 事件对象
- 10.4 事件类型
- 第十一章 JSON
- 11.1-11.2 语法与序列化选项
- 第十二章 正则表达式
- 12.1 创建正则表达式
- 12.2-12.3 模式匹配与RegExp对象
- 第十三章 Ajax
- 13.1 XMLHttpRequest对象
- 你不知道的JavaScript
- 一、作用域与闭包
- 1.1 作用域
- 1.2 词法作用域
- 1.3 函数作用域与块作用域
- 1.4 提升
- 1.5 作用域闭包
- 二、this与对象原型
- 2.1 关于this
- 2.2 全面解析this
- 2.3 对象
- 2.4 混合对象“类”
- 2.5 原型
- 2.6 行为委托
- 三、类型与语法
- 3.1 类型
- 3.2 值
- 3.3 原生函数