JavaScript--函数与回调函数(及 this 指向问题)

函数

什么是函数?

是实现特定功能的 n 条语句的封装体,只有函数是可以执行的,其他类型的数据不能执行

使用函数的好处?

可以提高代码复用性,便于阅读交流

怎么定义函数?

1、 函数声明方式来定义函数:
function square(number) {
return number * number;
}

2、 函数表达式的方式来定义函数:
var square = function(number) { return number * number; };

怎么调用执行函数?

1、 直接调用 :fn()
2、 通过对象调用: obj.test
3、 new调用: new Function()
4、 使用 call/apply 等方法调用:fn.call/apply(obj) 临时让fn成为 obj 的属性方法进行调用,如下面的例子

        function showProps() {
            this.name='lily';
        }
        var obj={};
        showProps.call(obj);
        console.log(obj);//{name: "lily"}

回调函数

什么是回调函数?

回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。

回调函数的使用场景?

1、异步编程
2、事件监听、处理
3、setTimeout(延时定时器)、setInterval(循环定时器)

this指向问题 :

回调函数调用时的 this

在回调函数调用时 this 的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
调用回调函数的时候是在全局环境下,因此 this 指向的是window,所以sum的值是赋值给windows的,看下面例子:

//--------------直接调用函数时 this 指向--------------
       function f(str) {
            this.name=str;
        }
        f('ljy'); //直接调用相当于在 window全局对象上调用 f 方法,this 指向 window ,name是全局变量
        console.log(window.name);//'ljy'
        console.log(name);//'ljy'

//--------------调用对象中的属性方法时 this 指向--------------
        var obj1={
            sum:0,
            add:function (num1,num2) {
                this.sum = num1 + num2;
            }
        }
        obj1.add(1,1);//直接调用时 add 方法是作用于 obj1 这个对象上的,this.sum 指obj1 的sum属性
        console.log(obj1.sum);//2
        console.log(window.sum);//undefined

//--------------回调函数中 this 指向--------------
/*在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
* 调用回调函数的时候是在全局环境下,因此this指向的是window,所以sum的值是赋值给windows的*/
        function add(a,b,callback) {
            callback(a,b);
        }
        add(2,3,obj1.add);
        console.log(obj1.sum);//2
        console.log(window.sum); //5

不同执行环境下 this 指向

函数的 this 在调用时绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。
一般情况下, 任何函数本质上都是通过 this对象 调用的,this 指向的是调用函数的对象,没有直接指定的对象默认是 window ,下面分析函数调用的几种方式:

注意: 使用 call 方法调用函数时,this 指向的是 call 方法中的参数对象。

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

this指向问题总结:

    //    this指向问题总结:
    //    1、普通函数直接调用:this 指向的对象是window
    function f() {
        console.log('普通函数的this:'+this)
    }
    f()//window
    
    // 2、对象的方法:this 指向的是对象obj
    var thisObj
    var obj={
        method:function () {
            thisObj=this
            console.log('对象方法的this:'+this)
        }
    }
    obj.method()
    console.log(obj===thisObj)//true
    
    // 3、构造函数:this 指向的是构造函数创建的实例对象
    var that;
    function Person() {
        that=this
    }
    var person = new Person()
    console.log(that===person)//true
    
    // 4、绑定事件函数:this指向的是函数调用者btn 这个按钮元素
    var btn = document.querySelector('button')
    btn.onclick=function () {
        console.log('绑定事件函数的this:'+this)//绑定事件函数的this:[object HTMLButtonElement]
    }
    
    // 5、 定时器函数:this指向window
    setTimeout(function () {
        console.log('定时器的this:'+this)//[object Window]
    },2000)
    
    // 6、立即调用函数:window
    ;(function () {
        console.log('立即调用函数的this:'+this)//[object Window]
    })()

注: 上面例子 5 中,如果没有特殊指向,setIntervalsetTimeout 的回调函数中 this 的指向都是 window。这是因为 JS 的 定时器方法 是定义在 window 下的。

如果需要改变 this 指向,JavaScript提供了一些函数方法来处理改变 this指向问题:常用的有 call apply bind ,关于改变 this 指向及 call apply bind 三种方法的使用记录在 这篇文章中 。

!!!总结: 由于 this 在函数调用时才确定是会动态改变的,与其调用方式和运行的环境有关系,与其函数定义的位置,所处的作用域没有直接关系,看下面例子加深理解。区别以下几种不同的调用方式 this 的结果:

   let callObj={
        name:'call改变的this'
    }
    let name = 'window'
    let obj={
        name:'Obj',
        show:function (str) {
            console.log(str+this.name)
            function f(){
                console.log('f函数直接调用指向的this:'+this.name)
            }
            f()//f 始终为直接调用,this 都是指向window,与其所在的作用域执行上下文无关
        }
    }
    //以对象的方法调用
    obj.show('以对象的方法调用时this:')
    //全局变量 fn 引用该函数,在 window下直接调用
    let fn = obj.show
    fn('全局变量 fn 引用该函数,在 window下直接调用:')

    // 使用 call 方法改变this 指向
    fn.call(callObj,'使用 call 方法改变this :')
    

输出结果:

JavaScript--函数与回调函数(及 this 指向问题)_第1张图片


补充:箭头函数 this 继承问题

箭头函数回顾:JavaScript-- ES6 箭头函数

箭头函数没有 this,只会从自己的作用域链上继承上一层作用域的 this,所以箭头函数也不能使用 call apply bind 方法来改变 this 指向,第一个参数会忽略无效。

两道题对 this 指向问题进行巩固:
题目参考文章:https://segmentfault.com/a/1190000010981003


    var name = 'window'
    var person1 = {
        name: 'person1',
        show1: function () {
            console.log(this.name)
        },
        show2: () => console.log(this.name),
        show3: function () {
            return function () {
                console.log(this.name)
            }
        },
        show4: function () {
            return () => console.log(this.name)
        }
    }
    var person2 = { name: 'person2' }

    //普通函数:谁调用该函数 this 就指向谁
    person1.show1()//person1
    person1.show1.call(person2)//person2

    //箭头函数:this继承其作用域链中上一层作用域的 this,即直接包含它的那个函数或函数表达式中的 this,call等修改this方法无效
    person1.show2()//window
    person1.show2.call(person2)//window

    person1.show3()()//window
    person1.show3().call(person2)//person2
    person1.show3.call(person2)()//window

    person1.show4()()//person1
    person1.show4().call(person2)//person1
    person1.show4.call(person2)()//person2

第二道:


    var name = 'window'

    function Person (name) {
        this.name = name;
        this.show1 = function () {
            console.log(this.name)
        }
        this.show2 = () => console.log(this.name)
        this.show3 = function () {
            return function () {
                console.log(this.name)
            }
        }
        this.show4 = function () {
            return () => console.log(this.name)
        }
    }

    var personA = new Person('personA')
    var personB = new Person('personB')

    personA.show1()//personA
    personA.show1.call(personB)//personB

    personA.show2()//personA
    personA.show2.call(personB)//personA

    personA.show3()()//window
    personA.show3().call(personB)//personB
    personA.show3.call(personB)()//window

    personA.show4()()//personA
    personA.show4().call(personB)//personA
    personA.show4.call(personB)()//personB
    

参考文章:

  • MDN-函数
  • 深入理解 JavaScript 回调函数
  • JavaScript异步机制详解
  • 面试官问:JS的this指向

你可能感兴趣的:(js,面试复习,javascript)