热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析:开发笔记中的this、call、bind和apply方法详解

篇首语:本文由编程笔记#小编为大家整理,主要介绍了深入理解thiscallbindapply相关的知识,希望对你有一定的参考价值。   首先看一道网易的面试题:var a = { a:"haha

篇首语:本文由编程笔记#小编为大家整理,主要介绍了深入理解thiscallbindapply相关的知识,希望对你有一定的参考价值。



  首先看一道网易的面试题:


var a = {
a:
"haha",
getA:function(){
console.log(
this.a);
}
}
var b = {
a:
"hello"
}
var getA = a.getA;
var getA2 = getA.bind(a);
function run(fn){
fn();
}
//分别输出
a.getA();//haha
getA();//window下面的a对象
run(a.getA);//window下面的a对象
getA2.call(b);//haha

  这里考察了三个点:形参实参的理解、this的指向、call和bind对this指向的影响。

一、this指向问题

  关于this指向的问题,上一篇:理解Javascript里this关键字有比较好的总结。

  (1)这里有三种简单情况:

  1、如果函数中的this没有调用它的对象,那么this指向的就是window(注意:严格模式下这种情况的this会为空,即undefined

  2、如果函数中的this被不包含子对象的对象所调用,那么this指向的就是调用它的对象。

  3、如果函数中的this被包含多级对象的对象调用,this指向的也只是它上一级的对象,如下例:


var demoObj = {
a:
1,
b:{
fun:function(){
console.log(
this.a);
}
}
}
demoObj.b.fun();
//undefined,因为this指向demoObj.b,b里面没有a

  这里this不是指向demoObj对象,而是指向demoObj.b对象,这里找不到demoObj.b对象里的a,所以会输出undefined。 

  (2)还有三种特殊情况:

  1、还是上面的例子,改一下调用函数的方式,如下。


var demoObj = {
a:
1,
b:{
fun:function(){
console.log(
this.a);
}
}
}
var newFun = demoObj.b.fun;
newFun();
//undefined,this指向window

  这里还是得到undefined,但是this的指向却是window,这里的undefined是因为没找到window对象里的a,才输出的undefined。虽然函数fun是被对象b所调用,但是在将fun赋值给变量newFun的时候并没有执行,newFun的上级对象window,所以最终执行时指向的是window。

  2、构造函数用new实例对象时对this的影响。


function Fun(){
this.name = "haha";
}
var stu = new Fun();
console.log(stu.name);
//haha

  这里之所以对象stu.name可以输出haha,是因为new关键字就是创建一个对象实例,这个stu对象中包含了this.name这个属性,相当于复制但却没有执行。在执行时调用这个函数Fun的是对象stu,所以this指向的就是对象stu。

  用new操作符创建对象时发生的事情:

  第一步: 创建一个Object对象实例。
  第二步: 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
  第三步: 执行构造函数中的代码(这里的执行并不是真的让this指向哪里,而是为这个新对象添加属性)。
  第四步: 返回新生成的对象实例

  原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。

  3、有return的函数在new时对this的影响(正常的构造函数是没有return语句),我们先看下面的几个例子。


//例1
function Fun()
{
this.name = \'haha\';
return {};
}
var stu = new Fun();
console.log(stu.name);
//undefined,stu为{}


//例2
function Fun()
{
this.name = \'haha\';
return function(){};
}
var stu = new Fun();
console.log(stu.name);
//undefined


//例3
function Fun()
{
this.name = \'haha\';
return 123;
}
var stu = new Fun();
console.log(stu.name);
//haha,stu为Fun的实例


//例4
function Fun()
{
this.name = \'haha\';
return undefined;
}
var stu = new Fun();
console.log(stu.name);
//haha

  可以看出:如果return的是一个对象,那么this会指向返回的对象,如果return的不是一个对象,那么this还是指向函数的实例。

  但是return的是null时比较特殊。虽然null也是对象,但是this还是指向函数的实例。


//例5
function Fun()
{
this.name = \'haha\';
return null;
}
var stu = new Fun();
console.log(stu.name);
//haha

二、call、bind、apply对this指向的影响

  call和apply只有参数不同,这里就只讨论call,因为call和bind参数使用方法是一样的。

  1、call是动态的改变this的指向,即换个对象执行原对象方法的方法,并立即执行;

  2、bind是静态改变this的指向,并返回一个修改后的函数。

  就拿开始的题目最后一个输出来说:

  如果只是使用call的话:在执行到这两句时动态改变了this的指向,所以call(b)的输出hello,call(a)的输出haha。


getA.call(b);//hello
getA.call(a);//haha

  接下来看有bind影响的:


var getA2 = getA.bind(a);

  这里getA其实是a.getA,那么getA.bind(a)将this指向a,其实还是返回了a.getA函数赋值给了getA2。注意:其实函数没有变化,但是内部已经将this指向了a


getA2.call(b);//haha
//相当于a.getA.call(b);

  此时无论call里是a还是b,都会输出haha,因为内部的this已经被bind绑定指向bind里面的a了。怎么理解呢,看下面示例:


var getA2 = getA.bind(a);
var getA3 = getA.bind(b);
getA2.call(b);
//haha
getA3.call(b);//hello
var getA2 = getA.bind(a);
var getA3 = getA2.bind(b);
getA2.call(b);
//haha
getA3.call(b);//haha

  使用bind静态指定this:第一次使用bind后,this就被固定为bind的参数了,call、apply、bind均无法改变。

  如果new一下,this就是指向当前构造函数的实例;其他情况,this一直被静态绑定为a


var a = {
a:
"haha",
getA:function(){
this.c = "12345678";
console.log(
this,this.a);
}
}
var b = {
a:
"hello"
}
var getA = a.getA;
var getA2 = getA.bind(a);
var getA3 = getA2.bind(b);
var getObj = new getA3();//new一下,this就是当前构造函数的实例
getA3();//其他情况,this一直被静态绑定为a

  总的来说,call方法是在调用时改变this并立即执行这个函数,bind方法可以先改变函数中的this,之后对应的函数可以在需要的时候再调用。

三、call、bind、apply用法:

  1、fun.apply(context,[argsArray])

  立即调用fun,同时将fun函数原来的this指向传入的新context对象,实现同一个方法在不同对象上重复使用

  context:传入的对象,替代fun函数原来的this;

  argsArray:一个数组或者类数组对象,其中的数组参数会被展开作为单独的实参传给 fun 函数,需要注意参数的顺序。 

  2、fun.call(context,[arg1],[arg2],[…])

  同apply,只是参数列表不同,call的参数需要分开一个一个传入。如果不知道参数个数,则使用apply。

  使用:


Math.max() //只接收单独的参数,通过下面的方法可以在数组上面使用max方法:
Math.max.apply(null, array); //会将array数组参数展开成单独的参数再传入
Array.prototype.push.apply(arr1,arr2); //将一个数组拆开push到另一个数组中;不用apply则会将后续数组参数当成一个元素push进去。
Array.prototype.slice.call(arguments); //在类素组对象上使用slice方法

function isArray(obj){
return Object.prototype.toString.call(obj) === \'[object Array]\' ;
}
//验证是否是数组

  3、fun.bind(context,[arg1],[arg2],[…])

  使fun方法执行的context永不变。静态指定this

  arg1:要传递到新函数的参数列表

  返回一个函数供后续调用,其函数体和原函数fun一样,但新函数的this指向新传入的context对象。新函数会具有bind方法指定的初始参数arg1/arg2...,后续调用新函数时的实参要往已有参数的后面排。就是科里化方式,绑定默认的参数,后面的参数可以变化。

  使用:


//原来的函数有4个参数
var displayArgs = function (val1, val2, val3, val4) {
console.log(val1
+ " " + val2 + " " + val3 + " " + val4);
}
var emptyObject = {};
// 生成新函数时bind方法指定了2个参数,则新函数会带着这个两个实参
var displayArgs2 = displayArgs.bind(emptyObject, 12, "a");
// 调用时传入另2个参数,要在bind方法传入的2个实参后面
displayArgs2("b", "c");
// Output: 12 a b c

  bind的参数可以在执行的时候再次添加,但是要注意的是,参数需要按照形参的顺序添加


var demoObj = {
name:
"haha",
fun:function(a,b,c){
console.log(a,b,c);
}
}
var newFun = demoObj.fun;
var newFun2 = newFun.bind(demoObj,5);
newFun2(
7,9);//5,7,9

 



推荐阅读
  • 基于灰度直方图的水果识别系统开发:MATLAB源代码及图形用户界面设计
    基于灰度直方图的水果识别系统开发:MATLAB源代码及图形用户界面设计 ... [详细]
  • 定义Function类型:1functionsum(num1,num2){return num1+num2;}2varsumfunction(num1,num2){returnn ... [详细]
  • EasyUI作为一种高效的前端框架,显著简化了JavaScript代码的编写,提升了开发效率。在构建窗口应用程序时,首先需要引入EasyUI所需的JS文件和CSS样式表。由于EasyUI依赖于jQuery,因此还需确保正确加载jQuery库。通过这种方式,开发者能够快速实现界面组件的动态交互与美观布局,为用户提供更加流畅的使用体验。 ... [详细]
  • 一张思维导图带你梳理HashMap相关知识
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 技术分享:JavaScript博客园鼠标点击动态效果实现笔记
    技术分享:JavaScript博客园鼠标点击动态效果实现笔记 ... [详细]
  • 将 Eclipse 中的 Java Web 项目迁移至 IntelliJ IDEA 并配置 Tomcat 环境
    为了适应更高效的工作流程,本文详细介绍了如何将基于Eclipse构建的Java Web项目迁移到IntelliJ IDEA,并在新环境中配置Tomcat服务器,以确保项目的顺利运行。此过程不仅涉及项目文件的转移,还包括解决可能遇到的兼容性问题和环境配置挑战。通过本文的指导,开发者可以轻松实现从Eclipse到IntelliJ IDEA的过渡,提升开发效率。 ... [详细]
  • 在《JavaScript进阶之旅:第三阶段深入探索》中,我们将通过一系列复杂的代码示例,深入探讨JavaScript的高级特性与应用技巧。本阶段将重点讲解如何利用用户输入进行动态交互,例如通过提示框获取1到9之间的正整数,并基于此实现更多功能。此外,还将介绍如何优化代码结构,提升程序的可读性和维护性。 ... [详细]
  • 本文介绍了如何利用摄像头捕捉图像,并将捕获的图像数据保存为文件。通过详细的代码示例,展示了摄像头调用的具体实现方法,适用于多种应用场景,如安全监控、图像处理等。 ... [详细]
  • 校园活动综合管理平台
    本项目为上学期的Java大型作业,旨在开发一个校园活动综合管理平台。该平台主要实现了用户登录、活动查询等功能,能够有效提升校园活动的组织与管理效率。通过优化用户界面和增强系统功能,该项目为学生和教师提供了便捷的活动管理和参与体验。 ... [详细]
  • 如何在服务器后台运行PHP脚本?
    如何在服务器后台运行PHP脚本? ... [详细]
  • 深入探讨DOM对象的特性和应用
    1.逻辑运算 || && !1||2 5&&4  !0|| 遇到第一个为true的数字就终止并返回&&  遇到第一个为false的值就终止返回false的值如果没有false就返回 ... [详细]
  • 题目1:给定一个非空数组A,包含有N个整数,起始下标为0。数组包含有奇数个元素,其中除了唯一一个元素之外,其他 ... [详细]
  • 一.问题汇总:折线图问题与解决折线图中的多条折线,怎么设置?怎么设置echarts的背景颜色?怎么设置X轴,Y ... [详细]
  • 在Java中准确获取字符串长度的方法与技巧解析。首先,通过Eclipse创建一个新的Java项目,项目名称可自定义。完成后,右键点击项目名称,选择新建类。在类中,可以通过调用`String`对象的`length()`方法来统计字符串的长度。此外,还可以利用其他字符串处理库或工具类来实现更复杂的字符串长度计算,例如使用Apache Commons Lang库中的`StringUtils`类,以提高代码的可读性和健壮性。 ... [详细]
  • 在PB数据窗口中,错误处理技术主要针对两类问题进行优化:一是由用户不当数据输入引发的错误,二是程序执行过程中因代码缺陷导致的异常。高效的应用程序设计需确保无论出现哪种类型的错误,系统都能有效应对,保证稳定性和用户体验。通过引入先进的错误检测与恢复机制,可以显著提升系统的健壮性和可靠性。 ... [详细]
author-avatar
18岁的淡淡淡色彩
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有