this的用法

本文详细探讨了JavaScript中函数调用时this的四种不同情况:全局调用、作为对象方法调用、构造函数调用和apply调用。强调了this的值取决于函数的调用方式,特别是new关键字如何改变this的指向。此外,还讨论了return语句对this的影响以及new关键字的工作原理。通过对各种场景的分析,帮助读者深入理解JavaScript中this的动态绑定特性。
摘要由CSDN通过智能技术生成

this 指向问题

在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。

this是 JavaScript 语言的一个关键字。

它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。

function test() {
 this.x = 1;
}

上面代码中,函数test运行时,内部会自动有一个this对象可以使用。

那么,this的值是什么呢?

函数的不同使用场合,this有不同的值。总的来说,this就是函数运行时所在的环境对象。下面分四种情况,详细讨论this的用法。

情况一:纯粹的函数调用

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象。请看下面这段代码,它的运行结果是1。

var x = 1;
function test() {
   console.log(this.x);
}
test();  // 1

情况二:作为对象方法的调用

函数还可以作为某个对象的方法调用,这时this就指这个上级对象。

function test() {
  console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;

obj.m(); // 1
var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user); //追梦子
    }
}
window.o.fn();

这里同样也是对象o点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3可以证明,如果不相信,那么接下来我们继续看几个例子。

var o = {
    a:10,
    b:{
        // a:12,
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn(); //undefined

尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。

还有一种比较特殊的情况,例子4:

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();

这里this指向的是window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要。

this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。

this讲来讲去其实就是那么一回事,只不过在不同的情况下指向的会有些不同,上面的总结每个地方都有些小错误,也不能说是错误,而是在不同环境下情况就会有不同,所以我也没有办法一次解释清楚,只能你慢慢地的去体会。

情况三 作为构造函数调用

所谓构造函数,就是通过这个函数,可以生成一个新对象。这时,this就指这个新对象。

function Fn() {
 this.x = 1;
}

var a = new Fn();
a.x // 1

运行结果为1。为了表明这时this不是全局对象,

这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,理解这句话可以想想我们的例子3,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。
我们对代码做一些改变:

var x = 2;
function test() {
   this.x = 1;
   console.log(this);
}

var obj = new test(); //  this 指向obj ={x:1,a:23} ==> x是通过this.x 添加上去的,因为this指向obj所以this.x === obj.x
obj.a = 23;
console.log(x); // 2

运行结果为2,表明全局变量x的值根本没变。

情况四 apply 调用

apply()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this指的就是这第一个参数。

var x = 0;
function test() {
 console.log(this.x);
}

var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply() // 0

apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。

如果把最后一行代码修改为

obj.m.apply(obj); //1

运行结果就变成了1,证明了这时this代表的是对象obj

更新一个小问题当this碰到return时

function fn()  
{  
    this.user = '追梦子';  
    return {};  
}
var a = new fn;  
console.log(a.user); //undefined

再看一个

function fn()  
{  
    this.user = '追梦子';  
    return function(){};
}
var a = new fn;  
console.log(a.user); //undefined

再来

function fn()  
{  
    this.user = '追梦子';  
    return 1;
}
var a = new fn;  
console.log(a.user); //追梦子
function fn()  
{  
    this.user = '追梦子';  
    return undefined;
}
var a = new fn;  
console.log(a.user); //追梦子

什么意思呢?

**如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
**

function fn()  
{  
    this.user = '追梦子';  
    return undefined;
}
var a = new fn;  
console.log(a); //fn {user: "追梦子"}

还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

function fn()  
{  
    this.user = '追梦子';  
    return null;
}
var a = new fn;  
console.log(a.user); //追梦子

new关键字

语法

new constructor[([arguments])]
参数
  • constructor

    一个指定对象实例的类型的类或函数。

  • arguments

    一个用于被 constructor 调用的参数列表。

描述

new 关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即**{}**);
  2. 链接该对象(设置该对象的constructor)到另一个对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this

(译注:关于对象的 constructor,参见 Object.prototype.constructor

创建一个用户自定义的对象需要两步:

  1. 通过编写函数来定义对象类型。
  2. 通过 new 来创建对象实例。

创建一个对象类型,需要创建一个指定其名称和属性的函数;对象的属性可以指向其他对象,看下面的例子:


 function Foo() {
        //   ....
        console.log(this); //Foo
      }
      Foo.prototype.name = "我是prototype上的属性";
      var foo = new Foo();
      //   var foo = new Foo;  //没有参数的情况下与上相同(var foo = new Foo();)
      console.log(foo.name); // 我是prototype上的属性


当代码 new *Foo*(...) 执行时,会发生以下事情:

  1. 一个继承自 *Foo*.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new *Foo* 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

你始终可以对已定义的对象添加新的属性。例如,car1.color = "black" 语句给 car1 添加了一个新的属性 color,并给这个属性赋值 “black”。但是,这不会影响任何其他对象。要将新属性添加到相同类型的所有对象,你必须将该属性添加到 Car 对象类型的定义中。

你可以使用 Function.prototype 属性将共享属性添加到以前定义的对象类型。这定义了一个由该函数创建的所有对象共享的属性,而不仅仅是对象类型的其中一个实例。下面的代码将一个值为 nullcolor 属性添加到 car 类型的所有对象,然后仅在实例对象 car1 中用字符串 “black” 覆盖该值。详见 prototype

function Car() {}
car1 = new Car();
car2 = new Car();

console.log(car1.color);    // undefined

Car.prototype.color = "original color";
console.log(car1.color);    // original color

car1.color = 'black';
console.log(car1.color);   // black

console.log(car1.__proto__.color) //original color
console.log(car2.__proto__.color) //original color
console.log(car1.color)  // black
console.log(car2.color) // original color

如果你没有使用 new 运算符, 构造函数会像其他的常规函数一样被调用, 并*不会创建一个对象**。***在这种情况下, this 的指向也是不一样的。

示例
对象类型和对象实例

假设你要创建一个汽车的对象类型。你希望这个类型叫做car,这个类型具备make, model, year等属性,要做到这些,你需要写这样一个函数:

function Car(make, model, year) {
   this.make = make;
   this.model = model;
   this.year = year;
}

现在,你可以如下所示创建一个 mycar 的对象:

var mycar = new Car("Eagle", "Talon TSi", 1993);

这段代码创建了 mycar 并给他的属性指定值,于是 mycar.make 的值为"Eagle", mycar.year 的值为1993,以此类推。

你可以通过调用 new 来创建任意个汽车对象。例如:

var kenscar = new Car("Nissan", "300ZX", 1992);
对象属性为其他对象

假设你定义了一个对象叫做 person

function Person(name, age, sex) {
   this.name = name;
   this.age = age;
   this.sex = sex;
}

然后实例化两个新的 person 对象如下:

var rand = new Person("Rand McNally", 33, "M");
var ken = new Person("Ken Jones", 39, "M");

然后你可以重写 car 的定义,添加一个值为 person 对象的 owner 属性,如下:

function Car(make, model, year, owner) {
   this.make = make;
   this.model = model;
   this.year = year;
   this.owner = owner;
}

为了实例化新的对象,你可以用如下代码:

var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);

创建对象时,并没有传字符串或数字给owner,而是传了对象 randken 。这个时候,你可以这样来获取 car2 的owner的name:

car2.owner.name

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值