文章目录
this 关键字
this
可以用在构造函数之中,表示实例对象。除此之外,this
还可以用在别的场合。但不管是什么场合,this
都有一个共同点:它总是返回一个对象。简单说,this
就是属性或方法“当前”所在的对象
由于函数可以在不同的运行环境执行,有时候函数我们希望在函数体内部当前环境下运行,但是有时候不指定环境的话,就会在windows环境下运行,就不是我们所希望的情况,所以需要有一种机制,能够在函数体内部获得当前的运行环境。所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境
例如:
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
第一个 f() 输出为1,this.x
指向全局环境的x
,因为全局定义了x=1 所以第一个输出为1
第二个 obj.f() 指定了运行环境 this.x
指向了 obj
下的x
,obj
下的x
为2,所以输出为2
可以看出,指定的运行环境不同,输出的值就不同
使用场合
(1)全局环境
全局环境使用this
,它指的就是顶层对象window
。
this === window // true
function f() {
console.log(this === window);
}
f() // true
上面代码说明,不管是不是在函数内部,只要是在全局环境下运行,this
就是指顶层对象window
。
(2)构造函数
构造函数中的this
,指的是实例对象
var Obj = function (p) {
this.p = p;
};
上面代码定义了一个构造函数Obj
。由于this
指向实例对象,所以在构造函数内部定义this.p
,就相当于定义实例对象有一个p
属性
下面创建一个实例对象o
,并给了属性值为Hello World!
:
var o = new Obj('Hello World!');
o.p // "Hello World!"
所以就会输出Hello World!
(3)对象的方法
如果对象的方法里面包含this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this
的指向。
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
下面这几种用法,就会改变this
的指向
情况一
(obj.foo = obj.foo)() // window
情况二
(false || obj.foo)() // window
情况三
(1, obj.foo)() // window
上面的情况就把obj.foo含义改变了,此时的obj.foo就是一个具体的值了,就不是指定环境了,所以运行环境就是windows了
如果this
所在的方法不在对象的第一层,这时this
只是指向当前一层的对象,而不会继承更上面的层。下面的例子:
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
为什么会undefined呢?
因为a.b.m
方法在a
对象的第二层,就是在a
下面的b
里面,该方法内部的this
不是指向a
,而是指向a.b
,但是Hello在a
下面,所以找不到,就打印undefined
如果要打印,就要将p: 'Hello',
移一下位置变成:
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
这样Hello
就在b
下面了,就可以打印了
使用注意点
避免多层 this
由于this
的指向是不确定的,所以切勿在函数中包含多层的this
。
例如:
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
这样的this
,第一层指向o
,第二层就指向了windows
了
解决办法就是用一个变量that
将this
接收一下,里面的that
固定指向外层的this
,这样内层的this
就不会乱指了。
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
绑定 this 的方法
JavaScript 提供了call
、apply
、bind
这三个方法,来切换/固定this
的指向。
Function.prototype.call()
函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数,利用f.call(obj)
这样就可以指定函数的作用域为obj
了
例如:
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
call
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
例如:
var n = 123;
var obj = { n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
a.call()
a.call(null)
a.call(undefined)
三种返回的值都为123
,没有区别,并且this
指向都为windows
a.call(window)
这个指定了运行环境为windows
,所以输出为123
a.call(obj)
同理,这个指定了运行环境为obj
,所以输出为456
Function.prototype.apply()
apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下:
func.apply(thisValue, [arg1, arg2, ...])
apply
方法的第一个参数也是this
所要指向的那个对象,如果设为null
或undefined
,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call
方法中必须一个个添加,但是在apply
方法中,必须以数组形式添加
例如:
function f(x, y){
console.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
call就是一个一个添加,apply是以数组形式添加
Function.prototype.bind()
bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。
例子如下:
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
正常不用bind()
的话,counter.inc()
就被作为值赋给func
了,这样运行环境就不对了,
而counter.inc.bind(counter)
这样就把inc
里面的this
运行环境绑定在counter
了,就不会出错了。