精华内容
下载资源
问答
  • js代码-手写 apply

    2021-07-16 15:03:17
    js代码-手写 apply
  • js手写apply

    2018-10-17 20:43:14
    前端面试题之JS手写apply 参考之前手写的call方法: https://blog.csdn.net/qq593249106/article/details/83115935 Function.prototype.call2 = function (obj) { var obj = obj || window; var args = []; for...

    参考之前手写的call方法:
    https://blog.csdn.net/qq593249106/article/details/83115935

    Function.prototype.call2 = function (obj) {
        var obj = obj || window;
        var args = [];
        for(var i = 1; i < arguments.length; i++) {
            args.push('arguments[' + i + ']');
        }
        obj.fn = this;
        var result = eval('obj.fn('+args+')');
        delete obj.fn;
        return result;
    };
    

    观察call和apply:

    func.call(thisArg, arg1, arg2, arg3…)
    func.apply(thisArg, [argsArray])

    call和apply的区别就是,apply参数是以数组的形式传的,可以看看下面这个例子

    var value = 'v in window';
    function func() {
        arguments = [].splice.call(arguments, 0); //这一句是把参数转成数组,因为arguments不是数组,输出的时候容易观察
        console.log(arguments);
        console.log(this.value);
    }
    var obj = {
        value: 'v in obj'
    };
    func.call(obj, 1,2,3);
    //[1, 2, 3]
    //v in obj
    func.apply(obj, [1,2,3]);
    //[1, 2, 3]
    //v in obj
    

    之前手写call的操作是把参数整合成数组args:

    var args = [];
    for(var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']');
    }
    

    然而apply是直接传进来一个参数,省去了整合过程,直接把arguments[1]放进去:

    var result = eval('obj.fn('+arguments[1]+')'); //arguments[1]是func.apply2(obj, [1,2,3])的[1,2,3]
    

    再处理一下不传参的情况:

    if(!arguments[1]) {
        result = obj.fn;
    }else {
        result = eval('obj.fn('+arguments[1]+')');
    }
    

    最终代码:

    var value = 'v in window';
    function func() {
        arguments = [].splice.call(arguments, 0); //这一句是把参数转成数组,因为arguments不是数组,输出的时候容易观察
        console.log(arguments);
        console.log(this.value);
    }
    var obj = {
        value: 'v in obj'
    };
    Function.prototype.apply2 = function (obj) {
        var obj = obj || window;
        obj.fn = this;
        var result;
        if(!arguments[1]) {
            result = obj.fn;
        }else {
            result = eval('obj.fn('+arguments[1]+')');
        }
        delete obj.fn;
        return result;
    };
    func.apply(obj);
    //[]
    //v in obj
    func.apply(obj, [1,2,3]);
    //[1,2,3]
    //v in obj
    func.apply2(obj);
    //[]
    //v in obj
    func.apply2(obj, [1,2,3]);
    //[1,2,3]
    //v in obj
    
    展开全文
  • 手写 apply 和 call

    2021-03-21 15:08:31
    手写 apply 和 call 关于apply和call的实现可以看大佬的这篇文章面试官问:能否模拟实现JS的call和apply方法, 非常详细,本篇只是以自己的理解再写一遍,加强印象,做一个自己的总结。 apply 和 call Function....

    手写 apply 和 call

    关于apply和call的实现可以看大佬的这篇文章面试官问:能否模拟实现JS的call和apply方法
    非常详细,本篇只是以自己的理解再写一遍,加强印象,做一个自己的总结。

    apply 和 call

    Function.prototype.apply()-MDN

    apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

    func.apply(thisArg, [argsArray])

    thisArg

    必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

    argsArray

    可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。


    Function.prototype.call()-MDN

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

    function.call(thisArg, arg1, arg2, …)

    thisArg

    可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

    arg1, arg2, …

    指定的参数列表。


    MDN上说apply的thisArg是必选的,call的thisArg是可选的,于是我在严格模式和非严格模式下跑了下面这个例子:

    var name = "window"
    var obj = {
        name: "Jiuto"
    }
    function sayName(a,b){
        console.log(this)
        console.log([a,b])
    }
    sayName.call()
    sayName.apply()
    sayName.call(obj,1,2)
    sayName.call('this is call',1,2)
    sayName.apply(obj,[1,2])
    sayName.apply('this is apply',[1,2])
    

    非严格模式下:

    sayName.call() // Window [undefined, undefined]
    sayName.apply() // Window [undefined, undefined]
    sayName.call(obj,1,2) // {name: "Jiuto"} [1,2]
    sayName.call('this is call',1,2) // String {"this is call"} [1,2]
    sayName.apply(obj,[1,2]) // {name: "Jiuto"} [1,2]
    sayName.apply('this is apply',[1,2]) // String {"this is apply"} [1,2]
    

    严格模式下:

    sayName.call() // undefined
    sayName.apply() // undefined
    sayName.call(obj,1,2) // {name: "Jiuto"} [1,2]
    sayName.call('this is call',1,2) // "this is call" [1,2]
    sayName.apply(obj,[1,2]) // {name: "Jiuto"} [1,2]
    sayName.apply('this is apply',[1,2]) // "this is apply" [1,2]
    

    该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

    所以只要实现两个方法中的一个,就可以实现另一个。


    看一下规范

    ECMAScript5.1中文版

    Function.prototype.apply (thisArg, argArray)

    当以 thisArg 和 argArray 为参数在一个 func 对象上调用 apply 方法,采用如下步骤:

    1. 如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常 .
    2. 如果 argArray 是 null 或 undefined, 则
      a. 返回提供 thisArg 作为 this 值并以空参数列表调用 func 的 [[Call]] 内部方法的结果。
    3. 如果 Type(argArray) 不是 Object, 则抛出一个 TypeError 异常 .
    4. 令 len 为以 “length” 作为参数调用 argArray 的 [[Get]] 内部方法的结果。
    5. 令 n 为 ToUint32(len).
    6. 令 argList 为一个空列表 .
    7. 令 index 为 0.
    8. 只要 index < n 就重复
      a. 令 indexName 为 ToString(index).
      b. 令 nextArg 为以 indexName 作为参数调用 argArray 的 [[Get]] 内部方法的结果。
      c. 将 nextArg 作为最后一个元素插入到 argList 里。
      d. 设定 index 为 index + 1.
    9. 提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。

    apply 方法的 length 属性是 2。

    在外面传入的 thisArg 值会修改并成为 this 值。thisArg 是 undefined 或 null 时它会被替换成全局对象,所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。

    Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

    当以 thisArg 和可选的 arg1, arg2 等等作为参数在一个 func 对象上调用 call 方法,采用如下步骤:

    1. 如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常。
    2. 令 argList 为一个空列表。
    3. 如果调用这个方法的参数多余一个,则从 arg1 开始以从左到右的顺序将每个参数插入为 argList 的最后一个元素。
    4. 提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。

    call 方法的 length 属性是 1。

    在外面传入的 thisArg 值会修改并成为 this 值。thisArg 是 undefined 或 null 时它会被替换成全局对象,所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。

    apply的4-8,call的2我们不实现。


    实现思路:

    我们选择先实现apply,再利用apply实现call。

    给传入的thisArg增加调用函数方法func,执行该方法,执行完毕后删除。

    另外需要注意,为了避免覆盖thisArg上可能存在的同名方法,可以对原同名方法做一个存储,删除func后恢复。

    注意
    • 在ES3、ES5(在局部作用域中)中 undefined 是能修改的:

    所以判断一个变量a是不是undefined,更严谨的方案是 typeof a === 'undefined’或者a === void 0; 这里面用的是void,void的作用是计算表达式,始终返回undefined,也可以这样写void(0)。

    实现

    apply

    function generateUUID(){
        var i, random;
        var uuid = '';
        for (i = 0; i < 32; i++) {
            random = Math.random() * 16 | 0;
            if (i === 8 || i === 12 || i === 16 || i === 20) {
                uuid += '-';
            }
            uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
                .toString(16);
        }
        return uuid;
    }
    function getGlobalObject(){
        // console.log(this) // 非严格模式this指向Window,严格模式为undefined
        return this;
    }
    // 生成new Function的code
    function generateFunctionCode(argsArrayLength){
        var code = 'return arguments[0][arguments[1]](';
        for(var i = 0; i < argsArrayLength; i++){
            if(i > 0){
                code += ',';
            }
            code += 'arguments[2][' + i + ']';
        }
        code += ')';
        // return arguments[0][arguments[1]](arg1, arg2, arg3...)
        return code;
    }
    Function.prototype.applyFn = function apply(thisArg, argsArray){
        // 1. 如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常 .
        // 判断调用apply的对象是不是函数
        if(typeof this !== 'function'){
            throw new TypeError(this + ' is not a function');
        }
    
        //  2. 如果 argArray 是 null 或 undefined, 则 返回提供 thisArg 作为 this 值并以空参数列表调用 func 的 [[Call]] 内部方法的结果。
        // argsArray为null或者undefined时,初始化argsArray为空数组
        if(typeof argsArray === 'undefined' || argsArray === null){
            argsArray = [];
        }
        
        // 3. 如果 Type(argArray) 不是 Object, 则抛出一个 TypeError 异常 .
        if(argsArray !== new Object(argsArray)){
            throw new TypeError('CreateListFromArrayLike called on non-object');
        }
    
        // 在外面传入的 thisArg 值会修改并成为 this 值。
        // thisArg 是 undefined 或 null 时它会被替换成全局对象。
        if(typeof thisArg === 'undefined' || thisArg === null){
            thisArg = getGlobalObject();
        }
        // 所有其他值会被应用 ToObject 并将结果作为 this 值。
        thisArg = new Object(thisArg);
    
        // 尽可能的唯一函数名
        var func = generateUUID();
    
        // 储存原同名方法
        var originalVal = thisArg[func];
        var hasOriginalVal = thisArg.hasOwnProperty(func);
        
        // 增加方法
        thisArg[func] = this;
    
        // 9. 提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。
        var code = generateFunctionCode(argsArray.length);
        var result = (new Function(code))(thisArg, func, argsArray);
    
        // 第九条的ES6版
        // var result = thisArg[func](...args);
    
        // 删除方法
        delete thisArg[func];
    
        // 恢复原方法
        if(hasOriginalVal){
            thisArg[func] = originalVal;
        }
    
        return result
    };
    
    

    call

    Function.prototype.callFn = function call(thisArg){
        var argsArray = [];
        var len = arguments.length;
        for(var i = 0; i < len - 1; i++){
            // argsArray.push(arguments[i + 1]); // push方法内部也有一层循环,理论上不用push性能更好
            argsArray[i] = arguments[i + 1];
        }
        return this.applyFn(thisArg, argsArray);
    }
    

    测试

    var name = "window"
    var obj = {
        name: "Jiuto"
    }
    function sayName(a,b){
        console.log(this)
        console.log([a,b])
    }
    sayName.callFn()
    sayName.applyFn()
    sayName.callFn(obj,1,2)
    sayName.callFn('this is call',1,2)
    sayName.applyFn(obj,[1,2])
    sayName.applyFn('this is apply',[1,2])
    

    非严格模式下:

    sayName.callFn() // Window [undefined, undefined]
    sayName.applyFn() // Window [undefined, undefined]
    sayName.callFn(obj,1,2) // {name: "Jiuto", b2d272d0-0235-45c0-9994-abcd2ca7c2b0: ƒ} [1,2]
    sayName.callFn('this is call',1,2) // String {"this is call", 184967ed-ac02-40f0-9671-9bd4fd45f1a4: ƒ} [1,2]
    sayName.applyFn(obj,[1,2]) // {name: "Jiuto", c3954fbb-f0ad-459f-b0ae-5f2d64333ced: ƒ} [1,2]
    sayName.applyFn('this is apply',[1,2]) // String {"this is apply", ecaa54b9-bcea-43ac-b148-f0425600d4db: ƒ} [1,2]
    

    严格模式下:

    sayName.callFn() // {406b9c82-9727-409b-bfc5-c2b062e17ce4: ƒ} [undefined, undefined]
    sayName.applyFn() // {85d9975d-5096-46e8-bc67-6ebaf9aab914: ƒ} [undefined, undefined]
    sayName.callFn(obj,1,2) // {name: "Jiuto", c571e336-1e52-4295-8667-aeaaaaf3b8fa: ƒ} [1,2]
    sayName.callFn('this is call',1,2) // String {"this is call", e11114e6-102a-4627-ad03-86b215c44465: ƒ} [1,2]
    sayName.applyFn(obj,[1,2]) // name: "Jiuto", 4a55ef4c-9c0e-4ea6-a4b2-9d19859aba23: ƒ} [1,2]
    sayName.applyFn('this is apply',[1,2]) // String {"this is apply", 81313be6-5368-4696-830d-ad5df4f1b706: ƒ} [1,2]
    

    验证成功。

    唯一的问题是由于我们调用(new Function(code))(thisArg, func, argsArray)的时候,传入的thisArg是被我们加过料(增加了func)的对象,
    所以在sayName中打印this会多一个func方法,也就是我们以uuid命名的方法。

    我也试过将这个方法放在原型上,但同样的道理,虽然this打印出来时我们想要的结果,但是this.__proto__上会多出一个方法。

    只要实现是通过更改thisArg就无法避免这个问题。

    参考

    面试官问:能否模拟实现JS的call和apply方法

    展开全文
  • js代码-手写apply()和call()
  • JS 手写 apply( )

    2021-02-22 17:52:15
    JS 手写 apply( ) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" ...

    JS 手写 apply( )


    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <script>
            Function.prototype.myApply = function (thisArg) {
                //判断指定this的类型 
                if (thisArg === null || thisArg === undefined) {
                    thisArg = window;
                }
                if (typeof thisArg !== 'undefined' && typeof thisArg !== 'function' && typeof thisArg !== 'object') {
                    thisArg = Object(thisArg);
                }
    
                //获取用户给被call调用的函数的参数 因为arguments的第二个参数就是一个数组,所以直接获取argument的下标为1的值就行
                var argArr = arguments[1];
                console.log(argArr)
                //给thisArg扩展一个方法 这个方法就是调用call的那个函数this
                //创建一个独一无二的值,作为thisArg的方法key
                var uniqKey = Date.now().toString(36);
                thisArg[uniqKey] = this;
    
                //调用thisArg的uniqKey方法,此时 调用call的fn函数将被调用,并且this也指向了thisArg
                //因为我们还要把参数也传递进去,所以目前需要使用eval方法
                eval("var re = thisArg[uniqKey](" + argArr.toString() + ")");
    
                //删除给thisArg扩展的方法
                delete thisArg[uniqKey];
    
                //其实call的返回值 就是被调用函数的返回值 也就是this的返回值
                return re;
            }
    
    
    
            var obj = {
                name: "laowang"
            }
    
            function fn(a, b, c) {
                console.log(this, a + b + c);
                return "hello"
            }
            var re = fn.myApply(obj, [1, 2, 3]);
            console.log(re)
        </script>
    </body>
    
    </html>
    
    展开全文
  • 手写apply()

    2021-04-04 17:18:13
    Function.prototype.apply=function(context,args) { context.fn=this//为context设置函数属性 let result=context.fn(...args)//调用函数 delete context.fn//删除context的函数属性 return result }
    Function.prototype.apply=function(context,args) {
    
         context.fn=this//为context设置函数属性
    
         let result=context.fn(...args)//调用函数
    
         delete context.fn//删除context的函数属性
    
         return result
    
    }
    

     

    展开全文
  • apply call bind 相关 对于 apply call bind 之前也有写过一篇很详细的入门级文章 apply , call, bind 用法和区别 了解用法后,最好还是了解其实现原理,这样才能掌握透彻 三者的区别 ...手写 call
  • js手写apply(不能使用ES6语法) 参考前边手写call // apply和call的区别就是传参方式不同,apply第二个参数传的是数组 Function.prototype.myApply = function(context,args){ if(context === null || context === ...
  • 手写apply,call和bind

    2021-03-16 15:54:31
    apply: Function.prototype.myapply=function(content){ var content=content||window //判断this对象 content.fn=this //这个this指向的是array.push函数 console.log(arguments[0])//[a,b] console.log...
  • 2019前端面试系列——JS高频手写代码题 总结: apply、call、bind三者都是用来改变函数的this对象的指向的; apply、call、bind三者第一个参数都是this要指向的对象,也就是想指定的上下文 apply、call、bind三者都...
  • 手写apply,bind,call函数

    2020-09-08 10:35:43
    Function.prototype.myCall = function(content){ if(typeof this !== 'function'){ throw new Error('err') } const obj = content || window obj.fn = this const args = Array.prototype.slice.myApply...
  • 思想就是想办法把当前方法添加到传入的对象上
  • 最近又进行了基础知识的重新学习,然后进行了call,apply,bind的this指向、参数类型以及代码的手写,然后整理了下。 this指向 // this指向 const phper = { name: 'php', getName: function(){ console.log(this...
  • Function.prototype.myApply=function(objThis){ if(typeofthis!=='function'){ throwError(`需要函数才能绑定`) } if(!objThis){ objThis=window } letkey=Symbol() ...letargs=Array.pr...
  • call()、apply()、bind()是用来改变this的指向的。 一、call和apply 直接举例子: var cat = { name:"喵喵", eatFish:function(param1,param2){ console.log("吃鱼"); console.log("this的指向=>"); ...
  • } //默认window context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(...args) delete context.fn return result } apply实现 Function.prototype....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,798
精华内容 3,919
关键字:

手写apply