Call,Apply,Bind

call,apply都是为了改编某个函数运行时的上下文context而存在,就是为了改变内部的this指向

1 .js的一大特点就是,函数存在定义时上下文,运行时上下文,以及上下文是可以改变的这样的概念

 var name="小王"
       var age=17
       var obj={
           name:"小李",
           age:19,
           objName:this.name,
        //    这里的this指的是全局的this
           objAge:this.age,
           myFunc:function(){
               console.log(this.name,this.age)
            //    这里的this是函数作用域的this,也就是obj
           }
       }
       console.log(obj.objName,obj.objAge)
       obj.myFunc()

1 .全局执行环境,严格模式下this指向undefined,非严格模式,指向window,global,self
2 .作为对象方法的调用,假设函数作为一个方法被定于i在对象中,那么this指向最后调用他的这个对象

call:函数没有使用括号之前使用它可以改变里面函数的this指向(可以有多个参数,只有第一个表示要改变的对象)

0 .fun.call(this.obj,arg1,arg2,arg3)

1 .第一个参数是调用函数时this的指向,随后的参数作为函数的参数并调用,也就是fun(arg1,arg2)

let obj={
            a:10
        }
        function test(a,b){
            console.log(a)
            console.log(b)
            console.log(this.a)
        }
        test()
//三个值都是undefined
        test.call(obj,11,12)
//11,12,10

1 .当一个obj里面没有某个方法,但是其他的某个对象有这个方法,我们就可以用call来使用其他对象的方法来操作本对象

1 .function cat(){

}
cat.prototype={
    "food":"fish",
    say:function(){
        c(this.food)
    }
}

const dog={
    food:'bone'
}

const c1=new cat();
c1.say.apply(dog)

2 .将参数和函数进行分离
function changeStyle(attr,value){
    this.style[attr]=value;
}
const box=document.getElementById('box')
window.changeStyle.call(box,'height','200px')
window.changeStyle.apply(box,['height','200px'])
//区别在于传递函数的方式

apply:(可以使用多个参数,但是多于的参数必须以数组的形式传入)

  1. 如果以上两个方法的第一个参数是null的话,那么指向window。
    2 .func.apply(thisArg, [argsArray])
    3 .thisArg:可选的,func函数运行时使用的this值。
    4 .利用apply可以使用一些本来需要写成遍历数组变量才可以使用的内建函数
let number=[1,2,3,4,5,6,7]
Math.max.apply(null,number)
ES6中更为简单
Math.max(...number)

let arr=[1,2,3,4,5]
console.log(Math.max(arr))
//直接使用,返会NaN,在Math.max的参数里面,如果有一个参数不能直接转化为数字,那么就会返回NaN

let arr=[1,2,3,4,5]
        // console.log(Math.max(arr))
console.log(Math.max.call(null,1,2,3,4,5))
console.log(Math.max.apply(null,arr))
console.log(Math.max(...arr))
//这三种都是返回一个结果,都是5



//大量的数组需要将参数切块之后循环传入目标方法。
function minofArray(arr){
    let min=Infinity
    let quantum=32768

    for(let i=0,len=arr.len;i<len;i+=quantum){
        let submin=Math.min.apply(null,arr.slice(i,Math.min(i+quantum,len)))
        min=Math.min(submin,min)
    }
    return min
}

5 .apply和call只有一个区别,就是传入的参数的个数不同,前者可以传入很多个,后者只能传入两个参数,而且第二个参数为调用函数时的参数构成的数组
6 .fun.apply(this.obj,[args])
7 .当不给函数传参数的时候,他们俩其实一样,需要传参数的时候应该注意他的参数转换成数组形式

function displayHobbies(...hobbies) {
    console.log(`${this.name} likes ${hobbies.join(', ')}.`);
}

// 下面两个等价
displayHobbies.call({ name: 'Bob' }, 'swimming', 'basketball', 'anime'); // => // => Bob likes swimming, basketball, anime. 
displayHobbies.apply({ name: 'Bob' }, ['swimming', 'basketball', 'anime']); 

bind:

不同1:bind返回一个修改过的函数,必须再次调用才会起作用,前两个都是使用完直接就输出的。
不同2:他传入的参数是按照实参,形参结果表示的、

