关于this
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候又称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。
默认绑定
独立函数调用,无法应用其他规则时的默认规则。
function foo() {
var a = 'one';
console.log(this.a)
}
var a = 'day';
foo(); // day
严格模式下上面的代码会绑定到undefined:
function foo() {
'use strict'
var a = 'one';
console.log(this.a)
}
var a = 'day';
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
上面的foo()
是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。
隐式绑定
看是否有上下文对象。
function foo() {
console.log(this.a);
}
var obj = {
a: 'oneday',
foo: foo
}
obj.foo(); // oneday
此时foo()
是作为引用属性添加到obj中的。会使用obj上下文来引用函数。对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function foo() {
console.log(this.a);
}
var obj2 = {
a: 'one',
foo: foo
}
var obj1 = {
a: 'day',
obj2: obj2
}
obj1.obj2.foo(); // one
但是有的时候被隐式绑定的函数会丢失绑定对象,然后就会应用默认绑定规则。
function foo() {
console.log(this.a);
}
var obj = {
a: 'one',
foo: foo
}
var bar = obj.foo;
var a = 'day';
bar(); // day
显式绑定
使用call(...)
和apply(...)
function foo() {
console.log(this.a);
}
var obj = {
a: 'one'
}
foo.call(obj); // one
然后this强制绑定在了obj上面。
硬绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 'one'
}
var bar = function() {
foo.call(obj);
}
bar(); // one
bar.call(window); // one
就不能改变foo的this指向了。ES5提供了内置方法Functon.prototype.bind方法,会返回一个硬绑定的新函数,然后就不能改变this指向了。
function foo() {
console.log(this.a);
}
var obj = {
a: 'one'
}
var bar = foo.bind(obj)
bar() // one
bar.call(window) // one
new绑定
在JavaScript中,构造函数只是一些使用new操作符时被调用的函数。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
- 创建一个新对象
- 新对象会连接原型链
- 新对象会绑定this
- 返回该对象
f = new Object()
f.__proto__ = F.prototype
F.call(f)
return f
四个绑定的优先级:
new绑定 > call、apply显式绑定 > 隐式绑定 > 默认绑定
箭头函数
this绑定了外层(函数或全局)作用域
function foo() {
setTimeout(() => {
console.log(this.a) // 这里的this的是foo的
}, 100)
}
var obj = {
a: 'oneday'
}
foo.call(obj) // oneday
还有一个粗暴的方法
function foo() {
var a = 'one';
console.log(this.a)
}
var a = 'day';
foo(); // day
// 等价于
foo.call(window)
// 严格模式相当于
foo.call(undefined)
function foo() {
console.log(this.a);
}
var obj = {
a: 'oneday',
foo: foo
}
obj.foo(); // oneday
// 等价于
foo.call(obj)
function foo() {
console.log(this.a);
}
var obj2 = {
a: 'one',
foo: foo
}
var obj1 = {
a: 'day',
obj2: obj2
}
obj1.obj2.foo(); // one
// 等价于
foo.call(obj1.obj2)
function foo() {
console.log(this.a);
}
var obj = {
a: 'one',
foo: foo
}
var bar = obj.foo;
var a = 'day';
bar(); // day
// 等价于
bar.call(window)
.
前面是什么,就是call什么...