专栏首页一Li小麦js设计模式补白之 this/call和apply

js设计模式补白之 this/call和apply

this

this指向一个动态的函数执行环境。

在一个普通的对象中,this的作用次域当然是对象本身

在前端最常用的浏览器中,alert(this)的结果是window。

var name='dangjingtao'

        var obj={
            name:'djtao',
            getName:function(){
                return this.name
            }
        }

        console.log(obj.getName()) //djtao
        var a=obj.getName; //抽离了使用环境
        console.log(a()) //dangjingtao

现在来研究一个业务中常见的问题。

<a id="hit-me" href="javascript:;">hit me</a>

    <script>
        document.getElementById('hit-me').onclick=function(){
            alert(this.id);//hit-me

            var callback=function(){
                alert(this.id)//undefined
            }

            callback();
        }
    </script>

处理方法是用一个变量存下 this

document.getElementById('hit-me').onclick=function(){
            alert(this.id);//hit-me

            var _this=this;

            var callback=function(_){
                alert(_this.id)//undefined
            }

            callback();
        }

call用于绑定当前作用于

var name='dangjingtao';

        var obj={
            name:'djtao',
            getName:function(){
                return this.name
            }
        }

        alert(obj.getName.call(this)); //dangjingtao

这里call(this)显然是把当前的作用域(window)绑定给了getName方法。

遗失的this

写一个简单的js选择器吧,比如 document.getElementById('div1'),实在太长了。

var getId=document.getElementById
    console.log(getId('div1'))

运行报错。因为用getId引用了之后,getElementById失去了this。

如何既能调用方法,又不丢失this?干货在于

document.getElementById=(function(func){
        return function(){
            return func.apply(document,arguments)
        }
    })(document.getElementById)

    var getId=document.getElementById
    console.log(getId('div1'))

call和apply

这两个方法非常重要。

调用apply方法的时候,第一个参数是this的指向 , 第二个参数是一个数组或类数组集合 。apply把这个集合作为参数传递给被apply的函数。

var func=function(a,b,c){
    alert([a,b,c]) //输出[1,2,3]
}

func.apply(null,[1,2,3])

call是apply的高级实现。当你知道参数数量时,也可以用call来写。比如 func.call(obj,'a','b','c')

非严格模式下,如果第一个参数为null,this还是指向默认的宿主。比如在浏览器中就是window。严格模式下则为null。

应用

如果认为,call和apply作用只局限于改变this的指向,那就错了。你还可以获取别的属性方法,绑定

来看看其它用法

访问一类对象的内部数据

大部分高级浏览器都实现了 Function.protoype.bind方法,用以绑定 this。学习上述知识,能否手写模拟一个bind方法?

Function.prototype.bind=function(ctx){
    var _this=this;//保存原函数

    return function(){
        return _this.apply(ctx,arguments)
    }
}

// 接下来试验一下
var name='dangjingtao'
var obj={
    name:'djtao'
}
var func=function(){
    return this.name
}.bind(obj)

console.log(obj) //控制台打印{name:'djtao'}
func() //弹出djtao

简单地说,我们在不为这个对象注册方法的前提下,为了一个对象实现了对内部私有变量的访问。

再丰富一点点

bind的简单实现并不足以满足功能。我想扩展bind方法并允许传入多个参数,并且还可以追加这个方法的参数。

可以这么做。

Function.prototype.bind=function(){
    var _this=this;//保存原函数
    var ctx=[].shift.call(arguments)//拿到第一个参数(也就要是绑定的this)
    var args=[].slice.call(arguments)//其他参数转化为数组
    //下面返回一个新函数
    return function(){
        //执行这个新函数的时候,会把ctx作为this
        //并且组合两次分别传入的参数,作为该函数的参数
        return _this.apply(ctx,[].concat.call(args,[].slice.call(arguments)))
    }
}

测试结果

var obj={
    name:'djtao'
}

var func=function(a,b,c,d){
    alert(this.name) //djtao
    alert([a,b,c,d]) //输出1,2,3,4
}.bind(obj,1,2)

// 执行
func(3,4)

借用其他构造对象的方法

读书人的事,靠偷是不对的,应该是借。以下A是一个构造函数,拥有给某个人添加"是天才"后缀的处理过程。我要拿到A的属性。

var A=function(name){
    this.name=name+'是天才'
}

可以构造一个B对象来借

var B=function(){
    A.apply(this,arguments)
}

//然后写一个方法。
B.prototype.getName=function(){
    return this.name
}

var b=new B('djtao');
console.log(b.getName)//结果是 djtao是天才

再比如

arguments对象不是数组。但是我想把它们作为数组来处理,比如push。我想借用Array对象的push方法,怎么做?

(function(){
    Array.prototype.push.call(arguments,3);
    console.log(arguments)
})(0,1,2)
// 执行结果打印出1,2,3

更骚的操作是给对象也加个push方法

var a={}

    Array.prototype.push.call(a,'0')

    Array.prototype.push.call(a,'第一个元素');
    Array.prototype.push.call(a,'第二个元素')

    console.log(a)

只要对象的属性可读写,length属性可读写,那就可以把push方法用到上面去。

本文分享自微信公众号 - 一Li小麦(gh_c88159ec1309),作者:djtao89

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-05-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 手撸vuex和vue-router

    把这个store返回出去,那就写完了,核心原理可以说是异常简单。 就用官方文档的案例验证下这个duex有多靠谱:

    一粒小麦
  • react实战:umi问卷发布系统

    技术团队中,保持技术分享和持续的学习是完全必要的。企业主会说:"公司不是培训机构。"这固然正确。但一个公司,总会遇到这种或那种需要攻关的难题。当你不愿意分享解决...

    一粒小麦
  • 基于react的H5音频播放器

    项目是基于React,镶嵌在页面。为此开发了组件audio.js。不过不管什么框架。逻辑都是一样的。

    一粒小麦
  • this,call,apply,bind(万字长文)

    大家好啊,我是吒儿?,每天努力一点点?,就能升职加薪?当上总经理出任CEO迎娶白富美走上人生巅峰?,想想还有点小激动呢?。

    达达前端
  • Javascript快速入门(上篇)

    Javascript的熟练之路,小弟来了。 ? JavaScript简介:JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,...

    用户1216676
  • JavaScript学习总结(五)

    之前的几讲中我们曾经说过,JavaScript中是没有类的概念的。但是我们讲过对象,那么这个对象是怎么来的呢? 只要有函数即可创建对象

    roobtyan
  • 原型和原型链

    function Foo(){} 相当于 var Foo = new Function(){}

    ConardLi
  • 前端AES的加密和解密

    在工作的过程中,经常要对一些数据做一些加密,当然有复杂的加密和简单的加密,也有对称加密等等。总之加密的方式有很多。今天在这里,我只是简单的分享一个我最近遇到的加...

    用户1174387
  • JavaScript组件设计思想

    上个周,并肩作战的田老师离职了,尽管在一起愉快玩耍的时间不到一年,自己仍然还是从其身上学到、体会到了好多关于知识、理想的东西。对于大多数年轻人关于“晚上想想千条...

    奋飛
  • 使用合适的设计模式一步步优化前端代码

    作者:晓飞 本文原创,转载请注明作者及出处 在后端语言中,设计模式应用的较为广泛。如Spring中常见的工厂模式、装饰者模式、单例模式、迭代器模式。但...

    iKcamp

扫码关注云+社区

领取腾讯云代金券