一、this指向
1、函数调用模式:
函数中的this指向window
/**
* 函数中this指向
*/
function test2() {
console.log(this); // window
}
test2();
2、方法调用模式:
方法中的this指向方法所在的对象(函数作为对象的方法使用)
/**
* 方法中函数的指向
*/
var obj = {
sayHi: function () {
console.log(this); //obj{}这个对象
}
}
obj.sayHi();
obj['sayHi']();
3、构造函数调用模式:
构造函数内的this指向了新创建的实例对象
/**
* 构造方法中的函数指向
*/
var p = new Person();
function Person() {
console.log(this); //Person{}
}
4、上下文调用模式:
call、apply、bind改变this的指向
/**
* call改变this指向
*/
var vm = {
name: 'tom',
age: 11,
fn: function (a, b) {
console.log(this.age);//undefined
console.log(a + b); //NaN
}
}
var fn2 = vm.fn;
fn2.call();// 没有参数,默认this指向window
/**
* call改变this指向
*/
var vm = {
name: 'tom',
age: 11,
fn: function (a, b) {
console.log(this.age); //11
console.log(a + b); //3
}
}
var fn2 = vm.fn; // 只是赋值
fn2.call(vm, 1, 2); //this指定是vm
/**
* apply改变this指向
*/
var vm = {
name: 'tom',
age: 11,
fn: function (a, b) {
console.log(this.age); //11
console.log(a + b); //3
}
}
var fn2 = vm.fn;
fn2.apply(vm, [1, 2]); //this指定是vm
bind可以改变函数指向,它通过构造一个新的函数
新函数和旧函数一样,但是在内存中是2份函数
var fn = function () {
console.log(this); //[ 1, 3, 4 ]
}
var fn2 = fn.bind([1, 3, 4]);
console.log(fn === fn2); //false
fn2();
setTimeout中函数的指向是window,定时器中的function是window来调用的
/**
* setTimeout中this指向
*/
function test() {
var a = 100;
setTimeout(function () {
console.log(this.a); // 200
}, 0)
}
var a = 200;
test();
二、call/apply/bind区别
相同点:
都是用来改变this的指向的
不同点:
call第一个参数是要给this的绑定值,后面是一个参数列表
apply第一个参数是要给this的绑定值,后面是一个参数数组
bind第一个参数是要给this的绑定值,后面一个是参数列表,返回的是一个函数,不是立即执行的,需要调用;而call,apply是立即执行的
如果call方法没有参数,或者参数为null或undefined或this,则等同于指向全局对象
【扩展】
常见的伪数组: arguments、jQuery对象、document.querySelectorAll的返回值
三、方法
call、apply、bind是Function.prototype原型上的方法
任何函数在任何地方都可以使用call、apply、bind,因为任何函数的原型链上都有Function.prototype原型
四、使用call和apply
1、不传参
function fn() {
console.log(123);
}
fn.call(); // 123
fn.apply(); // 123
2、传参
function sum(x, y) {
console.log(this);
console.log(x + y);
}
var arr = [2, 3];
sum.call(null, 2, 3); // 5
sum.apply(null, [2, 3]); // 5 第一个参数传入null,this指向js的全局对象,浏览器中是window
3、实际应用
var arr = [18, 29, 89, 73, 5, 10, 8, 99, 105, 52];
let max1 = Math.max.call(null, 18, 29, 89, 73, 5, 10, 8, 99, 105, 52);
console.log(max1); //105
let max2 = Math.max.apply(null, arr)
console.log(max2);// 105
4、修改this指向
/**
* 改变this指向
*/
var dog = {
name: '小青',
age: 3
}
function animal(a, b) {
console.log(a + b);
console.log(this);
console.log(this.name);
console.log(this.age);
}
animal(1, 2); // js全局对象 浏览器window undefined undefined
animal.call(dog, 1, 2);// this指向dog对象{ name: '小青', age: 3 }
5、类数组转数组
/**
* 创建伪数组:常见的伪数组 arguments、jQuery对象、document.querySelectorAll的返回值
*/
var obj = {
0: "卡卡西",
1: "佐助",
2: "鸣人",
length: 3
};
[].push.call(obj, '小孩'); // 利用数组的方法,往伪数组后面添加一项
console.log(obj); // { '0': '卡卡西', '1': '佐助', '2': '鸣人', '3': '小孩', length: 4 }
var arr = [].slice.call(obj); // 数组的slice方法可以把类数组转换成真数组
console.log(arr); //[ '卡卡西', '佐助', '鸣人', '小孩' ]
五、bind
1、改变this指向
/**
* bind改变this指向
*/
var fn = function () {
console.log(this);
}
var fn2 = fn.bind([10, 20, 30]); // this固定指定值[10,20,30]
console.log(fn === fn2); // false
fn2();
2、修改定时器指向
/**
* bind修改定时器指向
*/
var obj = {
name: 'tom',
age: 13,
hi: function () {
setTimeout(function () {
console.log(this); //this指向obj
console.log(this.age);
}.bind(this), 1000)// this不在定时器内部,指向的是对象obj
}
}
obj.hi(); // 13
/**
* var that = this 解决定时器指向
*/
var obj = {
name: 'tom',
age: 13,
hi: function () {
var that = this;
setTimeout(function () {
console.log(this); //this指向window
console.log(that.age);// that 指向obj
}, 1000)
}
}
obj.hi(); // 13
3、箭头函数没有this
/**
* 箭头函数
*/
var obj = {
name: 'yom',
age: 12,
hi: function () {
setTimeout(() => { //箭头函数没有自己的this,使用的是外部函数的this 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值,
console.log(this); // this指向obj
console.log(this.age);
}, 0)
}
}
obj.hi();// 12