资讯专栏INFORMATION COLUMN

call、bind的模拟实现

mmy123456 / 745人阅读

摘要:大致就是这样所以可以模拟实现和。这些参数作为的第二个参数跟在后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。

一个前端知识点汇总

综合了学习过程中的知识点,比如this、闭包、BFC、ES6等,如果大佬们觉得还可以的话,求个star啦!

call和apply

每个函数都包含两个非继承而来的方法:apply()和call()

用途相同,都是在特定的作用域中调用函数

接收参数方面不同,apply接收两个参数,一个是函数运行时的作用域(this),另一个是参数数组;call方法第一个参数与apply方法相同,但传递给函数的参数必须列举出来。

call()方法调用一个函数,其具有一个指定的this值和分别提供的参数:

fun.call(thisArg, arg1, arg2, ...)

apply()方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数:

fun.apply(thisArg, [argsArray])

举个栗子???:

var one = {
    name: "one",
    sayName: function(age) {
        console.log(`Hello, I"m ${this.name}, and I"m ${age} years old`)
    }
}
var day = {
    name: "day"
}
one.sayName.call(day, 20) // Hello, I"m day, and I"m 20 years old
one.sayName.apply(day, [20]) // Hello, I"m day, and I"m 20 years old

fn.call(o)的原理就是先通过o.m = fn将fn作为o的某个临时属性m存储,然后执行m,执行完毕后将m属性删除。

大致就是这样:

day.fn = one.sayName
day.fn()
delete day.fn

所以可以模拟实现apply和call。

首先来看apply的模拟:

第一版
Function.prototype.applyOne = function() {
    var context = arguments[0]
    var args = arguments[1]
    context.fn = this
    eval(context.fn(args.join(","))) // args.join(",")返回的是string,所以需要进行一下特殊处理)
    delete context.fn
}

one.sayName.applyOne(day, [20]) // Hello, I"m day, and I"m 20 years old
第二版

要注意到的是,若this传入的是null,或者不传入,则会默认是全局环境,并且apply是有返回值的。

Function.prototype.applyTwo = function() {
    var context = arguments[0] || window
    var args = arguments[1]
    context.fn = this

    if (args == void 0) {
        return context.fn()
    } 
    var result = eval(context.fn(args.join(","))) // args.join(",")返回的是string,所以需要进行一下特殊处理
    delete context.fn
    return result
}

var name = "oneday"

var one = {
    name: "one",
    sayName: function(age) {
        console.log(`Hello, I"m ${this.name}, and I"m ${age} years old`)
    }
}
var day = {
    name: "day"
}
one.sayName.applyTwo(null, [20]) // Hello, I"m oneday, and I"m 20 years old

emmmm...有一个问题就是万一context里面本来就有fn属性怎么办呢...对于es6而言,可以将fn设置为一个独特的Symbol值,如下:

var fn1 = Symbol("aaa")
var fn2 = Symbol("aaa")
fn1 == fn2 // false

但是毕竟symbol是es6的特性啊,所以在es5中可以使用产生随机数的方法,例如:

var fn1 = "o" + Math.random()
var fn2 = "o" + Math.random()

接下来就是apply:

Function.prototype.callOne = function() {
    var context = [].shift.applyTwo(arguments)
    var args = [].slice.applyTwo(arguments) // 将剩下的参数作为数组传入啊
    return this.applyTwo(context, args)
}

emmmm...第一个参数就是arguments的第一个(一般是this,或者没有),后面的参数就作为数组形式传入。

bind方法

bind方法创建一个新的函数,当被调用时,this值是传递给bind的第一个参数,它的参数是bind其他的参数和其原本的参数,返回的是由指定的this值和初始化参数改造的原函数拷贝。

fun.bind(thisArg[, arg1[, arg2[, ...]]])

实例:

var name = "2333"
function Person(name) {
    this.name = name
    this.sayName = function() {
        setTimeout(function() {
            console.log(`Hello, I"m ${this.name}`)
        }, 1000)
    }
}

var oneday = new Person("1111")
oneday.sayName() // Hello, I"m 2333

但是下面这样就是1111~

this.sayName = function() {
    setTimeout(function() {
        console.log(`Hello, I"m ${this.name}`)
    }.bind(this), 1000)
}

var oneday = new Person("1111")
oneday.sayName() // Hello, I"m 1111

而且还有偏函数(Partial Functions),在mdn中是这么说的:

bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数作为bind()的第二个参数跟在this后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。

emmmm...对呀没看懂,于是就看例子啊...

function list() {
    return Array.prototype.slice.call(arguments)
}

var list1 = list(1, 2, 3) // [1, 2, 3]

