zoukankan      html  css  js  c++  java
  • 【js重学系列】call-apply-bind

    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。
    作用:为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

    Function

    Function是函数对象的构造方法,所有的函数其实都是Function构造函数的实例对象,call,apply,bind其实并不在函数的这个实例对象中,而是在Function的原型对象中,都是构造函数原型对象上的方法,这个函数实例对象自然能访问到该方法
    是Function对象原型链上的方法,主要作用是改变函数执行时的上下文(改变this指向),将对象B的方法交给对象A来执行,并且是立即执行的

    为什么要改变函数的this指向,举个生活中例子你就明白了,比如你(对象A)上班时候想点根烟抽(事件),但是你没有打火机(方法),这时候你又不想偷跑出去买,所以你向你同事(对象B)借给一个打火机(方法),来点根烟抽(事件)

    在程序中也是一样,对象A有一个方法a,而 B 对象因为某种原因,也要用到方法a,那么是再为对象B添加方法a呢,还是借用一下 对象A的方法a呢?当然是借用对象A的,这样既完成了需求,又减少了内存的占用

    call apply

    function fn(x){
    	console.log(this,x);
    }
    fn(5)//window 5
    不穿参数
    fn.call() //window undefined
    fn.call(null)  //window undefined
    fn.call(null,5)//window 5 跟最开始调用一样了
    fn.apply(null,[5])//window 5 跟最开始调用一样了(两者传参的方式)
    
    function fn(x){
    	console.log(this.age,x);
    }
    		
    let obj={
    	age:10
    }
    fn(4)//undefined 4
    fn.call(obj,4)//10 4
    fn.apply(obj,[4])//10 4
    
    // 原型中:
    function Person(sex) {
    	this.sex = sex
    }
    Person.prototype.say = function(x,y) {
    	console.log(this.sex,x+y);
    }
    var per = new Person('男')
    	
    
    function Student(sex) {
    	this.sex = sex
    }
    var stu = new Student('女')
    per.say(4,6) //男 10
    per.say.call(stu,4,6) //女
    per.say.apply(stu,[4,6]) //女
    

    bind

    function fn(x,y) {
    	console.log(this,x+y);
    }
    bind使用的时候,是复制了一份函数,然后可以调用,参数可以在复制的时候传进去,也可以在复制之后调用的时候传进去
    fn.bind()() //window NaN
    fn.bind(null,10,20)() //window 30
    fn.bind(null)(10,20)//window 30
    

    结论:都可以改变this的指向
    call和apply方法是调用的时候改变this指向,没有返回值,call()方法只能接收多个参数列表, 而 apply()只能接收一个数组或者伪类数组,数组元素将作为参数传递给被调用的函数
    bind方法是复制的时候改变了this的指向,有返回值,就是该函数

    问题

    function show3() {
    	console.log(333);
    }
    
    function show4() {
          console.log(444);
    }
    
    function showrd() {
    // 1到10的随机数
    this.num = parseInt(Math.random() * 10 + 1)
    }
    showrd.prototype.show1 = function() {
    // console.log(this.show4);
    // 我没有理解这儿的this指向,如果这个this指向window,那么this.show3应该是window下的show3函数,为什么没反应
    // 而且this.show2和this.show2()该怎么理解 求大神指点
    // setInterval(this.show3, 1000)
    // setInterval(show3,1000)
    // setInterval(this.show2, 1000)
    // setInterval(this.show2(), 1000)
    setInterval(this.show2.bind(this), 1000)
    }
    showrd.prototype.show2 = function() {
          console.log(this.num);
    }
    var rd = new showrd()
    rd.show1()
    

    相同点

    调用的对象必须是Function对象;

    第一个参数是一个对象。调用者的this会指向这个对象。如果不传,则默认为全局对象 window。或者当第一个参数为null、undefined的时候,默认指向window。

    var age = '17';
    var name = '小明'
    let A = {
        setInfo(){
            console.log('我' + this.name + '今年' + this.age + '岁')
        }
    }
    let B={
        age:37,
        name:'老王'
    }
    A.setInfo();//我undefined今年undefined岁
    A.setInfo.call();//我小明今年17岁
    A.setInfo.apply();//我小明今年17岁
    A.setInfo.bind()();//我小明今年17岁
    A.setInfo.call(B);//我老王今年37岁
    A.setInfo.apply(B);//我老王今年37岁
    A.setInfo.bind(B)();//我老王今年37岁
    

    不同点

    第二个参数不同,call和bind接收一个参数列表,但apply不一样,接收一个包含多个参数的数组;

    执行返回不同,call和apply返回的是调用对象执行后的值,bind返回的是函数需要再次调用。

    let A = {
        setInfo(province,city){
            console.log('我' + this.name + '今年' + this.age + '岁,来自'+province+'省'+city+'市')
        }
    }
    let B={
        age:37,
        name:'老王'
    }
    A.setInfo.call(B,'四川','成都');//我老王今年37岁,来自四川省成都市
    A.setInfo.apply(B,['四川','成都']);//我老王今年37岁,来自四川省成都市
    A.setInfo.bind(B,'四川','成都')();//我老王今年37岁,来自四川省成都市
    

    应用场景

    求数组中的最大和最小值
    
    var arr = [1,2,3,89,46]
    var max = Math.max.apply(null,arr)//89
    var min = Math.min.apply(null,arr)//1
    将类数组转化为数组
    
    var trueArr = Array.prototype.slice.call(arrayLike)
    数组追加
    
    var arr1 = [1,2,3];
    var arr2 = [4,5,6];
    var total = [].push.apply(arr1, arr2);//6
    // arr1 [1, 2, 3, 4, 5, 6]
    // arr2 [4,5,6]
    判断变量类型
    
    function isArray(obj){
        return Object.prototype.toString.call(obj) == '[object Array]';
    }
    isArray([]) // true
    isArray('dot') // false
    调用父构造函数实现继承
    
      function Personal(name,age){
            this.name=name;
            this.age=age;
            this.setInfo=function(){
                console.log('我' + this.name + '今年' + this.age + '岁')
            }
        }
        const A=new Personal('小明','12');
        function FromTo(name,age,province,city){
            Personal.call(this, name,age)
            this.province=province;
            this.city=city;
            this.setAddress=function(){
            	console.log('我' + this.name + '今年' + this.age + '岁,来自'+province+'省'+city+'市')
            }
        }
        const B=new FromTo('老王','37','四川','成都');
        A.setInfo();//我小明今年12岁
        B.setInfo();//我老王今年37岁
        B.setAddress();//我老王今年37岁,来自四川省成都市
    

    封装一个修改 this 指向的方法

    function bindThis(f, oTarget,...rest) {
        const param=Array.from(arguments)[2];
        if(param && Array.isArray(param)){
            return function() {
                return f.apply(oTarget,...rest);
            };
        } else if(f.bind){
            return f.bind(oTarget,...rest)
        }else{
            return function() {
                return f.call(oTarget,...rest);
            };
        }
    }
    bindThis(A.setInfo,B, ['四川', '成都'])();//我老王今年37岁,来自四川省成都市
    bindThis(A.setInfo,B, '四川', '成都')();//我老王今年37岁,来自四川省成都市
    
    
  • 相关阅读:
    UVa 10055
    UVa 401
    c++中文件应用的一点小变化
    poj2136
    UVa 494
    一台电脑接两个显示器,双屏显示介绍zz
    学习jquery合集
    解决Windows下MinGW显示乱码zz
    QWS_MOUSE_PROTO该如何写
    Qt/e中鼠标设备分析
  • 原文地址:https://www.cnblogs.com/ycyc123/p/13415168.html
Copyright © 2011-2022 走看看