this的指向
在绝大多数情况下,函数的调用方式决定了this
的值(运行时绑定),this永远指向最后调用它的那个对象。
- 全局的this非严格模式指向window对象,严格模式指向undefined
- 对象的属性方法中的this指向对象本身
- apply、call、bind可以变更this指向为第一个传参
- 箭头函数中的this指向它的父级作用域,它本身不存在this
改变this的指向
- 使用ES6的箭头函数
- 在函数内部使用_this=this
- 使用
apply
、call
、bind
- new实例化一个对象
箭头函数
箭头函数的this始终指向函数定义时的this,而非执行时。
箭头函数中没有this绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则this绑定的最近一层非箭头函数的this,否则,this为undefined。
var name = "windowsName";
var a = {
name: "gary",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(() => {
this.func1()
}, 100);
}
};
a.func2() // gary
在函数内部使用_this=this
如果不使用ES6,这种方式应该是最简单的不会出错的方式了,我们是先将调用这个函数的对象保存在变量_this
中,然后在函数中都使用这个_this
,这样_this
就不会改变了。
var name = "windowsName";
var a = {
name: "gary",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout(function () {
_this.func1()
}, 100);
}
};
a.func2() // gary
在这个例子中,在func2中,首先设置var _this=this
,这里的this
是调用func2
的对象
a,为了防止在func2
中的setTimeout被window调用而导致的在setTimeout中的this为window。我们将this(指向变量a)
赋值给一个变量_this
,这样,在func2中我们使用_this就是指向对象a了。
使用apply、call、bind
使用apply
var name = "windowsName";
var a = {
name: "gary1",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.apply(a), 100);
}
};
a.func2() // gary1
使用call
var name = "windowsName";
var a = {
name: "gary2",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.call(a), 100);
}
};
a.func2() // gary2
使用bind
var name = "windowsName";
var a = {
name: "gary3",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
}.bind(a)(), 100);
}
};
a.func2() // gary3
apply、call、bind区别
apply
:apply()方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
语法:
fun.apply(thisArg, [argsArray])
- thisArg:在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null或undefined时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。从ECMAScript 5开始可以使用类数组对象。
apply和call的区别
apply和call基本类似,区别只是传入的参数不同。
call的词法为:
fun.call(thisArg[, arg1[, arg2[, ...]]])
apply和call的区别是apply接收的是一个包含多个参数的数组,而call方法接受的是若干个参数列表。
apply:
var a = {
name: "gary",
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn
b.apply(a, [1, 2]) // 3
call:
var a = {
name: 'gary',
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn
b.call(a, 1, 2) // 3
bind和apply、call区别
先用刚才的例子使用bind试一下:
var a = {
name: 'gary',
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn
b.bind(a, 1, 2) // 3
我们发现并没有输出,这是为什么呢,我们来看一下MDN上的文档说明:
bind()方法创建一个新的函数,当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
所以我们可以看出,bind是创建一个新的函数,我们必须要手动调用:
var a = {
name: 'gary',
fn: function (a, b) {
console.log(a + b)
}
}
var b = a.fn
b.bind(a, 1, 2)() // 3
JS中的函数调用
例6:
var name = "windowsName";
function fn() {
var name = 'gray';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
例7:
var name = "windowsName";
var a = {
name : "gray",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
函数调用的方法一共有4种:
- 作为一个函数调用
- 函数作为方法调用
- 使用构造函数调用函数
- 作为函数方法调用函数(call、apply)