前端面试题:你能手写个bind方法吗?

542 阅读3分钟

call 与apply的用法

运用apply call 方法,在调用函数时候绑定this,也就是指定调用的函数的this值区别是在给调用函数传递参数时,apply()是数组,call()参数是逐个列出的。
let user1 = {
    name: "你我贷",
    say: function(sex) {
        console.log(this)
        console.log(this.name + "性别:" + sex) 
    }
} 
let user2 = {
    name: "飞旋"
}
user1.say.call(user2,"男")


如果换成apply则如下:

user1.say.apply(user2,["男"])


bind()的用法

bind() 方法创建一个新的函数, 当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列。

公式:

fn.bind(thisArg[, arg1[, arg2[, ...]]])
let user1 = {
    name: "你我贷",
    say: function(sex) {
        console.log(this)
        console.log(this.name + "性别:" + sex)
    }
} 
let user2 = {
    name: "飞旋"
}
user1.say.bind(user2)("女")


或者

user1.say.bind(user2)(["女"])

bind方法传递给调用函数的参数可以逐个列出,也可以写在数组中。bind方法与call、apply最大的不同就是前者返回一个绑定上下文的函数,而后两者是直接执行了函数。因此可以改成如下

总结:

该方法创建一个新函数,称为绑定函数,绑定函数会以创建它时传入bind()的第一个参数作为this,传入bind()的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。


手动实现bind()

从bind的定义描述中可以看到,我们要写的这个函数的输入输出基本确定了:

  • 输入:接受一个或者多个参数,第一个是要绑定的上下文,额外参数当作绑定函数的前置参数。
  • 输出:返回原函数的拷贝,即返回一个函数,这个函数呢具备原函数的功能


先看看Array.prototype.slice.call(arguments)

function test(a,b,c,d) {
    var arg1 = Array.prototype.slice.call(arguments);
    var arg2 = Array.prototype.slice.call(arguments,1);
    console.log(arg1);
    console.log(arg2);
}
test("a","b","c","d"); 


实现:

Function.prototype.myBind = function(context) {
    var _self = this;
    //从第二个参数截取,获取实参
    var args = Array.prototype.slice.call(arguments, 1)
    return function() {
        // 对bind函数的实参和返回绑定函数的实参进行合并
        var _args = args.concat(Array.prototype.slice.call(arguments));
        //注意参数的处理
        return _self.apply(context,_args );
    }
}

function test1(val1) {
    this.name = val1
    console.log(this)
    console.log(this.name)
}

var obj1 = {}

test1.myBind(obj1)("你我贷")


测试下Array.prototype.slice.call(arguments)

function test1(val1) {
    this.name = val1
}

Function.prototype.fn= function(context) {
    var _self = this;
    var args = Array.prototype.slice.call(arguments,1)
    console.log(args)
    return function() {
        var _args = Array.prototype.slice.call(arguments)
        console.log(_args)
    }
   
}

test1.fn("111","2")("333")


arguments

在 JavaScript 中,arguments 对象是比较特别的一个对象,实际上是当前函数的一个内置属性。 arguments 非常类似 Array ,但实际上又不是一个 Array 实例。
function fn() {
    console.log(arguments)
    console.log(arguments[0])
    console.log(arguments.length)
}
fn("nwd")


Array.prototype.name = "你我贷"
function fn() {
    console.log(arguments)
    console.log(arguments[0])
    console.log(arguments.length)
    console.log(arguments.name)
}
fn("nwd")
var arr1 = [];
console.log(arr1.name)


实际应用:

求数组中的最大和最小值:

                
                let arr = [10,9,42,96,87]
		let max = Math.max.apply(null,arr)
		let min = Math.min.apply(null,arr)
		console.log(max) //96
		console.log(min) //9


利用call和apply实现继承

                
                function Person(name,sex) {
		    console.log(this) //Student {}
		    this.name = name
		    this.sex = sex
		    this.say = function(){
		        console.log("我的名字:" + this.name + "我的性别:" + this.sex)
		    }
		}
		function Student(){
		    Person.apply(this,arguments)
		}
		let student1 = new Student("飞旋","男")
		student1.say()


题目

       function Foo() {
            this.val = 1;
            console.log(this)
            this.bar = baz => {
                console.log(this)
                console.log(baz)
                return baz,this.val + baz
            }
        }
        var f = new Foo();
        console.log(f.bar.call({val:2},3)) //4



    function Foo() {
            this.val = 1;
            console.log(this)
            this.bar = function(baz) {
                console.log(this)
                console.log(baz)
                return baz,this.val + baz
            }
        }
        var f = new Foo();
        console.log(f.bar.call({val:2},3))//5


注意:这两道题目不仅考察了call改变this指向的问题 ,同时考察了箭头函数this指向的问题