JavaScript中this的四个绑定规则总结


Posted in Javascript onSeptember 26, 2016

前言

如果要问javascript中哪两个知识点容易混淆,作用域查询和this机制绝对名列前茅。所以这篇文章开始将介绍javascript中this的四个绑定规则,下面来一起看看吧。

绑定规则

1. 默认绑定

独立函数调用时,this 指向全局对象,如果使用严格模式,那么全局对象无法使用默认绑定, this绑定至 undefined

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

严格模式时:

function foo() {
 "use strict";
 console,log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined

2. 隐式绑定

当函数引用有上下文对象时(即函数作为引用属性被添加到对象中),隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。

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

对象属性引用链中只有最顶层或者说最后一层会影响调用位置:

obj1.obj2.foo(); // foo 中的 this 与 obj2 绑定

2.1 隐式丢失

隐式丢失指的是函数中的 this 丢失绑定对象,即它会应用第 1 条的默认绑定规则,从而将 this 绑定到全局对象或者 undefined 上,取决于是否在严格模式下运行。以下情况会发生隐式丢失:

绑定至上下文对象的函数被赋值给一个新的函数,然后调用这个新的函数时:

function foo() {
 console.log( this.a);
}
var obj = {
 a: 2,
 foo: foo
};
var bar = obj.foo; //函数别名
var a = "这是全局变量喔";
bar(); // "这是全局变量喔"

传入回调函数时:

function foo() {
 console.log( this.a);
}
function doFoo(fn) {
 fn(); // <-- 调用位置
}
var obj = {
 a: 2,
 foo: foo
};
var a = "这是全局变量喔";
doFoo( obj.foo ); // "这是全局变量喔"

其实这就是第一种情况的变种,实际上参数传递就是一种隐式赋值。除了开发人员自定义的函数,在将函数传入语言内置的函数比如 setTimeout 时,同样会发生隐式丢失的情况。

3. 显式绑定

显式绑定的核心是 JavaScript 内置的 call(..) apply(..) 方法,这两个方法在 JavaScript 提供的绝大多数函数以及开发者自己创建的所有函数上都可以使用。

call(..) apply(..)的第一个参数是一个对象(二者区别在后面传入的参数形式,这里不是重点,不讨论),他们会将 this 绑定到这个对象上。因为你可以直接指定 this 绑定的对象,所以这条规则被称为显式绑定。

function foo() {
 console.log( this.a);
}
var obj = {
 a: 2
};
foo.call(obj); // 2

如果 call 或者 apply 传入的第一个参数是原始值(字符串类型、布尔类型或者数字类型),那么该原始值会被转换成它的对象形式(new String()new Boolean() new Number() ),俗称“装箱”。

显式绑定仍然无法解决丢失绑定问题。

3.1 硬绑定

作为显式绑定的一个变种,硬绑定可以解决丢失绑定问题。

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); //无效,硬绑定的 bar 不会再修改它的 this

在一个新的函数内部强制绑定 this 到某个对象上,无论之后如何调用这个新的函数,其 this 都不会丢失。

典型应用场景为创建一个包裹函数,传入所有的参数并返回接收到的所有值:

function foo(something) {
 console.log(this.a, something);
 return this.a + something;
}
var obj = {
 a:2
};
var bar = function() {
 return foo.call(obj, arguments);
};
var b = bar(3); // 2 3
console.log(b); // 5

或者将绑定的对象改为可配置,这样就成了一个辅助绑定函数:

...
function bind(fn, obj){
 return function(){
 return fn.apply(obj, arguments);
 };
}
...

由于硬绑定实在太过常见,所以 ES5 提供了内置的 Function.prototype.bind,其用法如下:

function foo(something) {
 console.log(this.a, something);
 return this.a + something;
}
var obj = {
 a: 2
};
var bar = foo.bind(obj);
var b = bar(3); // 2 3
console.log(b); // 5

3.2 API 调用的“上下文”

JavaScript 自身以及许多第三方库的函数都提供了一个可选的参数,通常被称为“上下文”,其作用和 bind(..) 一样,确保回调函数使用指定的 this

function foo(el) {
 console.log( el, this.id);
}
var obj = {
 id: "awsome"
};
//调用 foo(..) 时把 this 绑定到 obj
[1,2,3].forEach(foo, obj);
// 1 awsome 2 awsome 3 awsome

实际上这些函数背后还是调用了 call() 或者 apply() ,只不过这样开发者需要写的代码就少了一些。

4. new 绑定

使用 new 来调用函数时,会自动执行下面的操作:

     1、创建一个全新的对象

     2、这个新对象会被执行 [[原型]] 连接

     3、这个新对象会绑定到函数调用的 this

     4、如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象

举例如下:

