资讯专栏INFORMATION COLUMN

深入理解JavaScript中的this指向

不知名网友 / 864人阅读

摘要:和作用域判别失败相关,而代表作用域判别成功,但是对结果的操作是非法的不合理的。在中,构造函数只是使用操作符时被调用的函数,它们并不属于一个类,也不会实例化一个类。也就是说,中,不存在所谓的构造函数,只有对函数的构造调用。

与其他语言相比,js中的this有所不同,也是比较头疼的问题。在参考了一些资料后,今天,就来深入解析一下this指向问题,有不对的地方望大家指出。

为什么要用this

对于前端开发者来说,this是比较复杂的机制,那么为什么要花大量时间来学习呢,先来看一段代码。
如果不使用this,要给identify( )和speak( )显式传入一个对象:

function identify(context) {
    return context.name.toUpperCase();
}
function speak(context) {
    var greeting = "Hello, I"m" + identify(context);
    console.log(greeting);
}
identify(you);
speak(me);

可以看到,speak( )里面直接写了identify( )的函数名,然而,随着使用模式越来越复杂,显式传递的上下文会让代码变得混乱,尤其体现在面向对象中。
显然,this提供了一种方式来隐式“传递”一个对象的引用,更加简洁,易于复用。

this的误解 1. this指向函数本身

记录函数foo被调用的次数:

function foo(num) {
    console.log("foo:" + num);
    
    // 记录次数
    this.count++;
}

foo.count = 0;

var i;

for (i = 0; i < 10; i++) {
    if (i > 5) {
        foo(i);
    }
}

// foo: 6
// foo: 7
// foo: 8
// foo: 9

//foo被调用了多少次?
console.log(foo.count); // 0

从前两次的console.log( )可以看出,foo确实被调用了4次,但是foo.count仍然为0,显然this指向函数本身的理解是错误的。

2. this指向函数作用域

要明确的是,this在任何情况下都不指向函数的词法作用域。因为,作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。
下面的代码试图使用this来隐式引用函数的词法作用域,没有成功:

function foo() {
    var a = 2;
    this.bar();
}
function bar() {
    console.log(this.a);
}
foo(); // ReferenceError: a is not defined

直接报出了访问不到foo( )中的a。ReferenceError和作用域判别失败相关,而TypeError代表作用域判别成功,但是对结果的操作是非法的、不合理的。

this是什么

排除了以上两个误解之后,来看一下this到底是什么。
this是运行时绑定的,它和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(执行上下文),这个记录包含函数在哪里被调用,函数的调用方式、传入的参数等,this就是这个记录的一个属性,在函数执行的过程中用到。即,this总是指向调用它所在方法的对象

1. 在浏览器中,调用方法没有明确对象时,this指向window
function foo() {
    console.log(this.a);
}

var a = 2;

foo(); // 2

在全局中声明变量a = 2,然后在全局中直接调用foo( ),this指向了全局对象,得到a的值。
要注意的是,在严格模式(strict mood)下,如果this没有被执行环境定义,那它将绑定为undefined。

function foo() {
    "use strict";
    
    console.log(this.a);
}

var a = 2;

foo(); // TypeError: this is undefined

在严格模式下,调用foo( )不影响this绑定。

function foo() {
    console.log(this.a);
}

var a = 2;

(function() {
    "use strict";
    
    foo(); // 2
})();
2. 在浏览器中,setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window
function foo() {
    console.log(this.a);
}

var obj = {
    a: 2,
    foo: foo
};

var a = "global";

setTimeout(obj.foo, 100); // "global"

JavaScript中的setTimeout( )的实现和下面伪代码相似:

function setTimeout(fn, delay) {
    // 等待delay毫秒
    fn(); // 调用函数
}
3. apply / call / bind可以强制改变this指向
function foo() {
    console.log(this.a);
}

var obj = {
    a: 2
};

foo.call(obj); // 2
foo.apply(obj); // 2
foo.bind(obj); // 2