// 所以listFun是拥有预设参数(5, 6)的,作为预设参数跟在第一个参数this后面
var listFun = list.bind(undefined, 5, 6)

// 后面传入的参数会跟在预设参数的后面
var list2 = listFun(7) // [5, 6, 7]
var list3 = listFun(8, 9) // [5, 6, 8, 9]

bind的模拟实现:

第一版
Function.prototype.bindOne = function() {
    var me = this // 缓存this
    var argArray = Array.prototype.slice.call(arguments, 1)
    return function() {
        return me.apply(arguments[0], argArray)
    }
}

但是上述的没有实现继续传参可以添加到原参数后的功能...所以有了第二版

第二版
Function.prototype.bindTwo = function() {
    var context = arguments[0]
    var me = this
    var argArray = Array.prototype.slice.call(arguments, 1)
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments)
        var finalArgs = argArray.concat(innerArgs)
        return me.apply(context, finalArgs)
    }
}

bind返回的函数如果作为构造函数,这个构造函数搭配new关键字出现的话,bind绑定的this需要被忽略,但是参数还要继续传入。意思就是bind的绑定比new的优先级要低。而且要在函数体内判断调用bind方法的一定要是一个函数。

复习一下new的作用:

创建一个新对象

新对象继承了该函数的原型(因此this就指向了这个新对象)

为这个新对象添加属性和方法并返回这个新对象

var obj = {}
obj.__proto__ = Base.prototype
Base.call(obj)
第三版
Function.prototype.bindThree = function() {
    if (typeof this !== "function") {
        throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable")
    }

    // context指要把this绑定到的目标函数
    var context = arguments[0]

    // 这里的this指向调用bind的函数
    var me = this
    var argArray = Array.prototype.slice.call(arguments, 1)

    var F = function() {}
    F.prototype = this.prototype
    var bound = function() {
        var innerArgs = Array.prototype.slice.call(arguments)
        var finalArgs = argArray.concat(innerArgs)

        // 如果调用bind的函数是F的实例,那么this就还是指向调用bind的函数,如果不是F的实例,那么this就进行改变
        return me.apply(this instanceof F ? this : context, finalArgs)
    }
    bound.prototype = new F()
    return bound
}
参考:

不用call和apply方法模拟实现ES5的bind方法

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92401.html

相关文章

  • JavaScript深入之bind模拟实现

    摘要:也就是说当返回的函数作为构造函数的时候,时指定的值会失效,但传入的参数依然生效。构造函数效果的优化实现但是在这个写法中,我们直接将,我们直接修改的时候,也会直接修改函数的。 JavaScript深入系列第十一篇,通过bind函数的模拟实现,带大家真正了解bind的特性 bind 一句话介绍 bind: bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数...

    FingerLiu 评论0 收藏0
  • JavaScript进阶之模拟call,apply和bind

    摘要:模拟和模拟一样,现摘抄下面的代码添加一个返回值对象然后我们定义一个函数,如果执行下面的代码能够返回和函数一样的值,就达到我们的目的。 原文:https://zhehuaxuan.github.io/... 作者:zhehuaxuan 目的 本文主要用于理解和掌握call,apply和bind的使用和原理,本文适用于对它们的用法不是很熟悉,或者想搞清楚它们原理的童鞋。 好,那我们开始...

    CoderBear 评论0 收藏0
  • 【进阶3-4期】深度解析bind原理、使用场景及模拟实现

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器,提供的值被忽略,同时调用时的参数被提供给模拟函数。 bind() bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。bind返回的绑定函数也能使用 n...

    guyan0319 评论0 收藏0
  • 面试官问:能否模拟实现JSbind方法

    摘要:点击那么面试官可能会问是否想过到底做了什么,怎么模拟实现呢。另外前不久写过一篇文章面试官问能否模拟实现的操作符。所以相当于调用时,的返回值函数内部要模拟实现实现的操作。文章中的例子和测试代码放在中模拟实现。 前言 用过React的同学都知道,经常会使用bind来绑定this。 import React, { Component } from react; class TodoItem ...

    Julylovin 评论0 收藏0
  • JavaScript专题之模拟实现bind

    摘要:但是三作为构造函数时函数其实还有一个非常重要的特点返回的函数如果作为构造函数,搭配关键字出现的话,我们的绑定就需要被忽略。其次,当返回的函数作为构造函数时,之前绑定的会失效。 本文共 1100 字,读完只需 4 分钟 概述 前一篇文章我们尝试模拟实现了 call 和 apply 方法,其实 bind 函数也可以用来改变 this 的指向。bind 和 call和 apply 两者的区别...

    刘明 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<