资讯专栏INFORMATION COLUMN

理解JavaScript中的this关键词

paulquei / 2221人阅读

摘要:除此之外,还有一种情况也会修改,在一些库中传入回调函数,可能会强制改变的绑定,例如在中本例中的就是被强制改变绑定到了触发事件的元素上。它们的第一个参数是一个对象,它们会把这个对象绑定到,接着在调用函数时指定这个。

理解JavaScript中的this关键词
this关键词是JavaScript语言中一个很重要,同时也是一个非常复杂的机制,它同时也是一个很特殊的关键词,它一般会被自动定义在函数的作用域中。不少很有经验的开发者也经常会被this的指向搞晕,当开发者搞不清楚它的指向的时候,内心的感受实际上差不多是这个样子:
接下来我们将会具体讨论this关键词到底指向什么。
this的绑定规则

首先明确一点,this的绑定和函数的声明位置是没有任何关系的,它只取决于函数的调用位置和调用方式。

1. 默认绑定

首先我们先看一下最常见的也是最简单的函数调用,全局中的独立的函数调用:

var a = "hello";
function foo() {
  console.log(this.a);
}
foo();// "hello"

众所周知,声明在全局作用域中的变量,就是全局对象(window或者global)的一个同名的属性,在本例中,this.a被解析成了全局变量a,而函数foo就是在直接在全局对象下、不带有任何修饰地进行调用的,所以,this的默认绑定规则就是指向全局对象。但是,当我们使用严格模式(strict)进行开发的时候,情况发生了变化

"use strict";
var a = "hello";
function foo() {
  console.log(this.a);
}
foo();// undefined

在严格模式下,this被禁止绑定到全局对象中,所以在本例中,this指向了undefined

2.作为对象方法的调用

看下面的代码:

var a ="world";
var foo = function () {
  console.log(this.a);
};
var obj = {
  a:"hello",
  b:foo
};
obj.b(); //"hello"

本例中,分别在全局对象中定义变量a,和在全局对象中定义属性obj.a,运行结果是this被绑定到了被调用函数所在的对象中,而并非是全局对象中的a,或者你可以说,this指向了该函数的上级对象中。但是严格来说,无论是直接在obj对象中直接定义,还是先在全局对象中定义再添加到obj中,foo函数都并不属于obj对象,然而调用位置会使用obj的上下文来引用函数。简单说,当函数引用有上下文对象时,隐式绑定规则会将函数调用中的this绑定到这个上下文对象中。

3.在构造函数及prototype里的调用
var Foo = function () {
  this.a = "hello";
}
Foo.prototype.bar = function () {
   console.log(this.a);
};
Foo.prototype.bar2 = function () {
   this.bar();
};
var foo = new Foo();
foo.bar2();//"hello"

本例与上例类似,this在“Foo类”(严格说Foo是应该是构造函数,但一般在开发过程中认为Foo是类,这样有助于在面向对象编程中减少误解)中绑定的是“类”里的共有变量和共有方法。

this隐式绑定丢失

上述的几种应用场景还是非常容易理解的,也是比较符合我们对this字面意义的理解的,相信对JavaScript有过接触和研究的童鞋很快就会掌握。下面将继续介绍几种让人感觉匪夷所思的this绑定丢失。
思考以下代码:

var a = "world";
function foo (){
   console.log(this.a);
}
var obj = {
  a:"hello",
  foo:foo
};
var bar = obj.foo; //函数别名
bar(); //"world"

代码执行完成后,我们看到this被绑定到了(或者说指向了)全局对象上,而并不是和前几种情况下绑定到obj对象中,这是为啥呢?
虽然barobj.foo的一个引用,但是实际上它引用的是foo函数本身,因此此时bar其实是一个不带任何修饰的函数调用,因此适用于默认绑定情况。
下面再来看看回调函数中this的绑定情况

function foo() {
  console.log(this.a);
}
function do(fnc) {
  fnc();
};
var obj = {
  a:2,
  foo:foo
};
var a = "world";
do(obj.foo);//"world"

参数传递其实就是一种隐式赋值,因此传入函数时也会被隐式赋值,所以结果和上一个例子没有区别,即便是将函数传入语言内置的的函数(比如setTimeout())中,结果也是没有区别的,this要么被绑定到全局对象中,要么绑定到undefined
除此之外,还有一种情况也会修改this,在一些JavaScript库中传入回调函数,可能会强制改变this的绑定,例如在jquery中

