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 相关文章推荐
JQERY limittext 插件0.2版(长内容限制显示)
Aug 27 Javascript
Json2Template.js 基于jquery的插件 绑定JavaScript对象到Html模板中
Oct 29 Javascript
移动节点的jquery代码
Jan 13 Javascript
Angular ng-repeat 对象和数组遍历实例
Sep 14 Javascript
JavaScript Ajax实现异步通信
Dec 14 Javascript
JS百度地图搜索悬浮窗功能
Jan 12 Javascript
基于Bootstrap table组件实现多层表头的实例代码
Sep 07 Javascript
原生JS实现的简单轮播图功能【适合新手】
Aug 17 Javascript
实例讲解JS中pop使用方法
Jan 27 Javascript
微信小程序顶部导航栏滑动tab效果
Jan 28 Javascript
layer弹出层扩展主题的方法
Sep 11 Javascript
vue递归获取父元素的元素实例
Aug 07 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编写大型网站问题集
2007/03/06 PHP
PHP学习笔记之二 php入门知识
2011/01/12 PHP
PHP优于Node.js的五大理由分享
2012/09/15 PHP
php自动加载autoload机制示例分享
2014/02/20 PHP
mysql_connect localhost和127.0.0.1的区别(网络层阐述)
2015/03/26 PHP
PHP使用GD库制作验证码的方法(点击验证码或看不清会刷新验证码)
2017/08/15 PHP
php实现简单的权限管理的示例代码
2017/08/25 PHP
php 使用expat方式解析xml文件操作示例
2019/11/26 PHP
[JS]点出统计器
2020/10/11 Javascript
兼容IE与firefox火狐的回车事件(js与jquery)
2010/10/20 Javascript
js获取系统的根路径实现介绍
2013/09/08 Javascript
将input file的选择的文件清空的两种解决方案
2013/10/21 Javascript
理解javascript回调函数
2014/12/28 Javascript
Javascript中For In语句用法实例
2015/05/14 Javascript
原生JS实现图片轮播效果
2016/12/26 Javascript
基于bootstrap按钮式下拉菜单组件的搜索建议插件
2017/03/25 Javascript
Js利用Canvas实现图片压缩功能
2017/09/13 Javascript
使用JavaScript实现在页面中显示距离2017年中秋节的天数
2017/09/26 Javascript
JS实现同一DOM元素上onClick事件与onDblClick事件并存的解决方法
2018/06/07 Javascript
JavaScript cookie原理及使用实例
2020/05/08 Javascript
JavaScript随机数的组合问题案例分析
2020/05/16 Javascript
JavaScript中的函数式编程详解
2020/08/22 Javascript
[48:44]2014 DOTA2国际邀请赛中国区预选赛5.21 TongFu VS HGT
2014/05/22 DOTA
python smtplib模块发送SSL/TLS安全邮件实例
2015/04/08 Python
Python multiprocess pool模块报错pickling error问题解决方法分析
2019/03/20 Python
Python如何爬取微信公众号文章和评论(基于 Fiddler 抓包分析)
2019/06/28 Python
Python中的self用法详解
2019/08/06 Python
python中的global关键字的使用方法
2019/08/20 Python
Python socket连接中的粘包、精确传输问题实例分析
2020/03/24 Python
Python Matplotlib绘图基础知识代码解析
2020/08/31 Python
css3实现一款模仿iphone样式的注册表单
2013/03/20 HTML / CSS
家得宝墨西哥官网:The Home Depot墨西哥
2019/11/18 全球购物
30年同学聚会邀请函
2014/01/25 职场文书
模具数控专业自荐信
2014/01/27 职场文书
教你用Python爬取英雄联盟皮肤原画
2021/06/13 Python
golang 语言中错误处理机制
2021/08/30 Golang