this 关键字
在上一篇文章 实例对象与new命令 中有提到,
this
可以用于构造函数当中,表示实例对象。本文将对this
展开详细的介绍。
1.含义
var Animal = {
name: 'pig',
describe: function () {
return '种类:'+ this.name;
}
};
Animal.describe()
// "种类:pig"
上述代码中,this.name
中的this表示name
属性所在的对象。由于this.name
是在describe
方法中调用,而describe
方法当前所在的对象是Animal
,所以this
指向Animal
,this.name
即为Animal.name
简单来说,this
就是属性或方法“当前”所在的对象
当该属性当前所在对象改变时,this的指向也会改变
var pp = {
name: 'pig',
describe: function () {
return '种类:'+ this.name;
}
};
var cc={
name:'cat'
}
cc.describe=pp.descibe;
cc.describe();//种类:cat
上面代码中,pp
的describe
方法被赋给cc
,所以cc.describe
表示describe
方法当时所在的对象是cc
,所以this
就指向cc
.
只要this所在函数赋给另外一个变量,this的指向就会改变
2.实质
JavaScript如何创建一个对象?
2.1 非函数
var obj={name:"pig"};
代码分析 上面代码将一个对象赋值给obj,实际上在JS内部引擎中会有以下两步
(1)生成对象{name:"pig"}
(2)把对象的内存地址赋值给obj
(存的是地址)
若要读取obj.name
,js引擎
先从obj
中拿到该对象的内存地址,再从地址中读取原始对象,返回该原始对象的name
属性
事实上,该对象是以数据字典的结构存储的
{
name: {
[[value]]:"pig"
[[writable]]: true //是否可写
[[enumerable]]: true //是否可遍历
[[configurable]]: true //是否可删改
}
}
name
属性的值保存在属性描述对象的value
属性中
2.2 函数
var obj = { name: function () {} };
当属性的值是一个函数时,JS引擎会单独开辟一块空间存放函数的值,此时属性描述对象value的值会变成函数的地址,而不是某一确切的值。
{
name: {
[[value]]: 函数的地址
...
}
}
由于函数是一个单独的值,所以它可以在不同的环境(代码段)中执行。
var fun=function(){};
var obj.fun=fun;
fun(); //单独执行
obj.fun(); //obj环境下执行
上述代码表示,函数可以在不同环境下执行。
2.3 this产生
由于函数可以在不同的运行环境下执行,那么就需要一个机制,来使得在函数内部获得当前的运行环境,所以JS使用this来指代函数当前的运行环境
var fun=function(){
console.log(this.x)
};
var x=1;
var obj={
fun:fun,
x:2
};
//obj环境执行
obj.fun(); //2
//单独执行
fun(); //1
上述代码中,函数fun
在全局中执行时,this
指向全局的变量window.x
,在obj
环境中执行时,this.x
指向obj.x
3. 使用场合
this
主要有以下几个使用场合
3.1 全局环境
全局中的this
,指向顶层对象window
var x=3;
this.x; //3
this===window; //true
function fun(){
console.log(this===window);
}
fun(); //true;
上述代码表示,只要在全局环境下运行,无论在不在函数体内,this
都是指向window
的.
3.2 构造函数
构造函数中的this
,指的是实例对象
var Obj=function(name){
this.name=name;
};
上面构建了一个构造函数obj
,当用new
命令来调用它时,obj
里的this
将指向实例对象
var o=new Obj("Hello");
o.name; //Hello
3.3 对象的方法
如果对象的方法里包含this
,那么在此方法执行时,this
就指向该对象,若把该方法赋值给另一个对象,就会改变this
的指向
var obj={
fun:function(){
console.log(this);
}
};
obj.fun() // obj
上面代码中,obj.fun
方法执行时,它内部的this
指向obj
。
但是,下面这几种用法,都会改变this
的指向。
//例一
(obj.fun=obj.fun)() //window
//例二
(false||obj.fun)() //window
//例三
(1,obj.fun)() //window
上面代码中,obj.fun是一个单独的值,这个值被调用的时候,运行环境已经变成了全局变量
可以这样理解,加了( )之后,会有一个返回值
例一:返回一个立即执行函数,等同于
(function(){
console.log(this);
})()
例二:括号中是一个表达式,执行布尔值为true的,等同于
(false || function () {
console.log(this);
})()
例三:括号中有逗号,返回值为逗号后的值,等同于
(1, function () {
console.log(this);
})()
上述三种情况,不难看出this指向window,因为该函数都是在全局环境下执行的。
如果this
所在的方法不在该对象的第一层,那么this
只会指向当前一层的对象,而不会继承更上面的层。
var obj={
pig:"I am in obj",
objson:{
fun:function(){
console.log(this.pig);
}
}
};
obj.objson.fun(); //undefined
上面代码中,this
位于objson
中,所以该函数执行时,this
代表对象objson
,而该对象并没有pig
这个属性。
4. 使用this应注意
4.1 避免多重this
由于this的指向是不确定的,所以千万别在函数中包含多重this。
var obj = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
obj.f1()
// Object
// Window
上面代码包含两层this,第一层this指向对象obj,第二层this指向window。因为实际执行的是下面的代码
var fun=function(){
console.log(this);
};
var obj = {
f1: function () {
console.log(this);
var f2 = fun();
}
}
obj.f1()
// Object
// Window
使用严格模式
为了避免这种情况,其中一种解决方法就是使用严格模式“use strict”
,因为在严格模式下,函数体内的this是禁止指向window
全局的。
'use strict';
var obj = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this.toString());
}();
}
}
obj.f1();//Uncaught TypeError: Cannot read property 'toString' of undefined
上面代码中,使用了严格模式'use strict'
,函数体内的this
不可指向window
,所以在原型中没有定义toString
。
4.2 避免数组处理方法中的this
数组的map
和foreach
方法,允许提供一个函数作为参数。这个函数内部不应该使用this
。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
// undefined a1
// undefined a2
上面代码中,foreach
方法的回调函数中的this
,其实是指向window
对象,因此取不到o.v的值。原因跟上一段的多层this
是一样的,就是内层的this
不指向外部,而指向顶层对象。
解决这个问题的一种方法,使用中间变量that
固定this。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
}
o.f()
// hello a1
// hello a2
另一种方法是将this
当作foreach
方法的第二个参数,固定它的运行环境。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
}
o.f()
// hello a1
// hello a2
4.3避免回调函数中的this
回调函数中的this
往往会改变指向,最好避免使用。
var o = new Object();
o.f = function () {
console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
上面代码中,点击按钮以后,控制台会显示false
。原因是此时this
不再指向o对象,而是指向按钮的DOM
对象,因为f
方法是在按钮对象的环境中被调用的。这种细微的差别,很容易在编程中忽视,导致难以察觉的错误。
下一章节将介绍this
的绑定方法,用于解决类似的问题。
最后,希望能和大家一起进步! |
---|