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


Posted in Javascript onAugust 15, 2017

正文

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

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

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

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

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

this的默认绑定

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

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

【正文】

当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的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)的时候,刚好身上带了一些钱,于是他找到一个旅馆住宿了下来 

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

当函数被一个对象“包含”的时候,我们称函数的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是定义在全局环境中的变量 2
var 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),经过努力的打拼,积累了一定的财富,于是他买下了自己的房子

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

上面我们提到了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(); // 输出2
fireInGrobal.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并没有太大差别,这里不加赘述 

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

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

因为this的缘故

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

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

new绑定

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

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

执行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); // 输出1
console.log(a2.a); // 输出2
console.log(a3.a); // 输出3
console.log(a4.a); // 输出4

总结

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

Javascript 相关文章推荐
ExtJs扩展之GroupPropertyGrid代码
Mar 05 Javascript
jQuery $.each的用法说明
Mar 22 Javascript
jQuery on方法传递参数示例
Dec 09 Javascript
javascript多行字符串的简单实现方式
May 04 Javascript
javascript简单比较日期大小的方法
Jan 05 Javascript
jQuery绑定事件的几种实现方式
May 09 Javascript
jQuery实现的自动加载页面功能示例
Sep 04 Javascript
AngularJS 单元测试(一)详解
Sep 21 Javascript
JS 终止执行的实现方法
Nov 24 Javascript
基于jQuery中ajax的相关方法汇总(必看篇)
Nov 08 jQuery
React传值 组件传值 之间的关系详解
Aug 26 Javascript
纯JS实现五子棋游戏
May 28 Javascript
详解react使用react-bootstrap当轮子造车
Aug 15 #Javascript
JScript实现表格的简单操作
Aug 15 #Javascript
AngularJS日程表案例详解
Aug 15 #Javascript
jQuery实现菜单栏导航效果
Aug 15 #jQuery
js实现网页的两个input标签内的数值加减(示例代码)
Aug 15 #Javascript
JScript实现地址选择功能
Aug 15 #Javascript
jQuery实现腾讯信用界面(自制刻度尺)样式
Aug 15 #jQuery
You might like
zend framework配置操作数据库实例分析
2012/12/06 PHP
ucenter通信原理分析
2015/01/09 PHP
PHP结合Ueditor并修改图片上传路径
2016/10/16 PHP
PHP PDO数据库操作预处理与注意事项
2019/03/16 PHP
TP5框架请求响应参数实例分析
2019/10/17 PHP
Yii redis集合的基本使用教程
2020/06/14 PHP
JS获取dom 对象 ajax操作 读写cookie函数
2009/11/18 Javascript
js获取元素在浏览器中的绝对位置
2010/07/24 Javascript
JQuery将文本转化成JSON对象需要注意的问题
2011/05/09 Javascript
jQuery 1.9使用$.support替代$.browser的使用方法
2014/05/27 Javascript
jQuery源码解读之removeAttr()方法分析
2015/02/20 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
2015/09/27 Javascript
JavaScript实现自动生成网页元素功能(按钮、文本等)
2015/11/21 Javascript
js操作数组函数实例小结
2015/12/10 Javascript
超赞的jQuery图片滑块动画特效代码汇总
2016/01/25 Javascript
javascript禁止超链接跳转的方法
2016/02/02 Javascript
浅谈vue实现数据监听的函数 Object.defineProperty
2017/06/08 Javascript
最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
2017/08/16 Javascript
10 种最常见的 Javascript 错误(频率最高)
2018/02/08 Javascript
原生JS实现$.param() 函数的方法
2018/08/10 Javascript
Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)
2018/08/24 Javascript
使用Python快乐学数学Github万星神器Manim简介
2019/08/07 Python
python 实现人和电脑猜拳的示例代码
2020/03/02 Python
如何利用python 读取配置文件
2021/01/06 Python
CSS3的 fit-content实现水平居中
2017/09/07 HTML / CSS
美国从事品牌鞋类零售的连锁店:Famous Footwear
2016/08/25 全球购物
飞利浦美国官网:Philips美国
2020/02/28 全球购物
Linux上比较文件的命令都有哪些
2013/09/28 面试题
购房意向书
2014/04/01 职场文书
安全责任协议书
2014/04/21 职场文书
2014幼儿园教师个人工作总结
2014/11/08 职场文书
关于开学的感想
2015/08/10 职场文书
教导处教学工作总结
2015/08/12 职场文书
公安干警正风肃纪心得体会
2016/01/15 职场文书
2019年工作总结范文
2019/05/21 职场文书
导游词之河北滦平金山岭长城
2019/10/16 职场文书