当前位置: 首页 > news >正文

bind,apply,call,caller,callee还傻傻分不清楚?

先介绍每个的语法:

1. bind()

语法:fn.bind(thisObj[, arg1[, arg2[, ...]]])

fn:是想要改变this指向的函数

thisObj:表示fn中this指针指向的新对象。可选。

作用:改变this指向,并返回一个函数。

后面的参数:看似复杂,实际上就相当于一个一个列出来,即fn.bind(thisObj, arg1, arg2, ...)。参数也可以为单独的数组,例如:fn.bind( thisObj,1,2,[3,34] );。可选

注意: 这个方式返回的是函数,并不像call和apply一样直接执行完成。要想执行必须后面再用 () 括号调用。这个方法在ie6-8不支持。并且ie9不支持严格模式

     bind()也可以这样使用:

var newFn=fn.bind(thisObj);
newFn(arg1,arg2, ...);

    这样每次调用newFn,这个函数里的this都是指向的是thisObj。

 

2. apply()

语法:fn.apply(thisObj, [argsArray])

fn:是想要改变this指向的函数

thisObj:表示fn中this指针指向的新对象。可选

后面的参数:只能是一个数组。可选

注意:如果thisObj不指定或者thisObj为null或者undefined,那么thisObj默认为window对象。如果thisObj为字符串,数字,布尔值这些原始数据类型(这里不包括null和undefined)。那么此时的this会指向改原始数据类型包装的对象。例如:

var obj={
    say:function(){
        console.log(this);
    }
}
obj.say.apply('123')

  执行之后,this的值为:

  其它的可以自行去举例体会。

 

3. call()

语法:fn.call(thisObj, arg1, arg2, ...)

fn:是想要改变this指向的函数

thisObj:表示fn中this指针指向的新对象。可选

后面的参数:一个一个列出来。可选

注意:thisObj的注意事项和上面apply的一样。apply与call的区别就是接收的参数的区别,call是一个一个列出来,apply是一个数组。

 

当参数确定时可以考虑用call,如果参数不确定时建议用apply。如果想多次使用改变this指针指向的函数,可考虑用bind

前三个方法的作用都是为了改变函数里this指针的指向

关于不同情况下this指针指向,请看我的这篇文章:深入理解this指针指向。

 

4. caller()

作用:返回调用指定函数的函数

语法:fn.caller()

注意:如果一个函数fn是在全局作用域内被调用的,则fn.caller为null。相反,如果一个函数fn是在另外一个函数作用域内被调用的,则fn.caller指向调用它的那个函数。而且caller只有在函数执行时才有效

     该特性是非标准特性,请尽量不要在生产环境中使用它。这是MDN上说的,但是caller被所有主流浏览器兼容

例子

function add(){
    function bdd(){
        console.log(bdd.caller)
    }
    bdd();
  console.log(add.caller); } add()

     最后输出的结果是:

  因为add这个函数是在全局作用于内被调用,所以是null,而bdd是在add函数中被调用,所以是整个add函数。

 

上面四个都是继承至Function。

下面的callee是函数参数集合arguments里的一个属性

 

5. callee

作用:用于引用该函数的函数体内当前正在执行的函数

语法:arguments.callee

注意:在es5的严格模式下arguments.callee被删除了。但是普通模式下兼容性挺好的。

 例子:

function add(){
    function bdd(){
        console.log(arguments.callee)
    }
    bdd();
    console.log(arguments.callee)
}
add()

  结果分别是:

  第一个console中,当前正在执行的函数是bdd,所以显示出来是bdd函数。第二个console,当前正在执行的是add,所以是add函数。

 


 

下面是例子:

call、apply:

var obj = {age:10};
function fn() {
    console.log(this);
    console.log(this.age);
}
fn();
fn.call(obj);
fn.apply(obj);

来分析一下:

(1) 当执行到fn()时,fn是window直接调用,是全局函数,所以此时this指向的是window。因此this.age是undefined

(2) 当执行到fn.call(obj)时,call改变了fn函数中this的指向,此时this指向了obj,所以此时this.age是10;

(3) 这里apply和call没区别,所以结果也是一样。

 

call、apply妙用:

(1) 求数组中的最大,最小值(number类型)

