this全面解析

《你不知道的JavaScript上卷》 笔记(一)


调用位置

关于this绑定对象,主要取决于函数的调用位置

绑定规则

默认绑定

  • 独立函数调用,找函数的调用位置
  • 严格模式下,绑定到undefined,否则绑定到全局对象

    隐式绑定

  • 调用位置是否有上下文对象,是否被某个对象拥有
  • 隐式绑定的函数会丢失绑定对象
1
2
3
4
5
6
7
8
9
10
11
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
obj.foo();//隐式绑定 2
var bar = obj.foo;//隐式绑定的函数会丢失绑定对象,这里 bar 是 obj.foo 的一个引用,但实际上它已经丢失了绑定对象,应用默认绑定
var a = "oops,global";
bar();//"oops,global"

硬绑定

  • 使用 call() apply() 方法显式强制改变绑定对象
  • ES6提供的内置方法 Function.prototype.bind
  • API调用的上下文

    new绑定

  • 使用new来调用函数,会自动执行以下操作:
    1.创建一个全新的对象
    2.新对象会被执行原型连接
    3.新对象会被绑定到函数调用的this
    4.如果函数没有返回其他对象,那么new表达式的函数调用会自动返回新对象
    

    绑定优先级

    new绑定 > 显式绑定( call , apply) > 隐式绑定(上下文对象中调用)>默认绑定

    绑定例外情况

    忽略this绑定

  • 使用 null , undefind 作为 this 绑定对象传入 call , apply ,bind ,this绑定会被忽略,实际应用默认绑定规则
1
2
3
4
5
fuction foo(){
console.log(this.a);
}
var a =2;
foo.call(null);//2
  • 传入一个特殊的对象(空对象),忽略 this 绑定更安全的做法
1
2
3
4
5
6
7
8
9
10
function foo(){
console.log("a:"+ a + "b:" + b);
}
//创建一个DMZ空对象
var v = Object.create(null);
//把数组展开成参数
foo.apply( v , [2,3]); // a:2 b:3
//使用 bind() 进行柯里化
var bar = foo.bind( v ,2);
bar(3); // a:2 b:3

间接引用

如果创建一个函数的间接引用,那么调用这个函数会应用默认绑定规则

1
2
3
4
5
6
7
8
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 = o.foo 的返回值是目标函数的引用,因此调用位置是 foo( ) 而不是 p.foo 或者 o.foo ,所以这里会应用默认绑定

软绑定

  • 即修改默认绑定的对象
  • softBind()

    this 在 ES6 的箭头函数中的用法

  • 箭头函数不使用 this 的四种绑定规则,而是根据外层(函数或全局)作用域来决定 this
  • 箭头函数会继承外层函数调用的 this 绑定,就像 self = this 机制一样
1
2
3
4
5
6
7
8
9
10
11
12
13
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() 内部创建的剪头函数已经绑定了obj1, 在 bar 调用时也会绑定到 obj1 ,箭头函数的绑定不会被修改