首页IT科技javascript中call和apply方法的作用(call ,apply和bind方法 详解)

javascript中call和apply方法的作用(call ,apply和bind方法 详解)

时间2025-05-05 16:49:51分类IT科技浏览3009
导读:前言 call( 、apply( 和bind( 方法 三者作用都是...

前言

call()          、apply()和bind()方法 三者作用都是 改变this指向          。

本文旨在探讨三者之间的区别和作用             。

call, apply, bind 三者的区别在哪里 什么情况下用apply,什么情况下用call apply的其他巧妙用法(一般在什么情况下可以使用apply)

bind             、call    、apply都是用来指定一个函数内部的this的值         , 先看看bind          、call              、apply的用法

var year = 2021 function getDate(month, day) { return this.year + - + month + - + day } let obj = {year: 2022} getDate.call(null, 3, 8) //2021-3-8 getDate.call(obj, 3, 8) //2022-3-8 getDate.apply(obj, [6, 8]) //2022-6-8 getDate.bind(obj)(3, 8) //2022-3-8

一    、 call和apply

1. call() 方法

call()方法接受的语法和作用与apply()方法类似              ,只有一个区别就是call()接受的是一个参数列表    ,而apply()方法接受的是一个包含多个参数的数组    。

二者都是函数对象Function的方法     ,且第一个参数都是要绑定对象的上下文

例如: let obj = { a: 1, get: function(){ return 2 } } let g = obj.get g.call({},1,2,3) g.apply({},[1,2,3]) call方法调用父构造函数 function Product(name, price){ this.name = name; this.food = food; } // 调用父构造函数的call方法来实现继承 function Food(name, price){ Product.call(this.name, toy); this.category = food; } function Toy(name, price){ Product.call(this, name, price); this.category = toy; } var cheese = new Food(feta, 5); var fun = new Toy(robot, 40); call方法调用匿名函数 var animals = [ {species: Lion, name: King}, {species: Whale, name: Fail} ]; for(var i = 0; i < animals.length; i++){ (function(i){ this.print = function(){ console.log(# + i + + this.species + : + this.name); } this.print(); }).call(animals[i], i); //call调用匿名函数 } call方法调用函数并且指定上下文的this var obj = { animal: cats, sleepDuration: 12 and 16 hours }; function greet(){ var reply = [this.animal, typically sleep between, this.sleepDuration].join(); console.log(reply); } greet.call(obj); //"cats typically sleep between 12 and 16 hours" call方法调用函数并且不指定第一个参数(argument)

在这个例子中              ,我们没有传递第一个参数         ,this的值将被绑定为全局对象          。

var sData = marshall; function display(){ console.log("sDatas value is %s",this.sData); } display.call(); // sData value is marshall

但是在严格模式下     ,this 的值将会是undefined

var sData = marshall; function display(){ console.log("sDatas value is %s",this.sData); } display.call(); // Cannot read the property of sData of undefined

2. apply() 方法

使用 apply              , 我们可以只写一次这个方法然后在另一个对象中继承它         ,而不用在新对象中重复写该方法              。

apply 与 call() 非常相似,不同之处在于提供参数的方式    。apply 使用参数数组而不是一组参数列表     。apply 可以使用数组字面量(array literal)              ,如 fun.apply(this, [‘eat’, ‘bananas’])              ,或数组对象, 如 fun.apply(this, new Array(‘eat’, ‘bananas’))              。

apply方法调用一个具有给定this值的函数         ,以及以一个数组的形式提供参数         。 var array = [marshall,eminem]; var elements = [0,1,2]; array.push.apply(array,elements); console.log(array); //[marshall,eminem,0,1,2] 使用apply和内置函数

对于一些需要写循环以遍历数组各项的需求              ,我们可以用apply完成以避免循环     。

//找出数组中最大值和最小值 var numbers = [5, 6, 2, 3, 7]; //使用Math.min和Math.max以及apply函数时的代码 var max = Math.max.apply(null, numbers); var min = Math.min.apply(null, numbers);

上边这种调用apply的方法    ,有超出JavaScript引擎参数长度上限的风险              。

如果我们的参数数组非常大         ,推荐使用下边这种混合策略:将数组切块后循环传入目标方法 function minOfArray(arr) { var min = Infinity; var QUANTUM = 32768; for (var i = 0, len = arr.length; i < len; i += QUANTUM) { var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len))); min = Math.min(submin, min); } return min; } var min = minOfArray([5, 6, 2, 3, 7]);

