javascript中call apply 与 bind方法详解_基础知识

在JavaScript中,call、apply和bind是Function对象自带的三个方法,本文将通过几个场景的应用,来详细理解三个方法。

call()

call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。

当调用一个函数时,可以赋值一个不同的 this 对象。this 引用当前对象,即 call 方法的第一个参数。

通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法。

语法 fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
在fun函数运行时指定的this值。需要注意的是下面几种情况

(1)不传,或者传null,undefined, 函数中的this指向window对象
(2)传递另一个函数的函数名,函数中的this指向这个函数的引用,并不一定是该函数执行时真正的this值
(3)值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象,如 String、Number、Boolean
(4)传递一个对象,函数中的this指向这个对象

arg1, arg2, ...
指定的参数列表。

例子
初级应用例子

function a(){
 //输出函数a中的this对象
 console.log(this);
}
//定义函数b
function b(){} 

var obj = {name:'这是一个屌丝'}; //定义对象obj
a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object

使用call方法调用匿名函数并且指定上下文的this

在下面的例子中,当调用 greet 方法的时候,该方法的 this 值会绑定到 i对象。

function greet() {
 var reply = [this.person, '是一个轻量的', this.role].join(' ');
 console.log(reply);
}

var i = {function greet() {
 var reply = [this.person, '是一个轻量的', this.role].join(' ');
 console.log(reply);
}

var i = {
 person: 'JSLite.io', role: 'Javascript 库。'
};

greet.call(i);
// JSLite.io 是一个轻量的 Javascript 库。

 person: 'JSLite.io', role: 'Javascript 库。'
};

greet.call(i);
// JSLite.io 是一个轻量的 Javascript 库。

使用call方法调用匿名函数

在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示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);
}
//#0 Lion: King
//#1 Whale: Fail

使用call方法调用函数传参数

var a = {
 name:'JSLite.io', //定义a的属性
 say:function(){ //定义a的方法
 console.log("Hi,I'm function a!");
 }
};
function b(name){
 console.log("Post params: "+ name);
 console.log("I'm "+ this.name);
 this.say();
}

b.call(a,'test');
//Post params: test
//I'm onepixel
//I'm function a!

apply()

语法与 call() 方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要,

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

语法:fun.apply(thisArg[, argsArray])
注意: 需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。

参数
thisArg

同上call 的thisArg参数。

argsArray

一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

例子

function jsy(x,y,z){
 console.log(x,y,z);
}

jsy.apply(null,[1,2,3]);
// 1 2 3

使用apply来链接构造器的例子

你可以使用apply来给一个对象链接构造器,类似于Java. 在接下来的例子中我们会创建一个叫做construct的全局的Function函数,来使你能够在构造器中使用一个类数组对象而非参数列表。

Function.prototype.construct = function(aArgs) {
 var fConstructor = this,
 fNewConstr = function() {
 fConstructor.apply(this, aArgs);
 };
 fNewConstr.prototype = fConstructor.prototype;
 return new fNewConstr();
};
function MyConstructor () {
 for (var nProp = 0; nProp < arguments.length; nProp++) {
 console.log(arguments,this)
 this["property" + nProp] = arguments[nProp];
 }
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);

console.log(myInstance.property1);  // logs "Hello world!"
console.log(myInstance instanceof MyConstructor); // logs "true"
console.log(myInstance.constructor);  // logs "MyConstructor"

使用apply和内置函数

聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下里的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值。

//里面有最大最小数字值的一个数组对象
var numbers = [5, 6, 2, 3, 7];

/* 使用 Math.min/Math.max 在 apply 中应用 */
var max = Math.max.apply(null, numbers);
// 一般情况是用 Math.max(5, 6, ..) 或者 Math.max(numbers[0], ...) 来找最大值
var min = Math.min.apply(null, numbers);

