实现JavaScript的call-apply-bind函数

01.call函数的实现

function foo() {
    
    
    console.log('函数被执行了', this); => {
    
    }
}
// 系统的call函数
foo.call({
    
    }); 


// 需要给foo添加自己的call函数,如果是直接写出foo.hycall(),不具备通用性,换成另外的函数不适用,所以把它添加到函数的原型上,变成通用的,任何函数的可以使用

Function.prototype.hycall = function (thisArg, ...restParameters) {
    
     // ...restParameters 这个剩余参数 不管有没有参数都返回一个空数组
    // console.log('hycall函数被执行了');
    //在这里可以去执行调用的那个函数(foo)
    // 问题:的可以获取到是哪一个函数执行了hycall
    // 1.获取需要被执行的函数 -- 重点
    var fn = this;
    // fn();//这里直接调用相当于是独立函数调用
    // fn.call(thisArg) //但是这样实现不太好,我们本身写的这个函数就是call,
    // 所以我们可以fn函数添加到这个thisArg这个传进来的参数上,然后进行调用

    // 2.对thisArg转成对象类型(防止它传入的是非对象类型) -- 重点
    // 加上一个判断条件,系统的call函数 当为null和undefined时默认绑定window
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    // 3.调用被执行的函数 -- 重点
    thisArg.fn = fn;
    // 这里是展开运算符  restParameters
    var result = thisArg.fn(...restParameters);
    delete thisArg.fn;

    // 4.将最终结果返回出去 -- 重点
    return result;
}


// 自己是写的函数hycall方法
// 默认进行隐式绑定(这里隐式绑定调用了函数,所以要想拿到foo,直接可以使用this)
var result = sum.hycall({
    
     name: 'why' }, 20, 30, 40, 50);
console.log(result);

// 发现传入非对象类型的时候报错,无法添加函数对象,这时候需要把非对象类型转成对象类型,在转的过程,需要判断一下是否为null和undefined,null和undefined默认设置指向全局window
 // 因为系统的call就是这样的null和undefine的默认指向window
sum.hycall('123');
sum.hycall(1);

02.ES6的剩余参数补充:

// reste parameters
function sum(...restParameters) {
    
     // => 函数接收参数的时候是剩余参数
    console.log(restParameters);
}
sum(10)
sum(10, 20)
sum(10, 20, 30)
sum(10, 20, 30, 40, 50)


// 站看运算符 spread
var names = ['abc', 'cba', 'nba'];

var newNames = [...names]
// console.log(newNames);
function foo(name1, name2, name3) {
    
    
    console.log(name1, name2, name3);
}
// 相当于把names展开传到foo的里面去 =>这里是扩展运算符
foo(...names) 

03.apply函数的实现

// 自己实现apply
Function.prototype.hyApply = function (thisArg, argArray) {
    
    
    // 1.获取需要调用的函数
    var fn = this;

    // 2.处理绑定的thisArg
    thisArg = thisArg ? Object(thisArg) : window
    thisArg.fn = fn;

    // 3.执行函数
    let result = ''
    // if (!argArray) { // argArray是没有值(没有传参数)
    //     result = thisArg.fn(...argArray)
    // } else {// 有传参数
    //     result = thisArg.fn(...argArray)
    // }
    argArray = (argArray !== null && argArray !== undefined) ? argArray : []
    // argArray = argArray || []
    // 相当于...undefined undefined是不能被迭代的
    thisArg.fn(...argArray)
    delete thisArg.fn;

    // 4.返回结果
    return result;
}

function sum(num) {
    
    
    console.log('sum函数被执行了', this, num);
    return num
}

// sum.apply({}, 10)
//  两种情况:一种是只有一个参数是不行的,还有一种是没有参数
sum.hyApply(0, [20])

04.bind函数的实现

// 自己实现的bind函数
Function.prototype.hyBind = function (thisArg, ...argArray) {
    
    
    // 1.获取到真实需要调用函数
    var fn = this //相当于 function sum(){}  thisArgs相当于传进来的this指向参数

    // 2.处理thisArg的绑定
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    // 这里返回一个函数
    function proxyFn(...args) {
    
    
        // 3.将函数放到thisArg中进行调用
        thisArg.fn = fn;
        // 特殊:对两个传入的参数进行合并
        const finalArgs = [...argArray, ...args]
        var result = thisArg.fn(...finalArgs);
        delete thisArgf.fn;
        // 4.返回结果
        return result;

    }
    return proxyFn
}

function sum(num1, num2) {
    
    
    console.log('sum函数被调用了', this, num1, num2);
    return num1 + num2;
}

