js之一步步手写bind

1,003 阅读3分钟

bind介绍

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用

参看官网Function.prototype.bind()

bind特性总结:

  1. 函数A调用bind方法时,返回新的函数B
  2. 函数B在执行时,具体功能实际上还是A,只不过this指向bind()中第一个参数,不传的话是window
  3. 函数A调用bind方法时,可以传递参数o, x, y, z...
  4. 函数B在执行的时候,传递的参数会拼接到 x, y, z的后面 一并在内部传给A
  5. new B()构造函数依旧是A,而且o不会起任何作用

手写代码1.0

实现1、2两点

<script>
Function.prototype.newBind = function (targetObj) {
    var self = this;// this在这里指向的是目标函数
    var f = function () {
        // 真正执行的是self,也就是外面的this
        // 这里面的this是window
        return self.apply(targetObj || window)
    }
    return f
}

function show () { 
    console.log("show函数被执行了")
    console.log(this)
}

var obj = { x: 20 }
var newShow = show.newBind(obj)
newShow()
// 输出
show函数被执行了
{ x: 20 }
</script>

手写代码2.0

在1.0基础上,加了特性3

// 2.0
Function.prototype.newBind = function (targetObj) {
    var self = this;// this在这里指向的是目标函数
    // console.log(arguments)
    // console.log(arguments[0] === targetObj) // true arguments[0]就是targetObj
    // 实际newBind时传入的参数是下标1以后的
    var args = [].slice.call(arguments, 1)
    var f = function () {
        // 真正执行的是self->this
        // 这里面的this是window
        return self.apply(targetObj || window, args)
    }
    return f
}


function show () { 
    console.log("show函数被执行了")
    console.log(this, arguments)
}

var obj = { x: 2 }
// 这里的
var newShow = show.newBind(obj, 3, 4)
newShow()
// 输出
show函数被执行了
{ x: 2 } [Arguments] { '0': 3, '1': 4 }

手写代码3.0

在2.0基础上,加了特性4

// 3.0
Function.prototype.newBind = function (targetObj) {
    var self = this;// this在这里指向的是目标函数
    // console.log(arguments)
    // console.log(arguments[0] === targetObj) // true arguments[0]就是targetObj
    // 实际newBind时传入的参数是下标1以后的
    var args = [].slice.call(arguments, 1)
    var f = function () {
        // 获取这个要返回函数的参数
        var _args = [].slice.call(arguments, 0) 
        // 真正执行的是self->this
        // 这里面的this是window
        return self.apply(targetObj || window, args.concat(_args))
    }
    return f
}

function show () { 
    console.log("show函数被执行了")
    console.log(this, arguments)
}

var obj = { x: 2 }
var newShow = show.newBind(obj, 3, 4)
newShow(5)
console.log(new newShow().constructor === show) // false
// 输出
show函数被执行了
{ x: 2 } [Arguments] { '0': 3, '1': 4, '2': 5 }
false

手写代码4.0

在3.0基础上,加了特性5,new B()的构造函数依旧是A

// 4.0
Function.prototype.newBind = function (targetObj) {
    var self = this;// this在这里指向的是目标函数
    // 实际newBind时传入的参数是下标1以后的
    var args = [].slice.call(arguments, 1)
    var temp = function () {}
    var f = function () {
        // 获取这个要返回函数的参数
        var _args = [].slice.call(arguments, 0) 
        return self.apply(targetObj || window, args.concat(_args))
    }
    temp.prototype = self.prototype
    f.prototype = new temp()
    return f
}

function show (x) { 
    console.log("show函数被执行了")
    this.x = x
    console.log(this, arguments)
}

var obj = { x: 2 }
var newShow = show.newBind(obj, 3)
var newShowObj = new newShow()
console.log(newShowObj.constructor === show) // true
console.log(newShowObj.x)  // undefined
// 输出
show函数被执行了
{ x: 3 } [Arguments] { '0': 3 }
true
undefined

这里有一个问题,就是newShowObj.x是undefined。

这是因为show函数中的this.x在实际执行的时候,this指向targetObj || window,

所以得对创建实例对象这种情况特殊处理。

手写代码5.0

只改动4.0中的第14行代码

// 5.0
Function.prototype.newBind = function (targetObj) {
    var self = this;// this在这里指向的是目标函数
    // console.log(arguments)
    // console.log(arguments[0] === targetObj) // true arguments[0]就是targetObj
    // 实际newBind时传入的参数是下标1以后的
    var args = [].slice.call(arguments, 1)
    var temp = function () {}
    var f = function () {
        // 获取这个要返回函数的参数
        var _args = [].slice.call(arguments, 0) 
        // 真正执行的是self->this
        // 这里面的this是window
        return self.apply(this instanceof temp ? this : (targetObj || window), args.concat(_args))
    }
    temp.prototype = self.prototype
    f.prototype = new temp()
    return f
}

function show (x) { 
    console.log("show函数被执行了")
    this.x = x
    console.log(this, arguments)
}

var obj = { x: 2 }
var newShow = show.newBind(obj, 3)
var newShowObj = new newShow()
console.log(newShowObj.constructor === show) // true
console.log(newShowObj.x)

// 输出
show函数被执行了
show { x: 3 } [Arguments] { '0': 3 }
true
3

补充

①__proto__和constructor属性是对象所独有的;

② prototype属性是函数所独有的。

constructor属性也是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来)

参考文章:blog.csdn.net/cc188688768…

本人前端小菜鸟一只,如有错误或疏漏,欢迎评论交流!