//通常情况我们会这样来找到数字的最大或者最小值
//比对上面的栗子,是不是下面的看起来没有上面的舒服呢?
max = -Infinity, min = +Infinity;
for (var i = 0; i < numbers.length; i++) {
 if (numbers[i] > max)
 max = numbers[i];
 if (numbers[i] < min)
 min = numbers[i];
}

参数数组切块后循环传入

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)));
 console.log(submin, min)
 min = Math.min(submin, min);
 }

 return min;
}

var min = minOfArray([5, 6, 2, 3, 7]);

bind

bind() 函数会创建一个新函数(称为绑定函数)

bind是ES5新增的一个方法
传参和call或apply类似
不会执行对应的函数,call或apply会自动执行对应的函数
返回对函数的引用
语法 fun.bind(thisArg[, arg1[, arg2[, ...]]])

下面例子:当点击网页时,EventClick被触发执行,输出JSLite.io p1 p2, 说明EventClick中的this被bind改变成了obj对象。如果你将EventClick.bind(obj,'p1','p2') 变成 EventClick.call(obj,'p1','p2') 的话,页面会直接输出 JSLite.io p1 p2

var obj = {name:'JSLite.io'};
/**
 * 给document添加click事件监听,并绑定EventClick函数
 * 通过bind方法设置EventClick的this为obj,并传递参数p1,p2
 */
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//当点击网页时触发并执行
function EventClick(a,b){
 console.log(
  this.name, //JSLite.io
  a, //p1
  b //p2
 )
}
// JSLite.io p1 p2

兼容

if (!Function.prototype.bind) {
 Function.prototype.bind = function (oThis) {
 if (typeof this !== "function") {
 // closest thing possible to the ECMAScript 5
 // internal IsCallable function
 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
 }

 var aArgs = Array.prototype.slice.call(arguments, 1),
 fToBind = this, // this在这里指向的是目标函数
 fNOP = function () {},
 fBound = function () {
  return fToBind.apply(this instanceof fNOP
   ? this //此时的this就是new出的obj
   : oThis || this,//如果传递的oThis无效,就将fBound的调用者作为this

  //将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
  aArgs.concat(Array.prototype.slice.call(arguments)));
 };
 fNOP.prototype = this.prototype;
 //将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
 fBound.prototype = new fNOP();
 //返回fBond的引用,由外部按需调用
 return fBound;
 };
}

兼容例子来源于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

应用场景:继承

function Animal(name,weight){
 this.name = name;
 this.weight = weight;
}
function Cat(){
 // 在call中将this作为thisArgs参数传递
 // Animal方法中的this就指向了Cat中的this
 // 所以Animal中的this指向的就是cat对象
 // 在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性
 // cat对象便拥有了Animal中定义的属性,从而达到了继承的目的
 Animal.call(this,'cat','50');
 //Animal.apply(this,['cat','50']);
 this.say = function(){
 console.log("I am " + this.name+",my weight is " + this.weight);
 }
}
//当通过new运算符产生了cat时,Cat中的this就指向了cat对象
var cat = new Cat();
cat.say();
//输出=> I am cat,my weight is 50

原型扩展

在原型函数上扩展和自定义方法,从而不污染原生函数。例如:我们在 Array 上扩展一个 forEach

function test(){
 // 检测arguments是否为Array的实例
 console.log(
 arguments instanceof Array, //false
 Array.isArray(arguments) //false
 );
 // 判断arguments是否有forEach方法
 console.log(arguments.forEach);
 // undefined
 // 将数组中的forEach应用到arguments上
 Array.prototype.forEach.call(arguments,function(item){
 console.log(item); // 1 2 3 4
 });
}
test(1,2,3,4);

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索javascript
, bind
, apply
call
bind call apply、apply call bind 区别、js call apply bind、apply call bind 参数、javascript bind详解,以便于您获取更多的相关知识。

时间: 2024-09-17 03:42:50

javascript中call apply 与 bind方法详解_基础知识的相关文章

Javascript中的迭代、归并方法详解_基础知识

迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的函数,如果每项都返回true,那么就会返回true filter() 对数组中的每一个项运用给定的函数,把返回true的项组成一个新数组并返回 forEach() 对数组中的每一项运用给定的函数,但是没有任何的返回值 map() 对数组中的每一个项运用给定的函数并返回每次函数调用的结果组成新的数组

Javascript中Date类型和Math类型详解_基础知识

Date类型 ECMASCript中的Date类型是在早期中Java中的java.util.Date类基础上构建的.为此Date类型使用自UTC(国际协调时间)1970年1月1日午夜(0时)开始经过的毫秒数来保存日期. 创建日期对象 1.创建当前日期.不需要传入参数 2.创建指定日期.需要传入参数,必须传入表示该日期的毫秒数(即从1970年1月1日午夜起至该日期止经过的毫秒数).为了简化这一计算过程,ECMAScript提供了两个方法:Date.parse()和Date.UTC(). var n

JavaScript中的Repaint和Reflow用法详解_基础知识

你是不是经常听师兄或一些前端前辈说不能用CSS通配符 *,CSS选择器层叠不能超过三层,CSS尽量使用类选择器,书写HTML少使用table,结构要尽量简单-DOM树要小....等这些忠告,以前我就大概知道使用通配符或者CSS选择器层次过多可能会降低性能,至于为什么不使用table标签我一直是迷迷糊糊,也就跟着那样做了,但我认识了Repain和 Reflow之后,原来这些还真不能用太多. ok,希望这篇文章对你有帮助! 1.什么是Repaint/Reflow? 好,先上一张图:浏览器解析大概的工

JavaScript中的原型prototype属性使用详解_基础知识

 prototype属性可以将属性和方法添加到任何对象(Number, Boolean, String 和Date等). 注:原型(Prototype)是一个全局的属性,它可以使用在几乎所有的对象.语法 object.prototype.name = value 实例: 这里有一个例子展示了如何使用原型(prototype)属性的属性添加到对象: <html> <head> <title>User-defined objects</title> <sc

JavaScript中的条件判断语句使用详解_基础知识

 在写一个程序,可能有一种情况,当你需要采用一个路径出给定两个路径.所以,需要使用条件语句,让程序来做出正确的决策和执行正确的行动. JavaScript支持其用于执行根据不同的条件不同的操作条件语句.在这里,我们将解释if..else语句. JavaScript支持if..else语句的形式如下:     if 语句     if...else 语句     if...else if... 语句. if 语句: if语句是基本的控制语句,它允许JavaScript来作出决定,有条件地执行语句.

javascript中attribute和property的区别详解_基础知识

DOM元素的attribute和property很容易混倄在一起,分不清楚,两者是不同的东西,但是两者又联系紧密.很多新手朋友,也包括以前的我,经常会搞不清楚. attribute翻译成中文术语为"特性",property翻译成中文术语为"属性",从中文的字面意思来看,确实是有点区别了,先来说说attribute. attribute是一个特性节点,每个DOM元素都有一个对应的attributes属性来存放所有的attribute节点,attributes是一个类数

JavaScript中的Math.E属性使用详解_基础知识

 这是一个欧拉常数和自然对数的基数,约为 2.718.语法: Math.E 示例: <html> <head> <title>JavaScript Math E Property</title> </head> <body> <script type="text/javascript"> var property_value = Math.E document.write("Property

JavaScript中的Math.LN2属性用法详解_基础知识

 这是一个欧拉常数和自然对数的基数,约为 2.718.语法: Math.E 示例: <html> <head> <title>JavaScript Math E Property</title> </head> <body> <script type="text/javascript"> var property_value = Math.E document.write("Property

JavaScript中Math.SQRT2属性的使用详解_基础知识

 这将返回2的平方根大约是 1.414. 语法 Math.SQRT2 例子: <html> <head> <title>JavaScript Math SQRT2 Property</title> </head> <body> <script type="text/javascript"> var property_value = Math.SQRT2 document.write("Prop