什么是this?
- 在js中,this是一个指针型变量,它动态指向当前函数的运行环境。
- 在不同的场景中调用同一个函数,this的指向也可能会发生变化,但是它永远指向其所在函数的真实调用者;如果没有调用者,就指向全局对象window。
this有以下6类场景分类:全局、函数内、对象内、构造函数、箭头函数、原型链;
1、全局作用域的this指向
在全局作用域下,this始终指向全局对象window,无论是否是严格模式!
congsole.log()
完整的写法是window.console.log(),window
可以省略,window调用了console.log()
方法,所以此时this指向window。
2、普通函数内this指向
函数内的this指向分为2种情况,严格模式、非严格模式;
严格模式:使用者对代码的的调用必须严格的写出被调用的函数的对象,不可以有省略或者说简写;
function aa(){
name:'hyy',
say:function(){
console.log('hello world',this)
}
}
aa() // undefined 因为没有写出被调用的函数对象,不能简写
window.aa() // window
非严格模式:
aa() // window
window.aa() // window
3、对象内this指向
对象内的this指向,谁调用方法就指向谁;多层嵌套的情况下,找最近的调用对象。
4、构造函数的this指向
关于this,指向new创建出来的新实例上。
5、箭头函数的this指向
箭头函数:this指向于函数作用域所用的对象,向上寻找父级、最近的this。
箭头函数的重要特征:箭头函数中没有this和arguments。
箭头函数没有自己的this指向,它会捕获自己定义所处的外层执行环境,并且继承这个this值,指向当前定义时所在的对象。箭头函数的this指向在被定义的时候就确定了,之后永远都不会改变。即使使用call()、apply()、bind()等方法改变this指向也不可以。
6、原型链中的this
**在原型链继承当中,this永远指向它原本的对象,**而不是通过原型链上找到的所属对象。
改变this指向的方法
call 、bind 、apply都可以改变this的指向;this的指向只能是复杂类型。
1、call(1.调用fn,2.改变this指向);
arr.call() 不传任何参数,实际是把参数设置为window
arr.call(基本类型),指向对应的基本类型包装类型的对象,如3 (new Number(3))===> Number{3} ;
函数.call(需要修改的this指向,实参列表) call(a,b,c)//一次性的
细节:如果什么都没传,或者是null或undefined,那么this的指向一定是window;
this的指向只能是复杂类型,如果传的是基本类型也会包装成负责类型;
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.sayHi = function (){
console.log(`我的名字是${this.name},我的年龄是${this.age}`)
}
let person1 = new Person('小明',18)
let person2 = new Person('老王',50)
person1.sayHi() // 我的名字是小明,我的年龄是18
person1.sayHi.call(person2) //我的名字是老王,我的年龄是50
person2.sayHi() //我的名字是老王,我的年龄是50
// arr1.call(arr2) ,arr1的this指向arr2
// 利用call借用数组方法
let arr = { // 伪数组
0:'1',
1:'2'
}
Array.prototype.push.call(arr,'3')
Array.prototype.join.call(arr,"")
[].prototype.sort.call(arr,function(a,b){return a-b})
// 利用call借用string方法
let arr = [1,2,3,45,5]
String.prototype.substr.call(arr,0,3)
// 利用call借用构造函数继承,以及“完整继承”如何实现
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.sayHi = function(){
console.log(`我叫${this.name},年龄${this.age}`)
}
function Student(name,age){
Person.call(this,name,age)
stud = function(){
console.log('我是学生')
}
}
let s1 = new Student('hyy',18)
// s1.sayHi() // 报错,因为这里只继承了构造函数Person,于原型链无关
// 完全继承,涉及混入,原型继承
for (let key in Person.prototype){
Student.prototype[key] = Person.prototype[key]
}
s1.sayHi()
2、apply(1.调用fn,2.改变this指向)
apply语法其实于call一样,区别不过传参形式不一样罢了;
函数.apply(需要修改的this指向,实参数组/伪数组) apply(a,[b])//一次性的
细节:传入的参数,必须是数组或伪数组
// 求数组最大最小值,以前用for循环
let arr = [1,2,445,6756,234,0]
// let max = Math.max(...arr)
// let min = Math.min(...arr)
let max = Math.max.apply(arr,arr)
let min = Math.min.apply(arr,arr)
console.log(max,min)
3、bind**(1.不调用fn,2.改变this指向,3.返回一个新的函数)
bind语法与call一模一样,区别在与立即执行还是等待执行,call是直接改变函数的this指向,而bind是生成一个新的函数,该函数改变指向
let newFn = Fn.bind(需要修改的this指向,实参列表) //永久了
细节:如果初始就传入实参列表了,以后实参就固定了无法修改;
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.sayHi = function (){
console.log(`我的名字是${this.name},我的年龄是${this.age}`)
}
let person1 = new Person('小明',18)
let person2 = new Person('老王',50)
person1.sayHi() // 我的名字是小明,我的年龄是18
person1.sayHi.bind(person2)() //我的名字是老王,我的年龄是50 (看这里是需要调用才会执行的)
person1.sayHi() //我的名字是小明,我的年龄是18 (产生了新的函数所以这里不影响原来的函数)
//案例:修改定时器的指向
// <button id='get'>获取验证码</button>
Document.querySelector('#id').onclick = function(){
let sec = 59;
this.innerHTML = '还有59秒'; // 这里的this是dom元素
this.disabled = true;
let timeId = setInterval(function(){
sec--
this.innerHTML = '还有' + sec + '秒';
if(sec == 0){
clearInterval(timeId);
this.innerHTML = '获取验证码'; // 定时器的this是window,所以这里肯定报错
this.disabled = false;
}
},1000)
}
// 目的:把定时器的this指向dom的this
Document.querySelector('#id').onclick = function(){
let sec = 59;
this.innerHTML = '还有59秒'; // 这里的this是dom元素
this.disabled = true;
let fn = function(){
sec--
this.innerHTML = '还有' + sec + '秒';
if(sec == 0){
clearInterval(timeId);
this.innerHTML = '获取验证码'; // 定时器的this是window,所以这里肯定报错
this.disabled = false;
}
}
let newFn = fn.bind(this);
let timeId = setInterval(newFn,1000)
}
//结果
Document.querySelector('#id').onclick = function(){
let sec = 59;
this.innerHTML = '还有59秒'; // 这里的this是dom元素
this.disabled = true;
let timeId = setInterval((function(){
sec--
this.innerHTML = '还有' + sec + '秒';
if(sec == 0){
clearInterval(timeId);
this.innerHTML = '获取验证码'; // 定时器的this是window,所以这里肯定报错
this.disabled = false;
}
}).bind(this),1000)
}