call和apply

call和apply的区别

call和apply的作用是一模一样的,区别在于传入参数的形式不同。

apply

apply接受两个参数,第一个指明函数体内this的指向,第二个参数是一个带下标的集合,可以是数组或者类数组。

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

arr作为参数一起传到func函数并输出。

call

call的传入的参数数量时不固定的,但是可以确定的是,第一个参数和apply一样,是函数体内的this指向。后面的参数会被依次传入函数。

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

我们可以看到,这两种方式最后的结果是一模一样的。但是为什么还要有这两种方法呢?这里就看我们是否知道要传入的参数的数量了,如果不知道,apply是更好地选择,apply的使用率也是更高的,但是如果我们知道,要传入的参数的数量,这时候用call就会更直观,可以直接一目了然的表达出形参和实参的对应关系。

其实call就是一颗包装在apply上面的语法糖。

当传入的第一个参数为null的时候,this的指向就是默认的宿主对象,在浏览器中就是全局对象window,但是如果使用严格模式,就是null

var func = function( a, b, c ){
    alert ( this === window ); // 输出 true
};
func.apply( null, [ 1, 2, 3 ] );

var func = function( a, b, c ){
    "use strict";
    alert ( this === null ); // 输出 true
}
func.apply( null, [ 1, 2, 3 ] );

当然,call和apply的作用不止于改变this的指向,我们还可以利用它来借用其他对象的方法。

Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ) // 输出: 5

Math.max()方法本来是要传入一组数,比较出这组数中最大的一个,用了apply后,我们就可以直接得到一个数组中最大的数了,当然,我们还有其他的办法实现,例如:

Math.max(...[1, 2, 5, 3, 4])

call和apply的用途

大概上我们可以总结的用途有三个,下面我们会一一来介绍这三种用途

改变this的指向

这是我们最先知道的用途,但是真的理解会用了吗?我们先来看个例子:

var obj1 = {
    name: 'sven'
};
var obj2 = {
    name: 'anne'
};
window.name = 'window';
var getName = function(){
    alert ( this.name );
};
getName(); // 输出: window
getName.call( obj1 ); // 输出: sven
getName.call( obj2 ); // 输出: anne

这个例子很简单,三次调用分别使用了三个不同的this,清晰明了。
在上一章的this中,我们讨论过一个关于this指向改变的问题,就是document.getElementById,当我们通过这个方法获取到这个对象,并触发这个dom的点击事件时,事件内部的this指向是dom本身

document.getElementById( 'div1' ).onclick = function(){
    alert( this.id ); // 输出: div1
};

但是,如果事件内部,在定义一个函数,像这样:

document.getElementById( 'div1' ).onclick = function(){
    alert( this.id ); // 输出: div1
    var func = function(){
        alert ( this.id ); // 输出: undefined
    }
    func();
};

内部的func函数的this不再是dom节点了,变成了全局对象window,这不是我们想要的结果,怎么来改变这个问题呢?这里就可以用call或者apply了

document.getElementById( 'div1' ).onclick = function(){
    var func = function(){
        alert ( this.id ); // 输出: div1
    }
    func.call( this );
};

再来看看上一节中,我们是怎么处理的:

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

var getId = document.getElementById;
var div = getId( 'div1' );
alert ( div.id ); // 输出: div1
Function.prototype.bind

现代大部分的浏览器都支持Function.prototype.bind,这个函数的作用是创建一个新函数,并指定这个函数的this指向。下面是mdn中对该函数的描述:

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

下面我们来看一下这个函数是怎么实现的:

Function.prototype.bind = function( context ){
    var self = this; // 保存原函数
    return function(){ // 返回一个新的函数
    return self.apply( context, arguments ); // 执行新的函数的时候,会把之前传入的 context
                                            // 当作新函数体内的 this
    }
};
var obj = {
    name: 'sven'
};
var func = function(){
    alert ( this.name ); // 输出: sven
}.bind( obj);
func()

这是一个简化版的bind实现,真实的bind肯定要比这个复杂的

借用其他对象的方法

借用方法也是call和apply应用很广泛的用途,下面我们来看第一种场景’借用构造函数‘

var A = function( name ){
    this.name = name;
};
var B = function(){
    A.apply( this, arguments );
};
B.prototype.getName = function(){
    return this.name;
};
var b = new B( 'sven' );
console.log( b.getName() ); // 输出: 'sven'

这是一个很简单的实现,B借用了A的构造函数,这就相当于实现了一些类似继承的效果。

第二种场景在平常编码中就用到很多次了,就是类数组去借用数组的方法。

(function(){
    Array.prototype.push.call( arguments, 3 );
    console.log ( arguments ); // 输出[1,2,3]
})( 1, 2 );

arguments是一个类数组元素,通过call方法,我们调用了数组的push方法,为arguments对象插入了一个新的元素,这样的例子很多,我们就不一一去看了。

但是我们需要知道很多对象都可以借用其他对象的方法的,比如例子中的push方法,只要满足两个要求的对象都可以调用这个方法,这两个要求就是:
- 对象本身要可以存取属性;
- 对象的 length 属性可读写。

通过这两个要求我们可以知道,number类型是无法借用push方法的,因为number没有存取属性,还有就是函数不可以,因为函数的length是只读的。

GitHub博客地址

本项目是一个基于SSM(Spring+SpringMVC+MyBatis)后端框架与Vue.js前端框架开发的疫情居家办公系统。该系统旨在为居家办公的员工提供一个高效、便捷的工作环境,同时帮助企业更好地管理远程工作流程。项目包含了完整的数据库设计、前后端代码实现以及详细的文档说明,非常适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 系统的核心功能包括用户管理、任务分配、进度跟踪、文件共享和在线沟通等。用户管理模块允许管理员创建和管理用户账户,分配不同的权限。任务分配模块使项目经理能够轻松地分配任务给团队成员,并设置截止日期。进度跟踪模块允许员工实时更新他们的工作状态,确保项目按计划进行。文件共享模块提供了一个安全的平台,让团队成员可以共享和协作处理文档。在线沟通模块则支持即时消息和视频会议,以增强团队之间的沟通效率。 技术栈方面,后端采用了Spring框架来管理业务逻辑,SpringMVC用于构建Web应用程序,MyBatis作为ORM框架简化数据库操作。前端则使用Vue.js来实现动态用户界面,搭配Vue Router进行页面导航,以及Vuex进行状态管理。数据库选用MySQL,确保数据的安全性和可靠性。 该项目不仅提供了一个完整的技术实现示例,还为开发者留下了扩展和改进的空间,可以根据实际需求添加新功能或优化现有功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值