上一节我们讲了闭包的原理及形成,希望大家可以多去理解一下。本节我们开始讲js中this的指向问题
一、结论与规则与优先级排序
结论
- 函数在调用时,JavaScript会默认给this绑定一个值。
- this的绑定和定义的位置(编写的位置)没有关系。
- this的绑定和调用方式以及调用的位置有关系。
- this是在运行时被绑定的。
规则
- 默认绑定。
- 隐式绑定。
- 显示绑定。
- new绑定。
优先级排序
- .隐式绑定高于默认绑定(默认绑定最低)
- 显示绑定优先级高于隐式绑定
- new绑定优先级高于隐式绑定
- new绑定优先级高于bind(new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高,new绑定可以和bind一起使用,new绑定优先级更高) 接下来我们开始对这4种规则一 一进行讲解
我们先来看一种特殊情况(在全局作用域下的this)
// js文件
console.log(this);
一般来说我们的this都是在函数中使用的,但是我们直接在全局打印呢? 两种情况:1. 浏览器中运行 this指向window 2. node环境下运行 this指向{}
一、默认绑定-》this指向window
什么情况下是默认绑定呢?独立函数调用时。 独立函数:函数没有被绑定到某个对象上进行调用。如下
function foo() {
console.log(this); // window
}
foo()
function foo() {
console.log(this); // window
foo2()
}
function foo2() {
console.log(this); // window
foo3()
}
function foo3() {
console.log(this); // window
}
foo()
function foo(fn) {
fn()
}
var bar = {
bar: function() {
console.log(this); // window
}
}
foo(bar.bar)
上面的这三种都是默认绑定所以都是window。前两种大家应都能理解。 我们来看看第三种,可能大家会疑惑 为什么第三个是window而不是bar,如果你有这种疑惑,那么你在仔细的看看我们最前面的结论this的绑定和定义的位置(编写的位置)没有关系,和其调用方式以及调用的位置有关系 ,现在大家明白了吗 fn在调用时,可没有绑定到某个对象上哟,他是独立的在调用哟。 那他什么时候this指向bar呢?看下面代码
function foo(fn) {
fn()
}
var bar = {
bar: function() {
console.log(this); // bar
}
}
// foo(bar.bar)
bar.bar()
看到了吗?我们只改了最后一行代码,他的指向就变成了bar。这就是我接下来要说的隐式绑定
二、隐式绑定-》this指向调用的那个对象
通过某个对象进行调用的就是隐式绑定,换句话说就是他的调用是由一个对象发起的
var bar = {
bar: function() {
console.log(this); // bar
}
}
bar.bar()
function foo(fn) {
console.log(this); // bar
}
var bar = {
name: 'bar',
foo: foo
}
var bar2 = {
name: 'bar2',
bar: bar
}
bar2.bar.foo()
三、显示绑定(call、apply、bind)
明确的绑定了this指向的对象的就叫显示绑定
- bind返回一个函数,可以延迟调用
- apply、call直接调用(但他们两接收的参数不一样,apply只能接收两个参数,第一个参数是this,第二个参数是一个数组,在调用的时候需要把其他参数都放在这个数组里。call可以接收多个参数,第一个参数是this,在调用的时候需要把其他参数一个一个按顺序传入)
function foo() {
console.log(this);
}
var obj = {
name: 'obj'
}
foo() // window
foo.call(obj) // obj
foo.apply(obj) // obj
var newFoo = foo.bind(obj)
newFoo() // obj
这里对前两个(foo.call(obj)和foo.apply(obj))指向的是obj,大家应该都可以理解,但是对于newFoo()这个大家可能会有疑问: 大家可能会觉得 newFoo()这个是个独立函数调用啊?上面不是说独立函数调用指向window吗? 是的,这里确实是独立函数调用,但是他在调用之前显示的绑定了一个obj,也就是说默认调用与显示调用冲突了,这里打印的obj,所以我们可以得出结论显示绑定优先级高于默认绑定
四、new绑定 ->指向new的那个对象
function Person(name) {
this.name = name
console.log(this); // Person
}
var p = new Person('xt')
console.log(p.name); // xt
可以看到this指向的就是 Person。 接下来我们来看看new与隐式绑定谁的优先级高
var obj = {
foo: function() {
console.log(this);
}
}
obj.foo() // obj
new obj.foo() // foo
从上面的结果,我们可以得出结论 new绑定优先级高于隐式绑定