五. call_apply_bind 和 arguments

82 阅读3分钟

五. call_apply_bind 和 arguments

来实现一下apply、call、bind函数, 不会过度考虑一些边界情况

这里只是模拟,v8引擎大部分都是c++代码写的

call apply的使用的区别 见4.5this的显示绑定

5.1. JS模拟实现call函数

 // 如果args没有传参数,args就是一个空数组
 Function.prototype.hycall = function (thi  sArg, ...args) {
     // 获取执行调用的函数
     var fn = this
     // 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
     thisArg = thisArg ? Object(thisArg) : window
     // 将传进来的对象绑定到this(隐式绑定)   
     thisArg.fn = fn
     // 调用被执行的函数
     var result = thisArg.fn(...args)
     delete thisArg.fn
     // 返回最终结果
     return result
 }
 ​
 function foo() {
     console.log("foo", this);
 }
 ​
 function sum(num1, num2) {
     console.log("sum", this);
     return num1 + num2
 }
 ​
 foo.hycall(null)
 var sums = sum.hycall("abc", 10, 20)
 console.log(sums);

5.2. JS模拟实现apply函数

 Function.prototype.hyapply = function (thisArg, argArray) {
     // 获取执行调用的函数
     var fn = this
     // 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
     // 如果传入的thisArg=0,则转为Number对象
     thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
     // 将传进来的对象绑定到this      
     thisArg.fn = fn
     // 调用被执行的函数
     var result
     // 执行前判断是否有数组传进来,不然解构时会报错
     argArray = argArray || []
     result = thisArg.fn(...argArray)
     delete thisArg.fn
     // 返回最终结果
     return result
 }
 ​
 function sum(num1, num2) {
     console.log("sum被调用", this, num1, num2);
     return num1 + num2
 }
 var result = sum.hyapply("abc", [10, 20])
 console.log(result);
 ​
 function foo() {
     console.log("foo", this);
 }
 var bar = foo.hyapply("aaa")
 console.log(bar);
 var bar1 = foo.hyapply(0)
 console.log(bar1);

5.3. JS模拟实现bind函数

  • 系统bind()的调用
 function sum(num1, num2, num3, num4) {
     console.log("sum被调用", this, num1, num2, num3, num4);
     return num1 + num2
 }
 ​
 var result = sum.bind("aaa", 10, 20, 30, 40)
 result()
 ​
 var result = sum.bind("aaa")
 result(10, 20, 30, 40)
 ​
 var result = sum.bind("aaa", 10, 20)
 result(30, 40)

JS模拟实现bind函数

 Function.prototype.hybind = function (thisArg, ...argArray) {
     // 获取执行调用的函数
     var fn = this
     // 将thisArg转成对象类型(防止传入是非对象类型)及特殊值判断
     // 如果传入的thisArg=0,则转为Number对象
     thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
 ​
     // 返回新函数
     return function proxy(...args) {
         // 将传进来的对象绑定到this      
         thisArg.fn = fn
         // 合并两次传入的参数
         var fanilyArr=[...argArray,...args]
         var result = thisArg.fn(...fanilyArr)
         delete thisArg.fn
         return result
     }
 }
 ​
 function foo() {
     console.log(this);
     return 1
 }
 ​
 var bar = foo.hybind("xxx")
 console.log(bar());
 ​
 function sum(num1, num2, num3, num4) {
     console.log("sum被调用", this, num1, num2, num3, num4);
     return num1 + num2 + num3 + num4
 }
 var newSum = sum.bind("aaa", 10, 20)
 console.log(newSum(30, 40));

5.4. 认识arguments

arguments 是一个 对应于 传递给函数的参数类数组(array-like)对象

  • 有数组的一些特性,比如说length,比如可以通过index索引来访问
  • 没有数组的一些方法,比如forEach、map等;

常见的对arguments的操作有三个

  1. 获取参数的长度: arguments.length
  2. 根据索引值获取某一个参数: arguments[2]
  3. 获取当前arguments所在的函数: arguments.callee

5.5. arguments转成数组

  • for遍历arguments中所有的元素

     function foo() {
         var newArr = []
         for (let i = 0; i < arguments.length; i++) {
             newArr.push(arguments[i])
         }
         return newArr
     } 
     foo(1, 2, 3, 4, 5)
    
  • 用slice()将arguments转为数组

     function foo() {
         // this最初是原型,后面被显示绑定为arguments
         var newArr = Array.prototype.slice.call(arguments)
         // var newArr= [].slice.call(arguments) 等同上面
         console.log(newArr);
     }
     foo(1, 2, 3, 4, 5)
    

    疑惑_为什么slice可以实现该需求?下面是模拟Array中的slice函数

     Array.prototype.hyslice = function (start, end) {
         let _this = this
         let newArr = []
         start= start || 0
         end= end || _this.length
         for (let i = start; i < end; i++) {
             newArr.push(_this[i])
         }
         return newArr
     }
     ​
     function foo() {
         var newArr = Array.prototype.hyslice.call(arguments,1,3)
         console.log(newArr);
     }
     foo(1, 2, 3, 4, 5)
    
  • Array.from()

     function foo() {
         var newArr= Array.from(arguments)
         console.log(newArr);
     }
     foo(1, 2, 3, 4, 5)
    
  • 解构...

     function foo() {
         var newArr= [...arguments]
         console.log(newArr);
     }
     foo(1, 2, 3, 4, 5)
    

5.6. 箭头函数里没有arguments

箭头函数是不绑定arguments的,所以我们在箭头函数中使用arguments会去上层作用域查找:

注意:window的全局作用域是没有arguments;node的全局是有arguments的

 function bar(m,n){
     return (a,b,c)=>{
         console.log(arguments); // [10,20]
     }
 }
 var fn=bar(10,20)
 fn(1,2,3)
  • 箭头函数没有arguments,但我们有需要拿到这些额外参数,可以用剩余运算符
 var foo=(num1, num2, ...args)=>{
     console.log(...args);
 }
 foo(1,2,3,4,5,6)