function foo() {
 this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2

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

优先级

具体推断细节不再表述,以上四种绑定规则的使用先后推断如下:

1、函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。

var bar = new foo();

2、函数是否通过 callapply (显示绑定)或者硬绑定?如果是的话,this 绑定的是指定的对象。

var bar = foo.call(obj2);

3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。

var bar = obj1.foo();

4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。

var bar = foo();

绑定例外

如果把 null 或者 undefined 作为 this 的绑定对象传入 callapply 或者 bind,那么这些值在调用时会被忽略,实际应用的是默认绑定规则。(书中推荐使用一个空对象来绑定 this)。

间接引用。这种情况容易在赋值时发生:

function foo() {
 console.log( this.a);
}
var a = 2;
var o = {a: 3, foo: foo};
var p = {a: 4};
o.foo(); // 3
(p.foo = o.foo)(); // 2

p.foo() 实际上引用了 foo() ,如此,会应用默认绑定。

另外ES6 对改变 this 的混乱绑定作了相应的努力,诞生了箭头函数,其根据当前词法作用域来决定 this 而非上面的四条规则,具体来说,箭头函数会继承外层函数调用的 this 绑定(这其实和 ES6 之前代码中的 self = this 是一个道理)。

总结

以上就是这篇文章的全部内容,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
js中关于String对象的replace使用详解
May 24 Javascript
ExtJS下书写动态生成的xml(兼容火狐)
Apr 02 Javascript
raphael.js绘制中国地图 地图绘制方法
Feb 12 Javascript
深入理解JS中的变量及作用域、undefined与null
Mar 04 Javascript
JavaScript中的原型和继承详解(图文)
Jul 18 Javascript
JavaScript实现自动弹出窗口并自动关闭窗口的方法
Aug 06 Javascript
jQuery入门基础知识学习指南
Aug 14 Javascript
ANGULARJS中使用JQUERY分页控件
Sep 16 Javascript
JS实现线性表的链式表示方法示例【经典数据结构】
Apr 11 Javascript
js中字符型和数值型数字的互相转化方法(必看)
Apr 25 Javascript
Node.js成为Web应用开发最佳选择的原因
Feb 05 Javascript
使用VScode 插件debugger for chrome 调试react源码的方法
Sep 13 Javascript
jQuery 选择器(61种)整理总结
Sep 26 #Javascript
jQuery tagsinput在h5邮件客户端中应用详解
Sep 26 #Javascript
Windows环境下npm install 报错: operation not permitted, rename的解决方法
Sep 26 #Javascript
几句话带你理解JS中的this、闭包、原型链
Sep 26 #Javascript
BootStrap下拉菜单和滚动监听插件实现代码
Sep 26 #Javascript
通过BootStrap实现轮播图的实际应用
Sep 26 #Javascript
jQuery css() 方法动态修改CSS属性
Sep 25 #Javascript
You might like
PHP foreach循环使用详解与实例代码
2010/05/08 PHP
php判断输入不超过mysql的varchar字段的长度范围
2011/06/24 PHP
PHP中使用crypt()实现用户身份验证的代码
2012/09/05 PHP
PHP钩子实现方法解析
2019/05/21 PHP
nicejforms——美化表单不用愁
2007/02/20 Javascript
jquery禁止输入数字以外的字符的示例(纯数字验证码)
2014/04/10 Javascript
javascript制作的网页侧边弹出框思路及实现代码
2014/05/21 Javascript
js数组中如何随机取出一个值
2014/06/13 Javascript
jquery 取子节点及当前节点属性值的方法
2014/08/24 Javascript
使用js dom和jquery分别实现简单增删改
2014/09/11 Javascript
jquery超简单实现手风琴效果的方法
2015/06/05 Javascript
js图片跟随鼠标移动代码
2015/11/26 Javascript
javascript断点调试心得分享
2016/04/23 Javascript
AngularJS使用指令增强标准表单元素功能
2016/07/01 Javascript
js简单实现图片延迟加载的方法
2016/07/19 Javascript
vue使用pdfjs显示PDF可复制的实现方法
2018/12/14 Javascript
vue路由导航守卫和请求拦截以及基于node的token认证的方法
2019/04/07 Javascript
vue实现路由监听和参数监听
2019/10/29 Javascript
vue基础知识--axios合并请求和slot
2020/06/04 Javascript
[01:00] DOTA2英雄背景故事第五期之重力引力法则谜团
2020/07/16 DOTA
Python中垃圾回收和del语句详解
2018/11/15 Python
解决Pandas的DataFrame输出截断和省略的问题
2019/02/08 Python
python实现对图片进行旋转,放缩,裁剪的功能
2019/08/07 Python
关于pytorch多GPU训练实例与性能对比分析
2019/08/19 Python
python中下标和切片的使用方法解析
2019/08/27 Python
python opencv调用笔记本摄像头
2019/08/28 Python
python 使用opencv 把视频分割成图片示例
2019/12/12 Python
python实现猜数游戏
2020/03/27 Python
VSCode基础使用与VSCode调试python程序入门的图文教程
2020/03/30 Python
python实现在线翻译
2020/06/18 Python
Python如何给你的程序做性能测试
2020/07/29 Python
三年级小学生评语
2014/04/22 职场文书
孝老爱亲模范事迹材料
2014/05/25 职场文书
2014年学生会个人工作总结
2014/11/07 职场文书
幼儿园欢迎词范文
2015/01/26 职场文书
投诉书范文
2015/07/02 职场文书