3. apply与call的实现

具体代码如下:

// call和apply实现方式类似              ,只是传参的区别 // 基本思想是把fn.call(obj,args)中的fn赋值为obj的属性    ,然后调用obj.fn即可实现fn中this指向的改变 Function.prototype.myCall = function(context = window){ //myCall函数的参数     ,没有传参默认是指向window context.fn = this //为对象添加方法(this指向调用myCall的函数) let args = [...arguments].slice(1) // 剩余的参数 let res = context.fn(...args) // 调用该方法              ,该方法this指向context delete context.fn //删除添加的方法 return res } Function.prototype.myApply = function(context = window){ //myCall函数的参数         ,没有传参默认是指向window context.fn = this //为对象添加方法(this指向调用myCall的函数) let res if(arguments[1]){ //判断是否有第二个参数 res = context.fn(...arguments[1])// 调用该方法     ,该方法this指向context }else{ res = context.fn()// 调用该方法              ,该方法this指向context } delete context.fn //删除添加的方法 return res } // 验证 function sayName(name= wwx,age= 18){ this.name = name this.age = age console.log(this.name) return this.age } var obj = { name : zcf, age:24 } var age = sayName.myCall(obj,"wxxka",19) // 19 var age1 = sayName.myApply(obj,["wwxSSS",20]) //20

二     、bind

1. bind 简介

bind()函数会创建一个新的绑定函数         ,这个绑定函数包装了原函数的对象         。调用绑定函数通常会执行包装函数。

绑定函数内部属性: 包装的函数对象 在调用包装函数时始终作为this传递的值 在对包装函数做任何调用时都会优先用列表元素填充参数列表              。

而原函数 retrieveX 中的 this 并没有被改变,依旧指向全局对象 window             。

this.x = 9; //this指向全局的window对象 var module = { x: 81, getX: function(){return this.x;} }; console.log(module.getX()); //81 var retrieveX = module.getX; console.log(retrieveX()); //9,因为函数是在全局作用域中调用的 // 创建一个新函数              ,把this绑定到module对象 // 不要将全局变量 x 与 module 的属性 x 混淆 var boundGetX = retrieveX.bind(module); console.log(boundGetX()); //81

bind传递参数问题: 在通过bind改变this指向的时候所传入的参数会拼接在调用返回函数所传参数之前              ,多余参数不起作用。

var newShowName = showName.bind(newThis, hello); //在通过bind改变this指向的时候只传了“hello         ”一个参数, //在调用newShowName这个返回参数的时候         ,bind传参拼接在其前 newShowName(world); //输出:newThis hello world var newShowName = showName.bind(newThis, hello); //在通过bind改变this指向的时候只传了“hello              ”一个参数              , //在调用newShowName这个返回参数的时候    ,bind传参拼接在其前         , //这时newShowName的参数为“hello    ”              ,“a     ”    ,“world              ” //而该函数只需要两个参数     ,则第三个参数被忽略 newShowName(a,world); //输出:newThis hello a

bind传入的参数和newShowName方法传入的参数会拼接在一起              ,一齐传给showName方法          。

bind无法改变构造函数的this指向

var name = window; var newThis = { name: newThis }; function showName(info1, info2) { console.log(this.name, info1, info2); } showName(a, b); //输出:window a b // 通过bind改变this指向 var newShowName = showName.bind(newThis, hello,1,2); newShowName(a,world); //输出:newThis hello world console.log(new newShowName().constructor); //输出:showName函数体

可以看出         ,通过bind改变this指向返回函数的构造器还是最开始的showName函数             。

new newShowName()实例化了一个新的方法     ,这个方法的this也不再指向newThis    。

2. bind的实现

通过apply模拟bind源码实现:

Function.prototype.myBind = function(context = window){ let fn = this // 调用bind的函数 let args = [...arguments].slice(1) // myBind的参数 let bind = function(){ let args1 = [...arguments].slice() // bind的参数 return fn.apply(context,args.concat(args1)) } return bind } // 测试 var obj = { name : zcf, age:24 } function sayName(name= wwx,age= 18){ this.name = name this.age = age console.log(this.name) return this.age } var mb = sayName.myBind(obj) mb() // obj = {name:"wwx",age:18} mb("acfwwx",1819) // obj = {name:"acfwwx",age:1819} };

三              、 call ,apply 和bind方法应用

1. 什么情况下用apply,什么情况下用call

在给对象参数的情况下:

如果参数的形式是数组的时候              ,比如apply示例里面传递了参数arguments         ,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就可以采用 apply          。

如果我的Person的参数列表是这样的(age,name)              ,而Student的参数列表是(name,age,grade)              ,这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));

call方法:call(obj,x,y,z,…) apply方法:apply(obj,[x,y,z]) <script type="text/javascript"> /*定义一个人类*/ function Person(name,age) { this.name=name; this.age=age; } /*定义一个学生类*/ functionStudent(name,age,grade) { Person.apply(this,arguments); //Person.call(this,name,age); this.grade=grade; } //创建一个学生类 var student=new Student("zhangsan",21,"一年级"); //测试 alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade); //大家可以看到测试结果name:zhangsan age:21 grade:一年级 //学生类里面我没有给name和age属性赋值啊,为什么又存在这两个属性的值呢,这个就是apply的神奇之处. </script>

2. call和apply 应用场景

a. 函数之间的相互调用

function add(a,b){ alert(a+b); } function sub(a,b){ alert(a-b); } add.call(sub,5,6); add.apply(sub,[5,6]); //弹出11         ,对象替换              ,等等这不是函数吗?? 其实函数名是Function对象的引用              。

b. 构造函数之间的调用

function Person(){ this.age = 50; this.showAge= function(){ alert(this.age); } } function Son(){ this.age = 20; } // 让Son也具有Person的方法 // function Son(){ // this.age = 20; // Person.call(this); // //Person.apply(this) // } var father = new Person(); var xiaoming = new Son(); father.showAge.apply(xiaoming) //立即执行显示20 father.showAge.call(xiaoming) //立即执行显示20 xiaoming.showAge(); //报错    ,showAge() is not a function

c. 多重继承

使用多个call 或者apply 即可    。

场景1:找出一个数组的最大值或最小值         ,数组长度不确定

var arr = [1,2,3,.......n] Math.min.apply(this,arr) // this可随便换              ,但需是一个对象

场景2:两数组合并

var arr1=new Array("1","2","3"); var arr2=new Array("4","5","6"); Array.prototype.push.apply(arr1,arr2);

d. 类数组共用数组方法

function add() { // 第一次执行时    ,定义一个数组专门用来存储所有的参数 var _args = Array.prototype.slice.call(arguments); // 在内部声明一个函数     ,利用闭包的特性保存_args并收集所有的参数值 var _adder = function() { _args.push(...arguments); return _adder; } function add() { // 第一次执行时              ,定义一个数组专门用来存储所有的参数 var _args = [].slice.call(arguments); // 在内部声明一个函数         ,利用闭包的特性保存_args并收集所有的参数值,执行时已经收集所有参数为数组 var adder = function () { var _adder = function () { // 执行收集动作     ,每次传入的参数都累加到原参数 [].push.apply(_args, [].slice.call(arguments)); return _adder; }; // 利用隐式转换的特性              ,当最后执行时隐式转换         ,并计算最终的值返回 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); } return _adder; } return adder(_args); } }
声明:本站所有文章,如无特殊说明或标注              ,均为本站原创发布     。任何个人或组织              ,在未征得本站同意时,禁止复制         、盗用     、采集              、发布本站内容到任何网站         、书籍等各类媒体平台              。如若本站内容侵犯了原著者的合法权益         ,可联系我们进行处理         。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python getattr和getattribute(python中getattribute方法作用是什么?)