// 系统默认的bind函数
// var bar = sum.bind({}, 20, 1)
// var bar = sum.bind({})
// console.log(bar(30, 20));;
// var bar = sum.bind({}, 10)
// console.log(bar(30));    

var bar = sum.hyBind('abc', 10)
var result = bar(20);
console.log(result);

05.arguments基本使用

// arguments是一个对应于 传递给函数的参数的类数组(array-like)对象
// array-like意味着他不是一个数组类型,而不是一个对象类型:
// 但是它却拥有数组的一些特性,比如说length, 比如可以通过index索引来访问;
// 但是他却没有数组的一些方法,比如forEach、map等;
/**
 *var ao = {
    num1:undefined,
    num2:undefined,
    argumetns:{}
 } 
 */
function foo(num1, num2, num3) {
    
    
    // 类数组对象中(长得像是一个数组,本质上是一个对象):arguments
    console.log(arguments);

    // 常见的对arguments的操作是三个
    // 1.获取参数的长度
    console.log(arguments.length);

    // 2.根据索引值来获取某一个参数
    console.log(arguments[2]);
    console.log(arguments[3]);
    console.log(arguments[4]);

    // 3.callee获取当前arguments所在的函数
    console.log(arguments.callee);
    // console.log(arguments.callee());//不能调用,调用了就是递归 没有结束条件就会造成页面卡死

}

foo(10, 20, 30, 40, 50)

06.arguments转array的方式

function foo(num1, num2) {
    
    
    // console.log(arguments);
    // 1. 自己遍历
    // var newArr = [];
    // for (let i = 0; i < arguments.length; i++) {
    
    
    //     newArr.push(arguments[i] * 10)
    // }
    // console.log('newArr=', newArr);

    // 2.argumetns转成数组类型
    // 2.1. 自己遍历arguments中所有的元素

    // 2.2. 将arguments转成array

    // 我们把这个slice的this指向argumetns
    // 我们把这个this(argumetns)放入到一个可迭代的、可遍历的元素,如果这里没有传长度的话,
    //    就会把它全部放入到一个数组里面,最终给他返回
    var newArr2 = Array.prototype.slice.call(arguments)
    console.log('newArr2=', newArr2);


    var newArr3 = [].slice.call(arguments);
    console.log('newArr3=', newArr3);

    // 2.3. ES6的语法
    var newArr4 = Array.from(arguments);
    console.log('newArr4=', newArr4);

    var newArr5 = [...arguments]
    console.log('newArr5=', newArr5);

}
foo(10, 20, 30, 40, 50)
// // 在Function的原型中添加了一个aaa函数
// Function.prototype.aaa = function () { }

// function foo() { }
// // 存在一个函数就可以调用aaa
// foo.aaa();

// 如果说压根就没有这个函数对象呢,我们也想要去调用aaa,我们就可以去拿Function.prototype.aaa

// Array.prototype.slice = function(){  } 
// var nums = [1, 2, 3, 4];
// nums.slice()//是可以直接调用


// Array中的slice实现
Array.prototype.hyslice = function (start, end) {
    
    
    console.log(this);
    var arr = this;
    start = start || 0;
    end = end || arr.length
    var newArr = [];
    for (var i = start; i < end; i++) {
    
    
        newArr.push(arr[i])
    }
    return newArr;
}

// var newArray = Array.prototype.hyslice.call(['aaa', 'bbb', 'cccc', 'dddd'])
// console.log(newArray);
// var names = ['aaa', 'bbb', 'cccc', 'dddd'];
// names.slice(1, 2)

07.箭头函数中没有arguments

var name = 'arguments'
// 1.案例一:
var foo = () => {
    
    
    // console.log(name);
    console.log(arguments);
}

foo();

//在浏览器中是没有argumetns
// 在node中是偶argumetns的,为什么?
// - 在node中一个js文件会被当成一个模块,然后这个模块会被包裹在一个函数里面
// - 然后去执行这个函数  .call({},ad,sd,fads) ,后面这些参数就是argumetns,这个参数也放到了全局的arguments

function fn(num1, num2) {
    
    
    console.log(arguments);
}
fn(1, 2)

// 2.案例二:
function foo() {
    
    
    var bar = () => {
    
    
        console.log(arguments);
    }
    return bar;
}

var fn = foo(123);
fn();


// 案例三:
// 箭头函数就是没有arguments
var foo = (num1, num2, ...args) => {
    
    
    console.log(args);
}
foo(10, 20, 30, 405, 80)

猜你喜欢

转载自blog.csdn.net/xiaoxiannvh/article/details/129482959