var num=[8,99,1,46,13,12,11,19,26];
var max=Math.max.apply(null,num);//99
var min=Math.min.apply(null,num);//1

 (2) 类似数组的contact方法:

var arr1 = ["a", "777","b","c"];
var arr2 = ["n", "m", 77];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1);//["a", "777", "b", "c", "n", "m", 77]

 

再来一道简单的面试题:

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        console.log('1.' + this.name);//第一个console
        return function(){
            return this.name;
        };
    }
};
var func = object.getNameFunc();
console.log('2.' + func());//第二个console
console.log('3.' + func.call(object));//第三个console
console.log('4.' + func.apply(object));//第四个console

  我们来分析一下这道题:

(1) 当执行到 var func = object.getNameFunc(); 这句代码时,会执行第一个console,这时候,getNameFunc()函数由于是通过object直接调用,那么这个函数中的this就是指向的object,所以第一个console输出的是  1. My  Object   。然后这个函数返回了一个匿名函数。

(2) 当执行到第二个console时,由于上一句执行完之后,func是一个function,但还未执行,this指向不明确。这时候执行这个函数时,func是通过window直接调用,所以,此时func函数中的this指向的是window。所以第二个语句的输出是 2. The  Window。

(3) 执行到第三个console时,本来func中的this是指向window,但是通过call,改变了这个this的指向,call(object)告诉我们这时候,this指向的是object。所以第三个console输出的是  3. My  Object

(4) 由于call和apply只有传入参数的区别,所以这里的call和apply是一样的效果,所以这里func这里的this指向的还是object。所以第四个console输出的是  4. My  Object

 

bind:

var a ={
     name : "bajie",
     fn : function (a,b) {
      console.log(this.name); console.log( a
+ b); } } var b = a.fn.bind(a); b(1,2);

 又来分析分析:

(1)当执行到 var b = a.fn.bind(a); 的时候,这时的b就是通过bind函数返回的一个函数,而且这时b函数里的this指向的是a这个对象。

(2) 当调用b函数时,这时有人可能会说直接调用b的是window,所以这时this应该指向window。但是实际上,b函数里的this已经通过bind修改成指向a了,所以这里不管执行多少次,this都还是指向a。所以最终执行后结果是bajie   3;

(3) 这里b函数也可以使用 window.b(1,2); 这样使用,结果还是一样的。使用了bind()之后并没有受到window的影响!!!这里需要注意。。。

 

转载于:https://www.cnblogs.com/zjjDaily/p/9494714.html

相关文章:

  • linux常用命令三
  • 作业4
  • 文件比较命令(comp)
  • lodash的一些实用的方法 TODO
  • UVA11853-Paintball(对偶图)
  • vue版 文字滚动
  • 面试题:合并两个排序的链表
  • .NET CORE 第一节 创建基本的 asp.net core
  • 3ds Max学习日记(九)
  • 【Linux】time+dd测试硬盘读写速度
  • [洛谷P2801]教主的魔法
  • 共享服务-FTP基础(一)
  • ZYNQ的Linux Linaro系统镜像制作SD卡启动
  • intellij idea 编译 kafka 源码
  • 杂项-Java:Druod Monitor
  • 时间复杂度分析经典问题——最大子序列和
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CentOS从零开始部署Nodejs项目
  • es6(二):字符串的扩展
  • Iterator 和 for...of 循环
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Java小白进阶笔记(3)-初级面向对象
  • MQ框架的比较
  • nfs客户端进程变D,延伸linux的lock
  • Python打包系统简单入门
  • Web设计流程优化:网页效果图设计新思路
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 入门级的git使用指北
  • 手写一个CommonJS打包工具(一)
  • 数据可视化之 Sankey 桑基图的实现
  • 微服务框架lagom
  • 栈实现走出迷宫(C++)
  • 2017年360最后一道编程题
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ​HTTP与HTTPS:网络通信的安全卫士
  • #android不同版本废弃api,新api。
  • #pragma once
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • $.ajax()方法详解
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (java)关于Thread的挂起和恢复
  • (九十四)函数和二维数组
  • (十六)一篇文章学会Java的常用API
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • @DataRedisTest测试redis从未如此丝滑
  • [20150321]索引空块的问题.txt
  • [Angularjs]ng-select和ng-options