js || this

98 阅读4分钟

一、this简介?

this是什么?

this是在运行时绑定,不是在编写时绑定,上下文取决于函数调用时各种条件,this的绑定和函数声明位置没任何关系,只取决于函数的调用方式。
函数被调用时,会创建一个活动记录(也被称为执行上下文)。它包含了函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this是这个记录的一个属性,会在函数执行过程中用到。

二、调用位置

function a(){
        console.log("当前调用栈是a,调用位置是全局作用域")
        b()
}
function b(){
        console.log("当前调用栈是a->b,调用位置是在a()中")
        c()
}
function c(){
        console.log("当前调用栈是a->b->c,调用位置是在b()中")
}
a()

三、绑定规则

函数执行过程中调用位置如何决定this的绑定对象

3.1 默认绑定

独立函数调用

function foo(){
    console.log(this.name);  //调用foo()时this.name被解析为全局变量name,
    //这时this是默认绑定(因为foo()调用的时候不带任何修饰的函数引用,只能默认绑定。严格模式下不能,this会被绑定到undefined中)
}
var name = 'zs' //声明全局作用域的变量
//在严格模式下运行foo()不影响默认绑定,this仍指向全局
foo()           //zs

3.2 隐式绑定

考虑调用位置是否有上下文对象(被某个对象拥有或包含)
在对象内部包含一个指向函数的属性,并通过这个属性间接引用函数

function foo(){
    console.log(this.name);  
}
var xf = {
        name : 'zs' ,
        foo : foo  //xf这个对象有了foo函数引用,若是没有foo,报错:xf.foo is not a function
}
xf.foo()    //zs 调用foo()时this被绑定在xf中,前面加了对xf的引用,调用位置会用xf上下文引用函数

3.2.1 隐式丢失

function foo(){
    console.log(this.name);  
}
var xf = {
        name : 'zs' ,
        foo : foo
}
var b = xf.foo;  //b此时引用的是foo函数本身,下面打印的可以看出来
console.log(b);   // ƒ foo(){console.log(this.name); },所以b是中this是指向全局的
// var b = xf.foo();   //这时是有了foo()函数引用
// console.log(b);   //zs
var name = "a是全局对象的属性"
b()         // a是全局对象的属性  b()不带任何修饰的函数调用,用的是默认绑定
// b(xf.foo)    // a是全局对象的属性,这时就引用了foo对象中

3.3 显示绑定

不想在对象内部包含函数引用,想在某个对象上强制调用函数,就要用到call()和apply()方法

function foo(){
    console.log(this.name);  
}
var xf = {
        name : 'zs' ,
}
foo.call(xf)  //调用foo时强制把它的this绑定到xf中

3.3.1硬绑定

function foo(){
    console.log(this.name);  
}
var xf = {
        name : 'zs' ,
}
var name = 'global'
var a = function b(){
        foo.call(xf)  //foo中this绑定到xf中,之后如何调用a。都会手动在xf上调用foo,这种强制绑定就是硬绑定
}
a()   //zs
setTimeout(a,100)  //zs
a.call(window)   //zs

应用场景: 1.创建包裹函数,负责接收参数并返回值 2.创建可以重复使用的辅助函数

3.4 new绑定

function foo(a){
    this.a = a;  
}
var b = new foo(2)   //会先创建一个新对象并把它绑定到foo()调用中的this上
console.log(b.a)    // 2

四、优先级

1.函数是否在new中调用(new绑定)?如果是,this绑定的是新创建的对象。 var bar = new foo()
2. 函数是否通过call、apply(显示绑定)或者硬绑定调用?如果是,this绑定的是指定的对象。 var bar = foo.call(obj)
3函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。 var bar = obj.foo()
4.如果都不是的砧,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。 var bar = foo()

五、this词法

箭头函数不使用this的四种规则,而是根据外层(函数或者全局)作用域来决定this。
箭头函数常用于回调函数中

function foo(){ 
        setTimeout(() =>{
                console.log(this.a);  //this继承foo()
        },100)			
}	
var obj = {
        a : 2,
}
foo.call(obj)  //2

六、改变this指向

6.1 call()

this使用call()不带参数之前是指向window的,通过call可让他指向某个对象

var obj = {
        name : 'zs'
}
function fn(){
        console.log(this);
}
fn.call()          //window
fn.call(obj)  //{name:'zs'}

继承时改变this

function Animal(name,age){
        this.name = name
        this.age = age
}
function Cat(name,age){
        Animal.call(this,name,age)
}
var cat = new Cat("mimi",5)
console.log(cat);

6.2 apply()

this使用apply()不带参数之前是指向window的,通过call可让他指向某个对象

var obj = {
        name : 'zs'
}
function fn(){
        console.log(this);
}
fn.apply()          //window
fn.apply(obj)  //{name:'zs'}

传参必须是数组形式

var obj = {
        name : 'zs'
}
function fn(arr){
        console.log(this);
        console.log(arr);       //arr 打印出来的是字符串
}
fn.apply()          //window
fn.apply(obj,['arr'])  //构造函数若是传参的话必须是数组形式

应用场景,传递的是数组,所以可用数组中一些方法

var arr = [1,66,3,99,4]
var max = Math.max.apply(null,arr)
var min = Math.min.apply(null,arr)
console.log(max);     //99
console.log(min);     //1

6.3 bind()

bind不会调用函数也能改变this指向

var obj = {
        name : 'zs'
}
function fn(){
        console.log(this);
}
var f = fn.bind(obj) //不会调用函数,会返回一个新函数,相当于一次拷贝
console.log(f);       //{name:'zs'}

apply()和call()会立即调用,bind()可以绑定函数

var buttons = document.querySelectorAll("button")
for(let i = 0;i<buttons.length; i++){
        buttons[i].onclick = function(){
                this.disabled = true         //这个
                setTimeout(function(){
                        this.disabled = false
                }.bind(this),3000)
        }
}