1 .bind多次绑定一个函数,后续的绑定不会生效,始终指向第一次被绑定的函数
2 .bind绑定之后的函数,不能再使用call,apply再次改变函数的指向
3 .bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数

函数的使用场景

1 .作为某个对象的方法

1 .函数中的this往往指向的都是调用他的对象

2 .作为一个独立的函数,一般不在里面使用this
3 .es6之前用来展开数组调用,es6之后使用...操作符,多参函数转换为单个数组参数调用

Array.prototype.push.call(arr1,arr2)
//es6
[...arr1,...arr2]
arr1.push(...arr2)

4 .将类数组转化为数组

1 .Array,prototype.slice.call(类数组)

es6转换
const arr=Array.from(类数组)
或者
[...类数组]

5 .实现组合继承

类.call(this,类)

四条规则

1 .默认绑定,没有其他装饰bind,apply,call,非严格模式下指向全局对象,严格模式下定义指向undefined

function foo(){
            console.log(this.a)
        }
 var a=2
 foo()
//2 

2 .隐式绑定:调用位置是否有上下文对象,或者是被某个对象拥有或者包含,那么隐式规定则会把函数调用中的this绑定到这个上下文对象,而且,对象属性链只有上一层或者最后一层在调用位置中起作用

function foo(){
            console.log(this.a)
        }
        var a="123"
        var obj={
            // a:2,
//a被注释的话,返回的是undefined
//a没有被注释,返回的是2
            foo:foo,
        }
        obj.foo()

3 .显示绑定,通过在函数上运行call,apply来显示的绑定this

function foo(){
            console.log(this.a)
        }
       var obj={
           a:1
       }
       foo.call(obj)
//1
       foo()
//undefined

4 .new 绑定。new调用函数会创建一个全新的对象,兵将这个对象绑定到函数调用的this。

自己实现call,apply,bind

Function.prototype.call=function(context,...args){
            context=context||window
//这里要针对不同的类型thisArg分别处理
//null,undefined,不传,this将会指向全局对象
//原始值将被转为对应的包装对象,如call(1),this将指向Number

            const fnSymbol=Symbol('fn')
            context[fnSymbol]=this
//这里的this就是func.call()中的func
           const result = context[fnSymbol](...args)
//通过这里调用func来改变this指向的作用
            delete context[fnSymbol]

            return result
        }

        Function.prototype.apply=function(context,argsArr){
            context=context||window

            const fnSymbol=Symbol('fn')
            context[fnSymbol]=this
            context[fnSymbol](...argsArr)
            delete context[fnSymbol]
        }


Function.prototype.myBind = function (thisArg, ...args) {
    const func = this;

    // bind 返回的是一个新函数,如果使用 new 调用了被绑定后的函数,其中的 this 即是 new 最后返回的实例对象,也就是 target
    const boundFunc = function (...otherArgs) {
        // 当 new.target 为 func,不为空时,绑定 this,而不是 thisArg
        return func.call(new.target ? this : thisArg, ...args, ...otherArgs)
    };

    boundFunc.prototype = Object.create(func.prototype);
    boundFunc.prototype.constructor = boundFunc;
    Object.defineProperties(boundFunc, {
        name: {
            value: `bound ${func.name}`
        },
        length: {
            value: func.length
        }
    });
//实现原函数的bind后的bound函数的name和length
    return boundFunc;
}

改变this指向的方法

1 .箭头函数
2 .内部缓存this

a = 10
obj = {
a: 20,
f() {
const _this = this
setTimeout(function() {
console.log(_this.a, this.a)
}, 0)
}
}

obj.f() // _this.a 指向 20 this.a 则指向 10

3 .apply
4 .call
5 .bind
6 .new 操作符
new 操作符实际上就是生成一个新的对象,这个对象就是原来对象的实例。因为箭头函数没有this,所以箭头函数不能作为构造函数,构造函数通过new操作符改变了this的指向
function Persion(name){this.name=name}
this.name表示新创建的实例拥有一个name属性,当调用new的时候,构造函数中的this就绑定在了实例上面

self改变指向的操作

var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

性能问题

1 .参数小于3个的时候,call强,大于3个的时候apply,统一使用reflect

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容