javaScript面向对象编程之this关键字
1. 含义
简而言之,this
就是属性或方法“当前”所在的对象。
比如:
var person = {
name: "jidi",
print: function(){
return "name:"+this.name;
}
}
person.print(); // "name:jidi"
在上面这个例子中,this.name
中的this
表示name属性所在的对象。因为this.name
在print()
方法中调用,而priint()
方法所在当前对象为person
。所以this
指向person
对象,this.name
即为person.name
。
对象的属性赋值给另一个对象后,属性所在当前对象跟着改变,this
的指向也会改变。
var person = {
name: "jidi",
print: function(){
return "name:"+this.name;
}
}
// 创建一个新对象
var lady = {
name: 'lady'
};
// 将person.print属性赋值给lady
lady.print = person.print;
person.print(); // "name:jidi"
lady.print(); // "name:lady"
以一种更清晰的方式展示this
的指向变化。
// 定义一个全局函数print
function printName(){
return "name:"+this.name;
}
// 分别创建两个对象
var o1 = {
name: "Object1",
print: printName
};
var o2 = {
name: "Object2",
print: printName
};
o1.print(); // "name:Object1"
o2.print(); // "name:Object2"
上面例子中,printName()
函数内部使用了this
,随着printName()
函数所在的对象不同,this
的指向也不同。
只要函数被赋值给另一个变量,this
的指向就会改变。
var p = {
name: "p",
print: function(){
return this.name;
}
};
var name = "我是全局变量";
// 将p.print赋值给q
var q = p.print;
p.print(); // “p”
q(); // "我是全局变量"
this
其实很常见,比如说:
<!--HTML表单片段-->
年龄:<input type="text" name = "age" placeholder="请输入你的年龄!" onChange="varifyAge(this)"></input>
<!--javaScript代码片段--->
function onChange(object){
if(object.value > 120 || object.value < 0 ){
console.error("你输入的年龄不合法!");
}
}
上面这个例子,this
指向的是<input>
这个文本框。
在JavaScript 中,一切皆对象,运行环境也是对象。函数都是在某个对象之中运行,this
就是函数运行时所在的对象(环境)。
2. 实质
javaScript中之所以有this
关键字,跟内存里面的数据结构有很大关系。
var example = {color: "red"};
在上述代码中,将一个对象赋值给变量example
,javaScript引擎会先在内存里生成一个对象{color: "red"}
,然后把这个对象的内存地址赋值给变量example
。即变量example
是一个对象的内存地址 。当读取example.color
时,先从example
拿到对象内存地址,然后再从内存地址提取出来原始的对象。
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。以上面的例子为例,实际上是以下面的形式保存的。
{
color:{ // 属性描述对象
value: "red",
writable: true,
enumerable: true,
configurable: true
}
}
如果对象的属性是一个函数,会将函数单独保存,把函数的内存地址赋值给属性名对应的属性描述对象的value
。
var example = {
print: function(){
console.log("我是一个函数");
}
}
// 内存中的保存形式
{
print: { // 属性描述对象
value: 函数内存地址,
......
}
}
由于函数是在内存中单独存储的,所以可以在不同的上下文环境中执行。
function printName(){
console.log(this.name);
}
var person = {
name: "person对象里的name",
printName: printName
}
var name = "全局环境中的name"
// 单独执行
printName(); // "全局环境中的name"
//person环境中执行
person.printName(); // "person对象里的name",
JavaScript 允许在函数体内部,引用当前环境的其他变量。由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境。所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
3. 使用场合
this
主要在以下场景使用。
- 构造函数
构造函数中的this
指的实例。 - 全局环境
全局环境中使用,指的是顶层对象window
。 - 对象的方法
如果对象的方法里面包含this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this
的指向。
var example ={
f: function () {
console.log(this);
}
};
// 情况一
example.f() // obj
// 情况二
(example.f = example.f)() // window
// 情况三
(false || example.f)() // window
// 情况四
(1, example.f)() // window
之所以出现上述情况,是因为example
是个对象,它的属性f
其实是一个内存地址,指向属性f
对应的属性描述对象,但是属性f
对应的描述对象的value
其实是函数的内存地址。即example
和 example.f
存储在两个内存地址中。example.f()
是从地址一调用地址二,所以地址二的运行环境是地址一,this
指向example
。example.f
是直接调用地址二,此时运行环境就是全局环境,因此this
指向全局环境。
4. 绑定this的方法
this
的动态切换,为 JavaScript 创造了巨大的灵活性,但有时,需要把this
固定下来,JavaScript 提供了call
、apply
、bind
这三个方法,来切换/固定this
的指向。
4.1 Function.prototype.call()
函数实例的call
方法,可以指定函数内部this
的指向,然后在所指定的作用域中,调用该函数。
var obj = {};
var f = function () {
return this;
};
f() === window // true
// 调用call方法,改变this指向
f.call(obj) === obj // true
在上面的例子中,全局环境中运行函数f()
,this
指向全局环境;使用call()
方法,将this
的指向改变为obj
对象,然后在obj
对象的作用域中运行函数f()
。
call
方法的参数,是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
var number = 123;
var obj = { number: 456 };
function a() {
console.log(this.number);
}
// 参数为空,null,undefined默认传入全局对象
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
// 改变this的指向
a.call(obj) // 456
call
方法还可以接受多个参数。call
的第一个参数就是this
所要指向的那个对象,后面的参数则是函数调用时所需的参数。
function add(a, b) {
return a + b;
}
// 多个参数,第一个为this指向的对象,后面的参数是函数调用时需要的参数
add.call(this, 1, 2) // 3
4.2 Function.prototype.apply()
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数。
apply
常用来转换类似数组的对象。
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
4.3 Function.prototype.bind()
bind
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
var counter = {
count: 0,
add: function () {
this.count++;
}
};
// 不绑定
var func = counter.add;
func();
counter.count; // 0
// 绑定
var func = counter.add.bind(counter);
func();
counter.count // 1
上面例子中,如果不绑定,将counter.add
赋值给func
,此时this
指向全局环境。
注意:bind
方法每运行一次,就会返回一个新函数。
5. 参考链接
本篇博文是我自己学习笔记,原文请参考javaScript教程。