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 相关文章推荐
JavaScript 基础问答三
Dec 03 Javascript
js 鼠标点击事件及其它捕获
Jun 04 Javascript
Mootools 1.2 手风琴(Accordion)教程
Sep 15 Javascript
Web前端设计模式  制作漂亮的弹出层
Oct 29 Javascript
Javascript常用字符串判断函数代码分享
Dec 08 Javascript
javascript实现当前页导航激活的方法
Feb 27 Javascript
jQuery检测滚动条是否到达底部
Dec 15 Javascript
javascript中JSON.parse()与eval()解析json的区别
May 19 Javascript
AngularJS入门教程之迭代器过滤详解
Aug 18 Javascript
Bootstrap CSS组件之输入框组
Dec 17 Javascript
Vue-router路由判断页面未登录跳转到登录页面的实例
Oct 26 Javascript
在vue中使用jsx语法的使用方法
Sep 30 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下使用以下代码连接并测试
2008/04/09 PHP
PHP图片验证码制作实现分享(全)
2012/05/10 PHP
基于PHP异步执行的常用方式详解
2013/06/03 PHP
使用Git实现Laravel项目的自动化部署
2019/11/24 PHP
js 提交和设置表单的值
2008/12/19 Javascript
ASP.NET jQuery 实例10 动态修改hyperlink的URL值
2012/02/03 Javascript
页面定时刷新(1秒刷新一次)
2013/11/22 Javascript
Windows系统中安装nodejs图文教程
2015/02/28 NodeJs
JS基于cookie实现来宾统计记录访客信息的方法
2015/08/04 Javascript
jQuery zclip插件实现跨浏览器复制功能
2015/11/02 Javascript
JS控制FileUpload的上传文件类型实例代码
2016/10/07 Javascript
js实现时间轴自动排列效果
2017/03/09 Javascript
javascript编程开发中取色器及封装$函数用法示例
2017/08/09 Javascript
vuejs 制作背景淡入淡出切换动画的实例
2018/09/01 Javascript
Angular设置别名alias的方法
2018/11/08 Javascript
vuex页面刷新后数据丢失的方法
2019/01/17 Javascript
jQuery 查找元素操作实例小结
2019/10/02 jQuery
深入理解redux之compose的具体应用
2020/01/12 Javascript
详细整理python 字符串(str)与列表(list)以及数组(array)之间的转换方法
2019/08/30 Python
Pandas DataFrame中的tuple元素遍历的实现
2019/10/23 Python
python Event事件、进程池与线程池、协程解析
2019/10/25 Python
使用pytorch和torchtext进行文本分类的实例
2020/01/08 Python
HTML5中的Web Notification桌面通知功能的实现方法
2019/07/29 HTML / CSS
html2 canvas生成清晰的图片实现打印功能
2019/09/23 HTML / CSS
Top Villas美国:豪华别墅出租和度假屋
2018/07/10 全球购物
世界上最大的字体市场:MyFonts
2020/01/10 全球购物
如何利用find命令查找文件
2016/11/18 面试题
易程科技软件测试笔试
2013/03/24 面试题
行政人事岗位职责
2014/03/17 职场文书
电子信息工程专业求职信
2014/06/28 职场文书
2014年行政后勤工作总结
2014/12/06 职场文书
原料仓管员岗位职责
2015/04/01 职场文书
证券公司客户经理岗位职责
2015/04/09 职场文书
在校学生证明格式
2015/06/24 职场文书
2019最新版股权转让及委托持股协议书范本
2019/08/07 职场文书
年会邀请函的格式及范文五篇
2019/11/02 职场文书