JavaScript---简述this的几种绑定形式

绑定规则

下面几种绑定方式, 除了es6的箭头函数, 都属于动态作用域, es6箭头函数里面的this采用的静态作用域

默认绑定

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

其实很简单, 这里只要看foo这个函数是由谁来调用的即可, 明显是window, 所以输出的是2, 如果使用严格模式(strict mode), 那么全局对象将无法使用默认绑定, 因此this会绑定到undefined, 参考下面代码

1
2
3
4
5
6
7
function foo() {
"use strict"
console.log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined

(多提一嘴TypeError和ReferenceError的区别, ReferenceError同作用域判别失败相关, TypeError则代表作用域判别成功了, 但是对结果操作是非法或不合理的)

隐式绑定

和上一种默认绑定差不多, 主要看是谁调用的函数, 下面看一下例子

1
2
3
4
5
6
7
8
9
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
obj.foo(); // 2

因为调用foo时, this被绑定到obj, 因此this.a和obj.a是一样的, 下面看一个长一点的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo,
}
var obj1 = {
a: 2,
obj2: obj2,
}
obj1.obj2.foo(); // 42 因为是obj2调用的函数

隐式丢失

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo,
}
var bar = obj1.foo;
var a = 111;
bar(); // 111 因为是window调用的bar, bar拿到的只是foo函数的一个引用而已

显示绑定

call和apply以及es5的bind;

关乎call和apply的详细用法可以参考我的这篇文章
https://blog.csdn.net/c_kite/article/details/77860103

下面是一个小例子

1
2
3
4
5
6
7
8
9
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
}
foo.call(obj) // 2

new绑定

使用new来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作

  1. 创建(或者构造)一个全新的对象
  2. 这个新对象会被执行 [[原型]] 连接, 也就是这个对象的 proto 指向构造函数的prototype
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回其他对象, 那么new表达式中的函数调用会自动返回这个新对象
1
2
3
4
5
6
7
8
// 例子
function foo(a) {
this.a = a
}
var bar = new foo(2);
console.log(bar.a) // 2

优先级

new > 显示绑定 > 隐式绑定

下面是一个new和显示绑定比较的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo(a) {
this.a = a
}
var obj = {}
var bar = foo.bind(obj);
bar(2)
console.log(obj.a) // 2
var baz = new bar(3)
console.log(obj.a) // 2 重点
console.log(baz.a) // 3 重点

绑定例外

如果把null或者undefined作为this的绑定对象传入call, apply或者bind, 这些值在调用时会被忽略, 实际应用的事默认绑定规则

1
2
3
4
5
6
7
// 例子
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); // 2

两种绑定方式

硬绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo(a) {
console.log(this.a, a);
return this.a + a
}
var obj = {
a: 3
}
var obj2 = {
a: 2
}
var bar = function () {
return foo.apply(obj, arguments)
}
var b = bar.call(obj2, 2); // 3 2 可以看到obj2的值没有用到
console.log(b); // 5

软绑定

硬绑定这种方式可以把this强制绑定到指定的对象, 问题在于, 硬绑定会大大降低函数的灵活性, 使用硬绑定之后就无法使用隐式绑定或者显示绑定来修改this, 下面是一个软绑定的例子.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
if (!Function.prototype.softBind) {
Function.prototype.softBind = function (obj) {
var fn = this;
// 捕获所有参数
var curried = [].slice.call(arguments, 1);
var bound = function () {
return fn.apply(
(!this || this === (window || global)) ? obj : this,
curried.concat.apply(curried, arguments)
)
}
bound.prototype = Object.create(fn.prototype);
// 等同于bound.prototype.__proto__ = foo.prototype
return bound
}
}
function foo() {
console.log(this.name)
}
var obj = {name: 'obj'}
var obj1 = {
name: 'obj1'
}
var obj2 = {
name: 'obj2'
}
var fooOBJ = foo.softBind(obj)
fooOBJ(); // obj
obj1.foo = foo.softBind(obj);
obj1.foo(); // obj1
fooOBJ.call(obj2); // obj2
setTimeout(obj1.foo, 10); // obj

this词法作用域es6

就是es6的箭头函数, 看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo() {
return (a) => {
console.log(this.a)
}
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3

foo()内部创建的箭头函数会捕获调用时foo的this, 由于foo()的this绑定到obj1, bar(引用箭头函数)的this也会绑定到obj1, 箭头函数的绑定无法被修改, (new也不行)

越来越多的平台(微信公众平台,新浪微博,简书,百度打赏等)支持打赏功能,付费阅读时代越来越近,特此增加了打赏功能,支持微信打赏和支付宝打赏。坚持原创技术分享,您的支持将鼓励我继续创作!