zires 博客

[QA] - this in javascript

May 04, 2018

Q: 为什么需要this关键字?

this在英语中是代词,一般是用来替代之前提到的主语,可以使整个句子简洁不啰嗦。

其实this还有一层抽象的意义,不管之前的主语是谁,都可以抽象成this来代替。

所以,在javascript中,this可以在不同的上下文对象中重复使用函数,而不需要针对每个对象编写不同版本的函数。

Q: 函数内的this指向函数本身吗?

除非使用绑定规则指定函数本身,否则一般不指向函数本身。

function foo() {
  this.count++;
}

foo.count = 0;

foo()

console.log(foo.count); // 永远是0,函数中的this其实是指向了全局对象。

Q: this是由词法作用域决定的吗?

不是的。this是在运行时动态进行绑定的,而不是在编写时绑定,它的上下文取决于函数调用时的各种条件。

this的绑定和函数声明的位置没有关系,只取决于函数是如何调用(调用位置)的。

Q: this的绑定规则?

  • 默认绑定

当函数独立调用的时候,this指向全局对象。

var a = 'outer'

function foo() {
  var a = 'inner'
  console.log(this.a)
}

foo(); // outer
  • 隐式绑定

当函数非独立调用,而是具有上下文的时候,会隐式的把函数调用中的this绑定到这个上下文对象。

var obj = {
  a: 'objAttribute'
  foo: foo
}

obj.foo(); // objAttribute

这里需要特别小心,函数调用中的this只和函数调用的方式有关,不会过多的考虑词法作用域,看下面这个例子:

var aFooRef = obj.foo

// 只要函数调用的时候没有上下文,便是默认绑定规则
aFooRef(); // outer

以上情况特别容易出现在传递参数的时候:

function doFoo(fn) {
  fn()
}

doFoo(obj.foo); // outer
  • 显式绑定

我们可以强制把函数中的this,绑定到指定的某个对象的上下文中,使用的方法是call或者apply

foo.call({
  a: 'call me'
}); // call me

foo.apply({
  a: 'apply me'
}); // apply me

callapply都是立即执行函数,如果只是绑定this而不执行的话,可以使用bind

var bindFoo = foo.bind({
  a: 'bind me'
})

bindFoo(); // bind me
  • new绑定

当我们使用new来调用函数的时候,创建的对象会绑定到函数调用的this。

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

var bar = new foo('new bind')

bar.a; // new bind
  • 箭头函数

ES6中提供了箭头函数() => (),箭头函数使用的是词法作用域,this的作用域等于外层(函数或者全局)作用域。

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

var obj1 = {
  a: 1
}

var obj2 = {
  a: 2
}

var bar = foo.call(obj1)

bar(3); // 1

bar.call(obj2); // 1

Q: 显式绑定中call与apply的区别?

callapply的用法基本相同,唯一的区别在第二个参数上,apply接受一个参数数组,call接受一个参数列表。

Q: new绑定与显式绑定哪个优先级高?

new绑定的优先级更高。

Q: 是否可以将this显式绑定到null或者undefined上?

语法上没有错误,但是绑定无效,会使用默认绑定。

foo.call(null); // outer

Zires

Hi~,i am zires, an IT engineer, live in Shanghai. You can get in touch with me via Github stackoverflow