new bind call apply的手写

new

new一个函数时,其内部语句其实为this(fn)._proto_ = Fn.prototype。
例:

function Fn() {}    //  内部语句:this.prototype = {}
Fn.prototype.test= function() {
  console.log(test)
}
let fn  = new Fn()  //  内部语句:this(fn).<u>proto</ u>= Fn.prototype
fn.test() 

实现:

function myNew() {
    let construct = [].shift.call(arguments)
    // ...省去若干的判断条件
    // let obj = {}
    // obj.__proto__ = construct.prototype
    let obj = Object.create(construct.prototype)  // 创建obj的__proto__ = construct.prototype
    const result = construct.apply(obj, arguments)

    return typeof result === 'object' ? result || obj : obj  // 匹配函数return出一个对象和null
}

function A(){ }
A.prototype.get = function() {
    console.log('get')
}
function B(){
    return {
        b: '1'
    }
}

let aa = myNew(A)
let bb = myNew(B)
console.log(aa)

call,apply

我们知道call和apply改变this的指向,并执行函数。
例:

let obj = {
    name: 'obj',
    getName: function() {
        console.log(this.name)
    }
}
let obj2 = {
    name: 'obj2'
}

obj.getName.call(obj2)

其实就是把obj下的getName方法复制到obj2下,运行完后删除。
实现:

Object.prototype.myCall = function (context, ...args) {
    // ...省去若干的判断条件
    context = context || window
    const key = Symbol()
    context[key] = this

    let result = context[key](...args)
    delete context[key]

    return result
}
obj.getName.myCall(obj2)

apply这是在传参上是数组,其原理实现更call 一样。

Object.prototype.myApply = function (context, ...args) {
    // ...省去若干的判断条件
    context = context || window
    args = args || []
    const key = Symbol()
    context[key] = this

    let result = context[key](...args)
    delete context[key]

    return result
}

bind

bind改变this指向,返回的是一个函数,我们在自己去执行它。
例:

let obj = {
    name: 'obj',
    getName: function(a, b) {
        console.log(a, b)
        console.log(this.name)
    }
}
let obj2 = {
    name: 'obj2'
}

let fn = obj.getName.bind(obj2,'1')
fn('2')
let test = new fn('3')

可以看到使用函数柯里化处理传参,并返回一个未执行的函数。我们执行时改变this指向,使用上例call或apply就可以了。当然这里需考虑new的情况
实现:

Object.prototype.myBind = function (context, ...args) {
    // ...省去若干的判断条件
    const fn = this
    args = args || []

    let newFn = function(...newArgs) {
        if (this instanceof newFn) {  // 遇到new
            fn.apply(fn, [...args, ...newArgs])
        } else {
            fn.apply(context, [...args, ...newArgs])
        }
    }
    // 针对创建实例做处理,此时newFn函数有自己的原型,我们需使它能够继承绑定函数的原型
    newFn.prototype = fn.prototype
    return newFn
}

let test = obj.getName.myBind(obj2, '1')
let dd = new test('2')
test('3')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。