当前位置:首页 > 日记 > 正文

JavaScript函数中的this四种绑定形式

JavaScript函数中的this四种绑定形式

正文

 javascript中的this和函数息息相关,所以今天,我就给大家详细地讲述一番:javascript函数中的this

一谈到this,很多让人晕晕乎乎的抽象概念就跑出来了,这里我就只说最核心的一点——函数中的this总指向调用它的对象,接下来的故事都将围绕这一点展开

(提醒前排的筒子们准备好茶水和西瓜,我要开始讲故事啦!!)

【故事】有一个年轻人叫"迪斯"(this),有一天,迪斯不小心穿越到一个叫 “伽瓦斯克利”(javascript)的 异世界,此时此刻迪斯身无分文, 他首先要做的事情就是——找到他的住宿的地方——调用函数的对象

this的默认绑定

【故事——线路1】如果迪斯(this)直到天黑前都没有找到能收留自己的住所,他眼看就要过上非洲难民的生活, 这时候,一位乐善好施的魔法师村长——window救世主一般地出现了:先住在我家吧! 

【正文】

当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象

function fire () {  console.log(this === window)}fire(); // 输出true

上面的例子我相信对大多数人都很简单,但有的时候我们把例子变一下就会具有迷惑性:

function fire () { // 我是被定义在函数内部的函数哦!  function innerFire() { console.log(this === window)  }  innerFire(); // 独立函数调用}fire(); // 输出true

函数 innerFire在一个外部函数fire里面声明且调用,那么它的this是指向谁呢? 仍然是window

许多人可能会顾虑于fire函数的作用域对innerFire的影响,但我们只要抓住我们的理论武器——没有明确的调用对象的时候,将对函数的this使用默认绑定:绑定到全局的window对象,便可得正确的答案了 

下面这个加强版的例子也是同样的输出true

var obj = { fire: function () {  function innerFire() {   console.log(this === window)  }  innerFire(); // 独立函数调用  }}obj.fire(); //输出 true

【注意】在这个例子中, obj.fire()的调用实际上使用到了this的隐式绑定,这就是下面我要讲的内容,这个例子我接下来还会继续讲解 

【总结】 凡事函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异 

this的隐式绑定

【故事——线路2】 迪斯(this)穿越来异世界“伽瓦斯克利”(javascript)的时候,刚好身上带了一些钱,于是他找到一个旅馆住宿了下来 

当函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了,这时候,通过this可以直接访问所绑定的对象里面的其他属性,比如下面的a属性

var obj = {  a: 1,  fire: function () {   console.log(this.a)  }}obj.fire(); // 输出1 

现在我们需要对平常司空见惯的的代码操作做一些更深的思考,首先,下面的这两段代码达到的效果是相同的:

// 我是第一段代码function fire () {  console.log(this.a)}var obj = {  a: 1,  fire: fire }obj.fire(); // 输出1// 我是第二段代码var obj = {  a: 1,  fire: function () {    console.log(this.a)   }}obj.fire(); // 输出1

fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别,也就是说在上述隐式绑定的两种形式下,fire通过this还是可以访问到obj内的a属性,这告诉我们: 

1.  this是动态绑定的,或者说是在代码运行期绑定而不是在书写期

2.  函数于对象的独立性, this的传递丢失问题 

(下面的描述可能带有个人的情感倾向而显得不太严谨,但这是因为我希望阅读者尽可能地理解我想表达的意思)
隐式绑定下,作为对象属性的函数,对于对象来说是独立的 

基于this动态绑定的特点,写在对象内部,作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”) 

我想表达的意思是:在上文中,函数虽然被定义在对象的内部中,但它和“在对象外部声明函数,然后在对象内部通过属性名称的方式取得函数的引用”,这两种方式在性质上是等价的(而不仅仅是效果上) 

定义在对象内部的函数只是“恰好可以被这个对象调用”而已,而不是“生来就是为这个对象所调用的” 

借用下面的隐式绑定中的this传递丢失问题来说明:

var obj = {  a: 1, // a是定义在对象obj中的属性 1  fire: function () { console.log(this.a)  }  }var a = 2; // a是定义在全局环境中的变量 2var fireInGrobal = obj.fire; fireInGrobal(); // 输出 2

上面这段简单代码的有趣之处在于: 这个于obj中的fire函数的引用( fireInGrobal)在调用的时候,行为表现(输出)完全看不出来它就是在obj内部定义的,其原因在于:我们隐式绑定的this丢失了!! 从而 fireInGrobal调用的时候取得的this不是obj,而是window 

上面的例子稍微变个形式就会变成一个可能困扰我们的bug: 

在上面,我们的

var a = 2;var obj = { a: 1, // a是定义在对象obj中的属性 fire: function () {   console.log(this.a)  }} function otherFire (fn) {  fn();} otherFire(obj.fire); // 输出2

关键角色是otherFire函数,它接受一个函数引用作为参数,然后在内部直接调用,但它做的假设是参数fn仍然能够通过this去取得obj内部的a属性,但实际上, this对obj的绑定早已经丢失了,所以输出的是全局的a的值(2),而不是obj内部的a的值(1)

在一串对象属性链中,this绑定的是最内层的对象

在隐式绑定中,如果函数调用位置是在一串对象属性链中,this绑定的是最内层的对象。如下所示:

var obj = {  a: 1,  obj2: {   a: 2,   obj3: {    a:3,    getA: function () {     console.log(this.a)      }   }  }}obj.obj2.obj3.getA(); // 输出3

this的显式绑定:(call和bind方法)

【故事——线路3】 迪斯(this)穿越来异世界“伽瓦斯克利”(javascript),经过努力的打拼,积累了一定的财富,于是他买下了自己的房子

上面我们提到了this的隐式绑定所存在的this绑定丢失的问题,也就是对于 “ fireInGrobal = obj.fire”

fireInGrobal调用和obj.fire调用的结果是不同的,因为这个函数赋值的过程无法把fire所绑定的this也传递过去。这个时候,call函数就派上用场了

call的基本使用方式: fn.call(object)

fn是你调用的函数,object参数是你希望函数的this所绑定的对象。

fn.call(object)的作用:

1.即刻调用这个函数(fn)

2.调用这个函数的时候函数的this指向object对象 

例子:

var obj = {  a: 1, // a是定义在对象obj中的属性  fire: function () {   console.log(this.a)  }}var a = 2; // a是定义在全局环境中的变量 var fireInGrobal = obj.fire;fireInGrobal(); // 输出2fireInGrobal.call(obj); // 输出1

原本丢失了与obj绑定的this参数的fireInGrobal再次重新把this绑回到了obj 

但是,我们其实不太喜欢这种每次调用都要依赖call的方式,我们更希望:能够一次性 返回一个this被永久绑定到obj的fireInGrobal函数,这样我们就不必每次调用fireInGrobal都要在尾巴上加上call那么麻烦了。 

怎么办呢? 聪明的你一定能想到,在fireInGrobal.call(obj)外面包装一个函数不就可以了嘛!

var obj = {  a: 1, // a是定义在对象obj中的属性  fire: function () {  console.log(this.a)  }}var a = 2; // a是定义在全局环境中的变量 var fn = obj.fire;var fireInGrobal = function () { fn.call(obj) //硬绑定}fireInGrobal(); // 输出1 

如果使用bind的话会更加简单

var fireInGrobal = function () { fn.call(obj) //硬绑定}

可以简化为:

var fireInGrobal = fn.bind(obj);

call和bind的区别是:在绑定this到对象参数的同时: 

1.call将立即执行该函数

2.bind不执行函数,只返回一个可供执行的函数 

【其他】:至于apply,因为除了使用方法,它和call并没有太大差别,这里不加赘述 

在这里,我把显式绑定和隐式绑定下,函数和“包含”函数的对象间的关系比作买房和租房的区别。 

因为this的缘故

在隐式绑定下:函数和只是暂时住在“包含对象“的旅馆里面,可能过几天就又到另一家旅馆住了

在显式绑定下:函数将取得在“包含对象“里的永久居住权,一直都会”住在这里“

new绑定

【故事】 迪斯(this)组建了自己的家庭,并生下多个孩子(通过构造函数new了许多个对象) 

执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象 

function foo (a) {  this.a = a;}var a1 = new foo (1);var a2 = new foo (2);var a3 = new foo (3);var a4 = new foo (4);console.log(a1.a); // 输出1console.log(a2.a); // 输出2console.log(a3.a); // 输出3console.log(a4.a); // 输出4

总结

以上所述是小编给大家介绍的JavaScript函数中的this四种绑定形式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

相关文章

浅谈JS中的反柯里化 |  uncurrying

浅谈JS中的反柯里化 | uncurrying

浅谈,柯里,电脑软件,JS,uncurrying,反柯里化相反,反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.即把如下给定的函数签名,obj.func(arg1, arg2)转化成一个函数形式,签名如下:func(obj, arg1, arg…

JavaScript的事件机制详解

JavaScript的事件机制详解

事件机制,详解,电脑软件,JavaScript,事件是将JavaScript脚本与网页联系在一起的主要方式,是JavaScript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响至关重要。本文将详细探讨JavaScript的事件机制,并对比分析了浏览器之间…

thinkPHP5.0框架简单配置作用域的

thinkPHP5.0框架简单配置作用域的

作用域,框架,配置,方法,简单,本文实例讲述了thinkPHP5.0框架简单配置作用域的方法。分享给大家供大家参考,具体如下:配置参数支持作用域的概念,默认情况下,所有参数都在同一个系统默认作用域下面。如果你的配置参数需要用于不同的项目或者相互…

Jquery根据浏览器窗口改变调整大小

Jquery根据浏览器窗口改变调整大小

浏览器,方法,调整,大小,窗口,如下所示:当浏览器窗口发生该表示,会触发resize函数// 浏览器大小发生变化时,给id为main-container的容器设置最小高度<script type="text/javascript"> $(function(){ $(window).resize(function(){ $…

excel2013如何设置纵横页面布局

excel2013如何设置纵横页面布局

页面布局,方法,设置,如何设置,电脑软件,  通常在Excel2013中记录数据,都是采用的纵排排列,就是以列的形式呈现出来,但是有时候用种方式不适合怎么办?下面小编就教你怎么设置excel2013的纵横布局,希望看完本教程的朋友都能学会并运用起来。exc…

word2013怎么插入窗口域word2013插

word2013怎么插入窗口域word2013插

步骤,方法,窗口,电脑软件,strong,  我们在使用word的时候,相信很多人都不知道窗体域这个功能,那么今天小编就来告诉你怎么插入这个功能吧。word2013插入窗口域的步骤第一步,点击&ldquo;文件&rdquo;并选择&ldquo;选项&rdquo;word2013插入窗口…

WPS备份管理怎么备份以便恢复未保

WPS备份管理怎么备份以便恢复未保

备份,恢复,文件,文件备份,电脑软件,  WPS加入了自动备份功能,一旦断电或异常关机等意外不可控情况时便可还原没有来及保存的文件,以便恢复之前的文件或异常未保存的文件。以下是小编为您带来的关于WPS备份管理备份以便恢复未保存的文件,希望…

使用JavaScript实现链表的数据结构

使用JavaScript实现链表的数据结构

链表,数据结构,代码,电脑软件,JavaScript,链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer) — 维基百科上面是维基百科对 链表 的解读。下面我们用…

老生常谈计算机中的编码问题 | 必

老生常谈计算机中的编码问题 | 必

编码问题,计算,老生常谈,必看,机中,计算机中的编码问题因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制1111111…

通过V8源码看一个关于JS数组排序的

通过V8源码看一个关于JS数组排序的

数组排序,诡异,源码,电脑软件,JS,前言前几天一个朋友在微信里面问我一个关于 JS 数组排序的问题。通过该问题发现了一些之前没发现的内容,下面话不多少了,来一起看看详细的介绍吧。原始数组如下:var data = [ {value: 4}, {value: 2}, {valu…

Photoshop 7.0新增了哪些功能?

Photoshop 7.0新增了哪些功能?

功能,新增了,电脑软件,Photoshop,Photoshop 7.0 上市好久了 ,当时它也是现在的CC 版本一样 独一无二的, 想知道当年PS&ldquo;进化&rdquo;的过程的同学跟我来吧!介绍你们当时新增的功能。软件名称:Adobe PhotoShop7.0 简体中文版软件大小:154MB更…

Excel中表格工作表保护密码撤销的

Excel中表格工作表保护密码撤销的

密码,操作方法,表格,工作,操作步骤,  在做excel表格时我们有时候会给自己的工作表加密,一旦忘记密码就会很麻烦,如何绕过密码,直接撤销保护。今天,小编就教大家在Excel中表格工作表保护密码撤销的操作方法。Excel中表格工作表保护密码撤销的…