call和apply的区别在于第二个参数,call是把args全部列出来,用“,”分隔,而apply是一个类数组。call、apply是硬绑定,通过硬绑定的函数不能再修改它的this。

function foo() {
    console.log(this.a);
}

var obj = {
    a: 2
};

var bar = function() {
    foo.call(obj);
}

bar(); // 2
setTimeout(bar, 100); // 2

bar.call(window); // 2

函数foo( )内部手动调用了foo.call(obj),把foo的this强制绑定到了obj,所以后面即使又把bar( )绑定到了window,还是无法改变this指向。

4. new操作符改变this指向

在传统的面向对象语言中,会使用new初始化类,然而在JavaScript中new的机制和面向对象语言完全不同。在js中,构造函数只是使用new操作符时被调用的函数,它们并不属于一个类,也不会实例化一个类。也就是说,js中,不存在所谓的“构造函数”,只有对函数的“构造调用”。

function foo(a) {
    this.a = a;
}

var bar = new foo(2);
console.log(bar.a); // 2

使用new调用foo( ),会构造一个新对象并把它绑定到foo( )调用中的this上。

优先级

既然有那么多可以改变this的指向,那么它们的优先级是怎么样的呢,记住这句话:范围越小,优先级越高。可以按照下面的顺序来判断:

判断函数是否在new中调用过:

var bar = new foo();

判断函数是否通过call、apply、bind绑定过:

var bar = foo.call(obj);

判断函数是否在某个上下文对象中调用过:

var bar = obj.foo();

如果以上情况均不存在,那么在严格模式下,绑定到undefined,否则绑定到全局对象:

var bar = foo();

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103071.html

相关文章

  • 我对JavaScriptthis的一些理解

    摘要:匿名函数的执行环境具有全局性,因此它的对象通常指向。如果对此有疑惑,可以看知乎上的答案知乎匿名函数的指向为什么是作为对象方法的调用,指向该对象当函数作为某个对象的方法调用时,就指这个函数所在的对象。 因为日常工作中经常使用到this,而且在JavaScript中this的指向问题也很容易让人混淆一部分知识。 这段时间翻阅了一些书籍也查阅了网上一些资料然后结合自己的经验,为了能让自...

    focusj 评论0 收藏0
  • Javascript深入理解this作用域问题以及new/let/var/const对this

    摘要:理解作用域高级程序设计中有说到对象是在运行时基于函数的执行环境绑定的在全局函数中,等于,而当函数被作为某个对象调用时,等于那个对象。指向与匿名函数没有关系如果函数独立调用,那么该函数内部的,则指向。 理解this作用域 《javascript高级程序设计》中有说到: this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象调用时,t...

    snowLu 评论0 收藏0
  • JavaScriptthis指向深入解析

    普通函数的this指向 简单说说 首先,按照惯例,我们先举个栗子: var bar = 2; function foo() { this.bar = 1; this.getBar = function() { console.log(this.bar); } } var test = new foo(); var getBar = test.getBar; test.getB...

    AlphaGooo 评论0 收藏0
  • 深入理解 Javascriptthis

    摘要:深入浅出的理解问题的由来写法一写法二虽然和指向同一个函数,但是执行结果可能不一样。该变量由运行环境提供。所以,就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。 深入浅出this的理解 问题的由来 var obj = { foo: function(){} } var foo = obj.foo; // 写法一 obj.foo(); // 写法二 foo...

    OnlyMyRailgun 评论0 收藏0
  • 深入理解JavaScript(三):获取数组中的最大值方法(this,apply)

    摘要:三个方法的作用,都是改变的指向,只是用法稍微有些区别什么是既不指向函数自身,也不指函数的词法作用域。它在函数定义的时候是确定不了的在函数被调用时才发生的绑定,也就是说具体指向什么,取决于你是怎么调用的函数。 1.排序法 思路:给数组先排序(由大到小排序),第一项就是最大值 let arr = [1,5,6,7,9,20,40,2,3]; let max1 = arr.sort(func...

    canopus4u 评论0 收藏0

发表评论

0条评论

不知名网友

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<