绝大多数情况下,
函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。
全局环境
在全局执行环境中(在任何函数体外部)this 都指向全局对象。
console.log(this === window);//true
var a = 30;
console.log(window.a);//30
console.log(this.a);//30
函数(运行内)环境
直接调用
非严格模式下,this绑定全局对象。
function fn1(){
return this;
}
fn1();//window
console.log(fn1() === window);//true
内部函数
函数嵌套的内部函数的this指向的也是全局对象。
function fn(){
function fn1(){
console.log(this);
}
fn1();
}
fn();//window
setTimeout、setInterval
这两个方法执行的函数this也是指向全局对象。
作为构造函数调用
当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。
function Person(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('Jane')
p1.printName();//Jane
作为对象方法调用
当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。
var obj = {
name: 'Jane',
fn : function(){
console.log(this);
}
};
obj.fn();
var fn1 = obj.fn;//fn只是赋值不是执行
fn1(); // window 可看作是window.fn1()
DOM对象绑定事件
在事件处理程序中this代表事件源DOM对象
document.addEventListener('click',function(){
console.log(this);
});
// #document
方法
call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
语法:fun.call(thisArg, arg1, arg2, ...)
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
add.call(o, 5, 7); // 16 o是指定对象
add.apply(o, [10, 20]); // 34
练习
使用 call 方法调用父构造函数
function Person(name,age){
this.name = name;
this.age = age;
}
function Student(name,age){
Person.call(this,name,age);
}
var student = new Student('Jane',18);
console.log(student);
apply
方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
语法:func.apply(thisArg, [argsArray])
将arguments作为argsArray参数,它可以被用作被调用对象的所有未指定的参数。
function num(msg){
console.log(msg);
}
num(1);//1
num(1,2);//1
//
function num(){
console.log.apply(console,arguments);
}
num(1,2);//1,2
练习
用apply将数组添加到另一个数组
var a = [1,2,3];
var b = [3,4,5];
a.push.apply(a,b);
console.log(a);//[1, 2, 3, 3, 4, 5]
使用apply和内置函数
找出数组中最大/小的数字
var numbers = [1,7,4,8,2,5,3];
var max = Math.max.apply(null,numbers);
var min = Math.min.apply(null,numbers);
console.log(max);//8
console.log(min);//1
缺点:若参数数组过长超出js引擎限制的长度,会抛出异常导致某些参数丢失。
解决方法:将数组切块后循环传入目标。
bind()
Function.prototype.bind这个新函数中,this将永久地被绑定到了bind的第一个参数。
语法:function.bind(thisArg[,arg1[,arg2[, ...]]])
var name = 'Jackson'
var fn = {
name: 'Jane',
sayName: function(){
console.log(this);
}
}
var obj = {
name: 'lily'
}
fn.sayName();//Jane
fn.sayName.bind(window)();//Jackson
fn.sayName.bind(obj)();//lily
//
var module= {
bind: function(){
var _this = this;
$btn.on('click', function(){
console.log(_this); //this指什么
_this.showMsg();
})
},
showMsg: function(){
console.log('hello world');
}
}
//等同于
var module= {
bind: function(){
$btn.on('click', this.showMsg.bind(this))
},
showMsg: function(){
console.log('hello world');
}
}
call、play、bind()区别
- call、apply、bind作用:改变函数执行时的上下文,即this的指向
- call、apply参数传递方式不同
- call把参数按顺序传递进去
fun.call(thisArg, arg1, arg2, ...) - apply把参数放在数组里
fun.apply(thisArg, [argsArray])
- call把参数按顺序传递进去
- bind()返回对应函数稍后调用,apply、call是立即调用。
call、apply 、bind函数执行的本质
"use strict"
function fn(a,b){
console.log(this)
}
fn(1, 2)
//等价于
fn.call(undefined, 1, 2)
fn.apply(undefined, [1, 2])
//在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。
//在非严格模式下(不加"use strict"), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象
var obj = {
fn: function(a, b){
console.log(this)
},
child: {
fn2: function(){
console.log(this)
}
}
}
obj.fn(1, 2)
//等价于
obj.fn.call(obj, 1, 2) // 所以 this 是 obj
obj.fn.apply(obj, [1, 2])
obj.child.fn2()
//等价于
obj.child.fn2.call(obj.chid) // 所以 this 是 obj.child
箭头函数
引入箭头函数有两个方面的作用:更简短的函数并且不绑定this。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this