(JS)this关键字 (一)

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指向Animalthis.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

上面代码中,ppdescribe方法被赋给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

数组的mapforeach方法,允许提供一个函数作为参数。这个函数内部不应该使用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的绑定方法,用于解决类似的问题。


最后,希望能和大家一起进步!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值