js中this的绑定规则及优先级
一. this绑定规则
函数调用位置决定了this的绑定对象,必须找到正确的调用位置判断需要应用下面四条规则中的哪一条。
1.1 默认绑定
看下面代码:
function foo() {
console.log(this.a);
}
var a = 1;
foo(); // 2
调用foo的时候,this应用了默认绑定,this指向了全局对象,但是在严格模式下,那么全局对象将无法进行默认绑定,因此this会绑定到undefined
function foo() {
'use strict';
console.log(this.a);
}
var a = 1;
foo(); // TypeRrror: this is undefined
严格模式下与 foo() 的调用位置无关:
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo(); //
})();
1.2 隐式绑定
另一条需要考虑的规则是调用位置是否有上下文对象
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); //
但是无论是直接在 obj 中定义还是先定义再添加为引用属性, 这个函数严格来说都不属于 obj 对象,然而, 调用位置会使用 obj 上下文来引用函数, 因此你可以说函数被调用时 obj 对象“ 拥有” 或者“ 包含” 它
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。 举例来说:
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); //
1.2.1 隐式丢失
一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象, 也就是说它会应用默认绑定, 从而把 this 绑定到全局对象或者 undefined 上, 取决于是否是严格模式,
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
虽然 bar 是 obj.foo 的一个引用, 但是实际上, 它引用的是 foo 函数本身, 因此此时的 bar() 其实是一个不带任何修饰的函数调用, 因此应用了默认绑定。在js内置函数中如setTimeout也是如此:
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
setTimeout(obj.foo, 100); // "oops, global"
和下面伪代码类似:
function setTimeout(fn, delay) {
// 等待 delay 毫秒
fn(); // <-- 调用位置!
}
1.3.显示绑定
call(...),apply(...)可以指定this的绑定对象(前者接收多个参数如call(this, param1, param2, param3...),后者接受一个或两个参数apply(this, [...]))
function foo() {
console.log(this.a);
}
var obj = {
a: 2
};
foo.call(obj); //
通过 foo.call(..), 我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。 如果你传入了一个原始值( 字符串类型、 布尔类型或者数字类型) 来当作 this 的绑定对 象, 这个原始值会被转换成它的对象形式( 也就是 new String(..)、 new Boolean(..) 或者 new Number(..))。 这通常被称为“ 装箱”。
1.3.1 硬绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 2
};
var bar = function() {
foo.call(obj);
};
bar(); //
setTimeout(bar, 100); //
// 硬绑定的 bar 不可能再修改它的 this
bar.call(window); //
我们创建了函数 bar(), 并在它的内部手动调用 了 foo.call(obj), 因此强制把 foo 的 this 绑定到了 obj。 无论之后如何调用函数 bar, 它 总会手动在 obj 上调用 foo。 这种绑定是一种显式的强制绑定, 因此我们称之为硬绑定。创建一个 i可以重复使用的辅助函数:
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
//简单的辅助绑定函数
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
};
}
var obj = {
a: 2
};
var bar = bind(foo, obj);
var b = bar(3); // 2 3
console.log(b); //
由于硬绑定是一种非常常用的模式, 所以在 ES5 中提供了内置的方法 Function.prototype.bind, bind(..) 会返回一个硬编码的新函数, 它会把参数设置为 this 的上下文并调用原始函数
1.3.2 API调用的“上下文”
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: "awesome"
};
// 调用 foo(..) 时把 this 绑定到 obj
[1, 2, 3].forEach(foo, obj);
// 1 awesome 2 awesome 3 awesome
这些函数实际上就是通过 call(..) 或者 apply(..) 实现了显式绑定, 这样你可以少些一些代码。
1.4. new绑定
在 JavaScript 中, 构造函数只是一些 使用 new 操作符时被调用的函数。 它们并不会属于某个类, 也不会实例化一个类。 实际上, 它们甚至都不能说是一种特殊的函数类型, 它们只是被 new 操作符调用的普通函数而已。使用 new 来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作:
1. 创建( 或者说构造) 一个全新的对象
2. 这个新对象会被执行 [[ 原型 ]] 连接
3. 这个新对象会绑定到函数调用的 this
4. 如果函数没有返回其他对象, 那么 new 表达式中的函数调用会自动返回这个新对象
二. 优先级
毫无疑问, 默认绑定的优先级是四条规则中最低的,来看看隐式绑定和显示绑定
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); //
obj2.foo(); //
obj1.foo.call(obj2); //
obj2.foo.call(obj1); //
显式绑定优先级更高, new 绑定和隐式绑定的优先级谁高谁低:
function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo(2);
console.log(obj1.a); //
obj1.foo.call(obj2, 3);
console.log(obj2.a); //
var bar = new obj1.foo(4);
console.log(obj1.a); //
console.log(bar.a); //
可以看到 new 绑定比隐式绑定优先级高。 但是 new 绑定和显式绑定谁的优先级更高呢?
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); //
var baz = new bar(3);
console.log( obj1.a ); //
console.log( baz.a ); //
bar 被硬绑定到 obj1 上, 但是 new bar(3) 并没有像我们预计的那样把 obj1.a 修改为 3。 相反,new 修改了硬绑定( 到 obj1 的) 调用 bar(..) 中的 this。 因为使用了 new 绑定, 我们得到了一个名字为 baz 的新对象, 并且 baz.a 的值是 3。之所以要在 new 中使用硬绑定函数, 主要目的是预先设置函数的一些参数, 这样在使用 new 进行初始化时就可以只传入其余的参数。 bind(..) 的功能之一就是可以把除了第一个 参数( 第一个参数用于绑定 this) 之外的其他参数都传给下层的函数( 这种技术称为“ 部 分应用”, 是“ 柯里化” 的一种)。 举例来说:
function foo(p1, p2) {
this.val = p1 + p2;
}
//之所以使用 null 是因为在本例中我们并不关心硬绑定的 this 是什么
// 反正使用 new 时 this 会被修改
var bar = foo.bind(null, "p1");
var baz = new bar("p2");
baz.val; // p1p2
js中this的绑定规则及优先级的更多相关文章
- JavaScript中this的绑定规则
JavaScript中this的绑定规则 前言 我们知道浏览器运行环境下在全局作用域下的this是指向window的,但是开发中却很少在全局作用域下去使用this,通常都是在函数中进行使用,而函数使用 ...
- js中的前绑定和后绑定详解
这篇文章详细介绍了js中的前绑定和后绑定,有需要的朋友可以参考一下 其主要意思就是看我有没有用过前绑定,即Dom树中的某些元素在还没有创建出来时,就指定该类型的元素一出生就应该拥有的某些事件.在实际开 ...
- js中this的绑定
人们对于this的绑定常常有两个误解,一:指向函数本身,二:指向函数作用域.这两种想法都是错的,this并不指向函数本身,也不指向函数作用域. function foo(){ this.count++ ...
- js中同一个onclick绑定了两个js方法出现的问题
问题: js中如果同一个onclick绑定了两个js方法问题,即 <li onclick="f1(),f2()"></li> 两个方法f1,f2中都分别有一 ...
- js中的事件绑定的三种方式
1 直接在html标签中绑定 <button onclick = "show()"></button> 注意当你引用的js代码是包裹在window.onlo ...
- js中判定this的规则
判定this new绑定:新建对象; var bar = new foo(); 明确绑定(call.apply,bind):指定对象; var bar = foo.call(obj) 隐含绑定:环境对 ...
- 关于JS中变量提升的规则和原理的一点理解
关于变量提升,以前在一些教程和书籍上都听到过,平时开发中也知道有这个规律,但是今天突然在一个公开课中听到时,第一反应时一脸懵逼,然后一百度,瞬间觉得好熟悉啊,差点被这个概念给唬住了,不信我给你 ...
- 关于js中对事件绑定与普通事件的理解
普通事件指的是可以用来注册的事件: 事件绑定是指把事件注册到具体的元素之上. 通俗点说: 普通事件:给html元素添加一个特定的属性(如:onclick): 事件绑定:js代码中通过标记(id ta ...
- JS 中的事件绑定、事件监听、事件委托
事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有 ...
随机推荐
- oracle工具sqluldr2和sqlldr的使用
在 Oracle 数据库中,我们通常在不同数据库的表间记录进行复制或迁移时会用以下几种方法:1. 逐条insert -- 只适用少量数据更新 ALTER TABLE order_items DISAB ...
- 关闭防火墙,selinux,交互式设置IP的脚本
脚本内容: #!/bin/bash # ens=$(cat /proc/net/dev | awk '{if($2>0 && NR > 2) print substr($1 ...
- tomcat配置接口访问时间
这次刚好用到,亲测可用.参照:https://www.cnblogs.com/wuxun1997/p/9068398.html 项目中有些页面时延不稳定,需要看每次接口调用时延,怎么看,有两种方法:一 ...
- 精读《C++ primer》学习笔记(第四至六章)
第四章: 重要知识点: 4.1 基础 函数调用是一种特殊的运算符,它对运算对象的数量没有限制. 重载运算符时可以定义运算对象的类型,返回值类型,但运算对象的个数,运算符的优先级,结合律无法改变. 当一 ...
- Wpf开源收集
1,到底有哪些开源MVVM框架? 前面介绍了WPF的基本概念和一些相关知识,我们了解到开发WPF应用程序可以使用现成的框架和模式,最为合适的莫过于时下正热的MVVM模式,所以这里我们也列出针对MVVM ...
- CentOS 7.x 如何关闭 numa
CentOS7.x中发现 numactl --interleave=all 执行失败. CentOS7.x中可以通过下面的方式关闭 numa: 1. 编辑 /etc/default/grub 文件,如 ...
- webstorm的相关操作
1.webstorm修改tab键的缩进
- sql 存储过程和触发器
mysql----------------------------------------------------------------------------------------------- ...
- ocr_vote disk损坏恢复
1. 检查votedisk和 ocr备份[root@rh6rac1 bin]./ocrconfig -showbackuprh6rac1 2019/03/19 16:57:40 /or ...
- springboot连mysql报一个奇怪的错误
错误提示:Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection ...