zoukankan      html  css  js  c++  java
  • JavaScript学习笔记(十) call、apply、bind

    call、apply、bind 都是定义在函数原型上的,也就是说每个函数都能调用这些方法

    那么它们都有什么作用呢?它们之间存在什么异同呢?下面让我们一起来探讨一下

    1、call

    call 可以用于改变函数的执行环境,简单来说就是可以改变函数内部 this 的指向

    使用 call 可以让一个对象借用另外一个对象的方法,可以借此实现继承

    第一个传入的参数是上下文执行环境,即函数运行时 this 的指向,之后传入的参数将会直接传递给调用函数

    在 call 调用完成后,返回调用函数的返回值

    // 借用方法
    let apple = {
        color: 'red',
        getColor: function() { return this.color }
    }
    
    let banana = {
        color: 'yellow'
    }
    
    let color = apple.getColor.call(banana)
    console.log(color)
    
    /*
     * 执行结果:
     * yellow
    **/
    
    // 实现继承
    function Parent(age, name) {
        this.age = age
        this.name = name
        this.getName = function() { return this.name }
        this.setName = function(name) { this.name = name }
    }
    
    function Child(age, name) {
        Parent.call(this, age, name)
    }
    
    let child = new Child(18, 'Peter')
    child.setName('Steve')
    let name = child.getName()
    console.log(name)
    
    /*
     * 执行结果:
     * Steve
    **/
    

    2、apply

    apply 的作用与 call 完全一样,都能用于改变函数的执行环境,两者的区别仅仅在于传入的参数

    第一个参数传入的都是上下文执行环境,即函数运行时 this 的指向,参数的区别在于之后传入的参数

    之后传入的参数是调用函数执行所需的参数,call 是按照顺序直接传入,而 apply 是将参数放在数组中再传入

    // 判断类型
    let number = 0
    let string = ''
    let boolean = true
    let object = {}
    let array = []
    
    function typeOf(value) {
        return Object.prototype.toString.apply(value).slice(8, -1)
    }
    
    console.log(typeOf(number))
    console.log(typeOf(string))
    console.log(typeOf(boolean))
    console.log(typeOf(object))
    console.log(typeOf(array))
    
    /*
     * 执行结果:
     * Number
     * String
     * Boolean
     * Object
     * Array
    **/
    
    // 数值求和
    function addNumber() {
        let isNumber = function(value) { return typeof value === 'number' }
        let numbers = Array.prototype.filter.apply(arguments, [isNumber])
        let sum = numbers.reduce(function(prev, curr) {
            return prev + curr
        })
        return sum
    }
    
    let result = addNumber(1, 'a', 2, 'b', 3, 'c')
    console.log(result)
    
    /*
     * 执行结果:
     * 6
    **/
    

    3、bind

    传入 bind 的参数与 call 完全相同,作用也与 call 大致一样,但它们还是有所区别的

    call 在调用后马上执行函数,bind 不会,调用 bind 返回一个改变了上下文的新函数,可以在需要的时候再调用

    // 借用方法
    let apple = {
        color: 'red',
        getColor: function() { return this.color }
    }
    
    let banana = {
        color: 'yellow'
    }
    
    let getColorForBanana = apple.getColor.bind(banana)
    console.log(getColorForBanana)
    let color = getColorForBanana()
    console.log(color)
    
    /*
     * 执行结果:
     * ƒ () { return this.color }
     * yellow
    **/
    
    // 解决回调函数 this 指向的问题
    let object = {
        value: 0,
        asyncPrint: function() {
            setTimeout(function() { console.log(this.value) }, 2000)
        },
        asyncPrintWithThat: function() {
            let that = this
            setTimeout(function() { console.log(that.value) }, 2000)
        },
        asyncPrintWithBind: function() {
            setTimeout(function() { console.log(this.value) }.bind(this), 2000)
        }
    }
    
    object.asyncPrint()
    object.asyncPrintWithThat()
    object.asyncPrintWithBind()
    
    /*
     * 执行结果:
     * undefined
     * 0
     * 0
    **/
    

    4、手动实现三个函数

    • call
    Function.prototype.myCall = function(cxt, ...params) {
        // 处理传入的上下文执行环境
        // 若为 null 或 undefined,则要转换成全局对象
        // 若为 原始值,也要转换成对应的实例对象
        const context = (cxt !== null && cxt !== undefined) ? Object(cxt) : window
        // 创建一个临时属性
        // 为了避免属性冲突,这里使用 Symbol 数据类型
        const property = Symbol('property')
        // 设置临时属性的值为调用函数
        context[property] = this
        // 通过对象方法调用函数,此时 this 指向 context,也就是指向了传入的上下文对象
        let result = context[property](...params)
        // 调用完成之后,删除方法,避免污染传入对象
        delete context[property]
        // 返回执行结果
        return result
    }
    
    • apply
    Function.prototype.myApply = function(cxt, arr) {
        // 处理传入的上下文执行环境
        const context = (cxt !== null && cxt !== undefined) ? Object(cxt) : window
        // 创建一个临时属性
        const property = Symbol('property')
        // 设置临时属性的值为调用函数
        context[property] = this
        // 声明执行结果
        let result = null
        // 用于检测传入的参数是否为类数组
        function isArrayLike(value) {
            if (value &&
                typeof value === 'object' &&
                isFinite(value.length) &&
                value.length >= 0 &&
                value.length === Math.floor(value.length) &&
                value.length < 4294967296) {
                return true
            } else {
                return false
            }
        }
        // 是否传入第二个参数
        if (arr) {
            // 第二个参数是否为类数组
            if (isArrayLike(arr)) {
                let params = Array.from(arr)
                result = context[property](...params)
            } else {
                throw new TypeError()
            }
        } else {
            result = context[property]()
        }
        // 调用完成之后,删除方法,避免污染传入对象
        delete context[property]
        // 返回执行结果
        return result
    }
    
    • bind
    Function.prototype.myBind = function(cxt, ...outerParams) {
        let self = this
        let bound = function(...innerParams) {
            // 判断绑定好的函数是否通过 new 调用
            // 如果通过 new 调用,则绑定到 this;否则就绑定到传入的上下文执行环境
            const context = (this instanceof bound) ? this : cxt
            // 通过 call 模拟实现
            return self.call(context, ...outerParams, ...innerParams)
        }
        // 使得绑定好的函数与调用 bind 的函数处于同一原型链上
        bound.prototype = Object.create(this.prototype)
        // 返回绑定好的函数
        return bound
    }
    

    【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记

    版权声明:本博客属于个人维护博客,未经博主允许不得转载其中文章。
  • 相关阅读:
    Springboot-Static-Resource
    Springboot-Listener
    Springboot--servlet 、filter
    java 面试-- java框架-mybaits
    SVN备份教程(二)
    SVN备份教程(一)
    深入浅出MongoDB(三)环境搭建
    关于在c#中引用外部dll文件,在页面中找不到命名空间
    在win8中如何实现下拉刷新的功能
    C# treeview控件部分节点添加checkbox
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/12180397.html
Copyright © 2011-2022 走看看