面试:call、apply、bind原理以及自己手写简易模式

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>call-apply-bind的实现</title>
    </head>
    <body>
        <script type="text/javascript">
            // 1.call的使用  call(ctx,...args) ctx:执行函数需要改变的this对象,args:执行函数需要传入的参数 call改变函数this指向
            function fn(name) {
              console.log(this, name); // window 老王
            }
            fn('老王');
            // 创建一个 obj
            let obj = {name: '老李'}
            // 通过call执行
            fn.call(obj, '我是谁') // {name: "老李"} "我是谁"  [注:如果有多个参数在后面接着即可]
            
            // 2.apply的使用 apply(ctx, array) ctx:执行函数的this指向; array:传入一个数组,我将数组的每一项作为函数的参数
            function fn1(name1, name2) {
              console.log(this, name1, name2);
            }
            let obj1 = {name: '老王'};
            let arr = ['小王', '小明'];
            fn1.apply(obj1, arr) // {name: "老王"} "小王" "小明"
            // 小结: call传入的是不定参数,apply传入的是一个具体的数组,数组里面包含所有参数
            // 例子:求数组中最大值
            let list = [1,2,3,4,9,20,6,8],
                max = Math.max.apply(null, list);
                console.log(max) // => 20
            // 3.bind的使用
            // bind(ctx, arg...): bind 和call方法的参数是一样的,只是用法有些区别
            // bind执行后返回值是一个函数,
            // 这个函数的this被定死(绑定)为传入的ctx对象且不可变,
            // 这个函数的参数也被定死(绑定)为 ...arg且不可变
            function fn3(age) {
              console.log(this, age)
            }
            let newoObj = {name: '小明'}
            let fun = fn3.bind(newoObj, 18)
            fun() // {name: "小明"} 18
            // 参数和this都被绑死
            fun.bind(window, 18)() // {name: "小明"} 18
            fun.call(window, 18) // {name: "小明"} 18
            fun.apply(window, [18]) // {name: "小明"} 18
            fun(18) // => {name: "小明"} 18
            // 小结:通过bind绑定过后的方法,无论怎么执行都无法修改this指向和参数的,注:以上三个方法如果不传第一个参数,this指向在非严格模式下均为window
            
            // 面试题:
            function fn() {
              console.log(this)
            }
            let objtest = {name: '老王'}
            
            fn.call(objtest) // {name: "老王"} call改变函数this指向
            
            fn.call.call(function () {
              console.log(this) // window
            }) 
            
            fn.call.call.call.call(function () {
              console.log(this) // {name: "老王"}
            }, objtest) 
            /*****************************************************/
            // 1、手写call (1)改变this指向  (2)处理传入的参数
            Function.prototype.myCall = function(ctx){
                // 先将fn挂在context上
                ctx.fn = this
                var args = [...arguments].slice(1)
                var res = ctx.fn(...args)
                // ctx调用fn 使fn中的this指向到ctx上
                delete ctx.fn
                return res
            }
            // 2、手写apply (1)改变this指向  (2)参数通过数组的形式
            Function.prototype.myApply = function(ctx){
                // 先将fn挂在context上
                ctx.fn = this
                var res
                if(arguments[1]){
                    res = [...arguments[1]];
                } else {
                    res = ctx.fn;
                }
                delete ctx.fn
                return res
            }
            // 3、手写bind  bind终生绑定不立即执行返回一个新函数,传参和 call 是一样的
            Function.prototype.myBind = function(context) {
                if (typeof this !== 'function') {
                    throw new TypeError('Error')
                }
                // 返回一个绑定this的函数,这里我们需要保存this
                const _this = this
                const args = [...arguments].slice(1)
                //返回一个函数
                return function F() {
                    // 因为返回一个函数,我们可以new F()需要判断能当做构造函数吗
                    if (this instanceof F) {
                        return new _this(...args, ...arguments)
                    }
                    return _this.apply(context, args.concat(...arguments))
                }
            }
            let testObj = {
                name: '测试手写'
            }
            let testArr = ['小王', '小明'];
            function test(){
                console.log(this, name);
            }
            
            test.myCall(testObj) // {name: "测试手写", fn: ƒ}
            
            function test2(name1, name2) {
              console.log(this, name1, name2);
            }
            
            test2.apply(testObj,testArr) // {name: "测试手写"} "小王" "小明"
            
            function foo() {
                console.log(this.name) // 测试手写
                console.log(arguments) // ['a','b','c']
            }
            
            foo.myBind(testObj, 'a', 'b', 'c')() 
        </script>
    </body>
</html>

 

posted @ 2020-10-14 20:07  鱼樱前端  阅读(187)  评论(0编辑  收藏  举报