$("#some-id").on("click",function () {
  console.log(this.id);//"some-id"
});

本例中的this就是被强制改变绑定到了触发事件的DOM元素上。

显式绑定

隐式绑定实际上就是在一个对象的内部包含一个指向函数的属性,并通过这个属性间接地引用属性,从而把this间接地绑定到这个对象上。
我们可以使用callapply两个函数进行显式绑定。它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this

var a = "world";
function foo() {
  console.log(this.a);
}
var obj = {
  a:"hello"
};
foo.call(obj);//"hello"
foo() //"world"
单纯的实现this绑定的功能的话,callapply是一样的,它们的区别体现在别的参数中。

思考下面的代码:

function foo() {
  console.log(this.a);
}
var obj = {
  a:"hello"
};
function bar() {
  foo.call(obj);
}
bar(); //"hello"
setTimeout(bar,500);// "hello"
bar.call(window); //"hello",硬绑定的bar不能再修改它的this

我们创建了函数bar,并且手动在它的内部调用foo.call(obj),因此强制把foothis绑定到obj,之后无论怎么调用函数bar,它总会在obj上调用foo,不会丢失,这种绑定方式被称为硬绑定.

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

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

相关文章

  • 【译】javascriptthis键词理解

    摘要:在中,当使用关键字调用函数构造函数时,函数构造函数中也有这个概念,但是它不是惟一的规则,而且常常可以引用来自不同执行上下文的不同对象。因此,我们使用调用函数,可以看到这是对象,并且的属性是正常的。 一直以来,javascript里边的this都是一个很难理解的东西,之前看的最多的就是阮一峰老师关于this的理解: http://www.ruanyifeng.com/blo... htt...

    tainzhi 评论0 收藏0
  • 初学者彻底理解javascript闭包以及this关键

    摘要:理解了这句话,我们就可以来看闭包了闭包前面说过,函数可以访问函数作用域链中的变量,但如果我们想在函数外访问函数内却不行了。 不管是闭包还是this关键字,都是困扰JS初学者的比较难懂的东西,如果你对它们的认识还不足够清晰,那么现在就一起把它们掌握掉。还是那句话,我们从最基本的开始,建立起一个非常清晰的知识结构,好了,开始吧 ? 闭包 当然我们今天说的是javascript里的闭包。要学...

    魏明 评论0 收藏0
  • 理解 JavaScript 中的 this 关键

    摘要:原文许多人被中的关键字给困扰住了,我想混乱的根源来自人们理所当然地认为中的应该像中的或中的一样工作。尽管有点难理解,但它的原理并不神秘。在浏览器中,全局对象是对象。运算符创建一个新对象并且设置函数中的指向调用函数的新对象。 原文:Understanding the this keyword in JavaScript 许多人被JavaScript中的this关键字给困扰住了,我想混乱的...

    jayzou 评论0 收藏0
  • 如何理解JavaScriptthis关键

    摘要:原文链接参考深入理解原型和闭包完结王福朋博客园中的作用域详解博客园 前言 王福朋老师的 JavaScript原型和闭包系列 文章看了不下三遍了,最为一个初学者,每次看的时候都会有一种 大彻大悟 的感觉,而看完之后却总是一脸懵逼。原型与闭包 可以说是 JavaScirpt 中理解起来最难的部分了,当然,我也只是了解到了一些皮毛,对于 JavaScript OOP 更是缺乏经验。这里我想总...

    Yangder 评论0 收藏0
  • 10个流行的JavaScript面试题

    摘要:然而,异步函数不会立即被推入调用堆栈,而是会被推入任务队列,并在调用堆栈为空后执行。将事件从任务队列传输到调用堆栈称为事件循环。我们调用接受和或返回另一个函数称为高阶函数的函数。 为了保证可读性,本文采用意译而非直译 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 1.如何理解 JS 中的this关键字? JS 初学者总是对 this 关键字感到困惑,因为与其他现...

    CollinPeng 评论0 收藏0

发表评论

0条评论

paulquei

|高级讲师

TA的文章

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