JS知识点 - this指向

62 阅读3分钟

参考《你不知道的JavaScript》和《JavaScript》忍者秘籍中有关this指向问题,第一次写记录下学习的内容,主要是为了方便下次复习和提供给其他想了解相关知识的同学。有不太好的地方希望大家多包容

this指向谁??

既不指向函数本身也不指向作用域, this是函数执行上下文,在运行时绑定,根据函数的调用方式决定。

绑定规则

1、默认规则 - 函数直接调用

  • 严格模式下this指向undefined
function foo() {
  "use strict";
  console.log(this); //undefined
}

var a = 2;
foo();
  • 非严格模式浏览器环境下this指向window image.png

2、隐式规则 - 函数作为对象的方法调用 此时this指向对象

image.png

注意:回调中this丢失的问题

3、显示规则 - 通过call、apply、bind显示指定this

三者的区别:call 和 apply传递参数形式不同

  • 函数名.call(显示指定this绑定的对象,arg1,arg2...),call传入参数列表

image.png

  • 函数名.apply(显示指定this绑定的对象,[arg1,arg2...]) apply传入参数数组

  • bind会返回新的函数,它会把指定的参数设置为this的上下文并调用原始函数

    bind可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层的函数(这种技术称为“部分应用”,是“柯里化”的一种)

function foo(p1, p2) {
  this.val = p1 + p2;
}

var bar = foo.bind(null, "p1"); 
var baz = new bar("p2");
console.log(baz.val); // p1p2

foo.bind(null, "p1")返回新的函数bar, 通过new bar()传入剩下的参数。

4、new绑定 - this指向创建的对象

function foo(a) {
  this.a = a;
}

var bar = new foo(3); //新创建的bar绑定到foo里的this

console.log(bar.a); //3

执行new 构造函数过程:

  1. 创建一个新的空对象
  2. 空对象的原型指向构造函数的原型
  3. 空对象作为构造函数的上下文(改变this指向)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

箭头函数的this指向

不满足以上规则,箭头函数根据外部作用域来决定this

function foo() {
  return (a) => {
    console.log(this.a); //2
  };
}

var obj1 = {
  a: 2,
};

var obj2 = {
  a: 3,
};

var bar = foo.call(obj1); //foo的this指向obj1,箭头函数里的this也指向obj1
console.log(bar);
bar.call(obj2);

var bar = foo.call(obj1); foo.call(obj1)将obj1显示指定foo内部的this,箭头函数的this也指向obj1

绑定规则的优先级

new绑定 > call、bind、apply显示绑定 > 隐式绑定 > 默认绑定

  • 如何证明new的优先级比bind高
function foo(something) {
  this.a = something;
}
var obj1 = {};
var bar = foo.bind(obj1); //obj1传递到foo内作为this
bar(2);
console.log(obj1.a); //2

var baz = new bar(3);
console.log(obj1.a); //2
console.log(baz.a); //3

var baz = new bar(3)将baz传递给bar内部作为函数上下文this, bar是foo.bind(obj1)返回的新函数,此时显式指定obj1为foo的上下文。先后使用了new和bind改变this的指向,结果证明new的优先级高于bind。

  • 如何证明显示绑定高于隐式绑定, 使用call显示调用 vs 对象方法名调用
function foo() {
  console.log(this.a);
}

var obj1 = {
  a: 2,
  foo: foo,
};

var obj2 = {
  a: 3,
  foo: foo,
};

//隐式规则
obj1.foo(); //2
obj2.foo(); //3

obj1.foo.call(obj2); //3,显示指定obj2作为this
obj2.foo.call(obj1); //2,显示指定obj1作为this
  • 如何证明new高于隐式绑定
function foo(something) {
  this.a = something;
}

var obj1 = {
  foo: foo,
};

var obj2 = {};

obj1.foo(2);
console.log(obj1.a); //2  隐式绑定

var bar = new obj1.foo(4); //new绑定
console.log(obj1.a); //2
console.log(bar.a); //4

var bar = new obj1.foo(4); 先通过new改变this指向bar,再使用隐式绑定obj1.foo()。结果显示obj1.a的值还是2没有改变,而bar.a的值是4,说明new的优先级高于隐式绑定。