JS 的 call apply bind 方法

JS核心系列:浅谈 call apply 与 bind

2016/03/01 · JavaScript
· apply,
bind,
call

威尼斯人官网,原文出处: 一像素   

在JavaScript中,call、apply和bind
是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向,从而可以达到接花移木的效果。本文将对这三个方法进行详细的讲解,并列出几个经典应用场景。

 

call(thisArgs [,args…])


该方法可以传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也就是函数中的this对象,而参数列表会被传入调用函数中。thisArgs的取值有以下4种情况:

(1) 不传,或者传null,undefined, 函数中的this指向window对象

(2) 传递另一个函数的函数名,函数中的this指向这个函数的引用

(3)
传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如
String、Number、Boolean

(4) 传递一个对象,函数中的this指向这个对象

JavaScript

function a(){ console.log(this); //输出函数a中的this对象 } function
b(){} //定义函数b var obj = {name:’onepixel’}; //定义对象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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function a(){
    console.log(this); //输出函数a中的this对象
}
function b(){} //定义函数b
 
var obj = {name:’onepixel’}; //定义对象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的核心功能,它允许你在一个对象上调用该对象没有定义的方法,并且这个方法可以访问该对象中的属性,至于这样做有什么好处,我待会再讲,我们先看一个简单的例子:

JavaScript

var a = { name:’onepixel’, //定义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!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = {
 
    name:’onepixel’, //定义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!

当执行b.call时,字符串test作为参数传递给了函数b,由于call的作用,函数b中的this指向了对象a,
因此相当于调用了对象a上的函数b,而实际上a中没有定义b 。

 

apply(thisArgs[,args[]])


apply和call的唯一区别是第二个参数的传递方式不同,apply的第二个参数必须是一个数组,而call允许传递一个参数列表。值得你注意的是,虽然apply接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的形式传递,我们看个简单的例子:

JavaScript

function b(x,y,z){ console.log(x,y,z); } b.apply(null,[1,2,3]); // 1 2
3

1
2
3
4
5
function b(x,y,z){
    console.log(x,y,z);
}
 
b.apply(null,[1,2,3]); // 1 2 3

apply的这个特性很重要,我们会在下面的应用场景中提到这个特性。

 

bind(thisArgs [,args…])


bind是ES5新增的一个方法,它的传参和call类似,但又和call/apply有着显著的不同,即调用call或apply都会自动执行对应的函数,而bind不会执行对应的函数,只是返回了对函数的引用。粗略一看,bind似乎比call/apply要落后一些,那ES5为什么还要引入bind呢?

其实,ES5引入bind的真正目的是为了弥补call/apply的不足,由于call/apply会对目标函数自动执行,从而导致它无法在事件绑定函数中使用,因为事件绑定函数不需要我们手动执行,它是在事件被触发时由JS内部自动执行的。而bind在实现改变函数this的同时又不会自动执行目标函数,因此可以完美的解决上述问题,看一个例子就能明白:

JavaScript

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {name:’onepixel’};
 
/**
* 给document添加click事件监听,并绑定onClick函数
* 通过bind方法设置onClick的this为obj,并传递参数p1,p2
*/
document.addEventListener(‘click’,onClick.bind(obj,’p1′,’p2′),false);
 
//当点击网页时触发并执行
function onClick(a,b){
    console.log(
            this.name, //onepixel
            a, //p1
            b  //p2
    )
}

当点击网页时,onClick被触发执行,输出onepixel p1 p2,
说明onClick中的this被bind改变成了obj对象,为了对bind进行深入的理解,我们来看一下bind的polyfill实现:

JavaScript

if (!Function.prototype.bind) { Function.prototype.bind = function
(oThis) { var aArgs = Array.prototype.slice.call(arguments, 1), fToBind
= this, //this在这里指向的是目标函数 fBound = function () { return
fToBind.apply( //如果外部执行var obj = new
fBound(),则将obj作为最终的this,放弃使用oThis this instanceof fToBind ?
this //此时的this就是new出的obj : oThis || this,
//如果传递的oThis无效,就将fBound的调用者作为this
//将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
aArgs.concat(Array.prototype.slice.call(arguments))); };
//将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
fBound.prototype = this.prototype; //返回fBond的引用,由外部按需调用
return fBound; }; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if (!Function.prototype.bind) {
    Function.prototype.bind = function (oThis) {
        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this, //this在这里指向的是目标函数
            fBound = function () {
                return fToBind.apply(
                    //如果外部执行var obj = new fBound(),则将obj作为最终的this,放弃使用oThis
                    this instanceof fToBind
                            ? this  //此时的this就是new出的obj
                            : oThis || this, //如果传递的oThis无效,就将fBound的调用者作为this
 
                    //将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
                    aArgs.concat(Array.prototype.slice.call(arguments)));
            };
 
        //将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
        fBound.prototype = this.prototype;
 
        //返回fBond的引用,由外部按需调用
        return fBound;
    };
}

应用场景一:继承


大家知道,JavaScript中没有诸如Java、C#等高级语言中的extend
关键字,因此JS中没有继承的概念,如果一定要继承的话,call和apply可以实现这个功能:

JavaScript

function Animal(name,weight){ this.name = name; this.weight = weight; }
function Cat(){ Animal.call(this,’cat’,’50’);
//Animal.apply(this,[‘cat’,’50’]); this.say = function(){
console.log(“I am ” + this.name+”,my weight is ” + this.weight); } } var
cat = new Cat(); cat.say();//I am cat,my weight is 50

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Animal(name,weight){
   this.name = name;
   this.weight = weight;
}
 
function Cat(){
    Animal.call(this,’cat’,’50’);
  //Animal.apply(this,[‘cat’,’50’]);
 
   this.say = function(){
      console.log("I am " + this.name+",my weight is " + this.weight);
   }
}
 
var cat = new Cat();
cat.say();//I am cat,my weight is 50

当通过new运算符产生了cat时,Cat中的this就指向了cat对象(关于new运算符的讲解,请参考:),而继承的关键是在于Cat中执行了Animal.call(this,’cat’,’50’)
这句话,在call中将this作为thisArgs参数传递,于是Animal方法中的this就指向了Cat中的this,而cat中的this指向的是cat对象,所以Animal中的this指向的就是cat对象,在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性,因此cat对象便拥有了Animal中定义的属性,从而达到了继承的目的。

 

应用场景二:移花接木


在讲下面的内容之前,我们首先来认识一下JavaScript中的一个非标准专业术语:ArrayLike(类数组/伪数组)

ArrayLike
对象即拥有数组的一部分行为,在DOM中早已表现出来,而jQuery的崛起让ArrayLike在JavaScript中大放异彩。ArrayLike对象的精妙在于它和JS原生的Array类似,但是它是自由构建的,它来自开发者对JavaScript对象的扩展,也就是说:对于它的原型(prototype)我们可以自由定义,而不会污染到JS原生的Array。

ArrayLike对象在JS中被广泛使用,比如DOM中的NodeList,
函数中的arguments都是类数组对象,这些对象像数组一样存储着每一个元素,但它没有操作数组的方法,而我们可以通过call将数组的某些方法移接到ArrayLike对象,从而达到操作其元素的目的。比如我们可以这样遍历函数中的arguments:

JavaScript

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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);

除此之外,对于apply而言,我们上面提到了它独有的一个特性,即apply接收的是数组,在传递给调用函数的时候是以参数列表传递的。
这个特性让apply看起来比call
略胜一筹,比如有这样一个场景:给定一个数组[1,3,4,7],然后求数组中的最大元素,而你知道,数组中并没有获取最大值的方法,一般情况下,你需要通过编写代码来实现。而我们知道,Math对象中有一个获取最大值的方法,即Math.max(),
max方法需要传递一个参数列表,然后返回这些参数中的最大值。而apply不仅可以将Math对象的max方法应用到其他对象上,还可以将一个数组转化为参数列表传递给max,看代码就能一目了然:

JavaScript

var arr = [2,3,1,5,4]; Math.max.apply(null,arr); // 5

1
2
3
var arr = [2,3,1,5,4];
 
Math.max.apply(null,arr); // 5

以上便是call和apply比较经典的几个应用场景,熟练掌握这些技巧,并把这些特性应用到你的实际项目中,会使你的代码看起来更加耐人寻味!

2 赞 12 收藏
评论

威尼斯人官网 1

obj.call(thisObj, arg1,arg2...)
obj.apply(thisObj, [arg1,arg2,arg3...])
obj.bind(thisObj, arg1,arg2...)

js的call apply bind
方法都很常见,目的都是为了改变某个方法的执行环境(context)

为什么需要这些?主要是因为this,来看看this干的好事。

一、call()和apply()方法

  • call 和 apply 都可以用于改变 this 指向,即将obj绑定到thisObj上,这时
    thisObj 就具有了 obj 的属性和方法。
  • 他们的区别在于第二个参数,call 接受的是连续参数,apply 接受的是
    数组参数;

 

box.onclick = function(){
  function fn(){
    alert(this);
  }
  fn();
};

1.方法定义

call

我们原本以为这里面的this指向的是box,然而却是Window。一般我们这样解决:

call方法:

function add(j, k){
    return j+k;
}

function sub(j, k){
    return j-k;
}

call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

box.onclick = function(){
  var _this = this;
  function fn(){
    alert(_this);
  }
  fn();
};

语法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

在控制台运行:

thisObj
可选项。将被用作当前对象的对象。
arg1, arg2, argN ..
可选项。将被传递方法参数序列。

将this保存下来。

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

add(5,3); //8
add.call(sub, 5, 3); //8
add.apply(sub, [5, 3]); //8

sub(5, 3); //2
sub.call(add, 5, 3); //2
sub.apply(add, [5, 3]); //2

 

还有一些情况,有时我们想让伪数组也能够调用数组的一些方法,这时call、apply、bind就派上用场了。

说明:

  • this 绑定后会立即执行
    bind 与 call、apply 的区别
  • bind 与 call 相同,接受的也是连续参数
  • bind 绑定后不会立即执行
  • bind 的第一个参数如果是 null 或者 undefined ,则会绑定到全局对象

如果没设置严格模式 “use strict”

我们先来解决第一个问题修复this指向。

call 方法可以用来代替另一个对象调用一个方法。call
方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj
指定的新对象。

同样是add()和sub():

add.bind(sub, 5, 3); //不再返回8
add.bind(sub, 5, 3)(); //8

  当thisObj 不存在或 为 undefined 或为 null 或为 this
时,则隐式地指向 全局对象(在浏览器中即为 window)

box.onclick = function(){
  function fn(){
    alert(this);
  }
  fn();
};

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。


第二个参数是一个个值

改成如下:

apply方法:

使用call调用原生方法

var a = {0:1, 1:"zohar", length: 2}; 
a.slice(); //TypeError: a.slice is not a function
Array.prototype.slice.call(a);//[1, "zohar"]

 

box.onclick = function(){
  function fn(){
    console.log(this);
  }
  fn.call(this);
};

语法:apply([thisObj[,argArray]])

使用call实现继承

var parent = function () {
  this.url= 'zohar.com.cn',
  this.name= 'zohar'
}
var child = {}
console.log(child); // {}
parent.call(child);
console.log(child); // {url: "zohar.com.cn", name: "zohar"}

原作者:飞鸿影~
出处:http://52fhy.cnblogs.com/

apply

很神奇吧,call的作用就是改变this的指向的,第一个传的是一个对象,就是你要借用的那个对象。

定义:应用某一对象的一个方法,用另一个对象替换当前对象。

apply([thisObj[,arg1, arg2, argN]])

fn.call(this);

说明:

apply和call类似,区别只是第二个参数,是一个数组(或类数组)的形式

这里的意思是让this去调用fn这个函数,这里的this是box,这个没有意见吧?如果这个你不清楚,很可能你是javscript的新朋友。box调用fn,这句话非常重要,我们知道this它始终指向一个对象,刚好box就是一个对象。那么fn里面的this就是box。

如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个
TypeError。

 

box.onclick = function(){
  function fn(){
    console.log(this);
  }
  fn.call(this);
};

如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作
thisObj, 并且无法被传递任何参数。

bind

这句话在某些情况下是可以简写的,比如:

2、常用实例

bind(thisArg [, arg1 [, arg2, …]]);

box.onclick = function(){
  var fn = function(){
    console.log(this); //box
  }.call(this);
};

a、

bind
也是改变某个方法的执行环境,区别也在于第二个参数(也是一个个的参数形式)和“返回值”的特性。

或者这样:

function add(a,b)

  它将一个func绑定给thisArg的上下文,并传入相应的参数,并以一个新函数的形式返回,以供调用。

box.onclick = function(){
  (function(){
    console.log(this);
  }.call(this)); //box
};

{

 

又或者这样:

    alert(a+b);

如 func.call(func1,var1,var2,var3)

var objName = {name:’JS2016′};
var obj = {
  name:’0 _ 0′,
  sayHello:function(){
    console.log(this.name);
  }.bind(objName)
};
obj.sayHello();//JS2016

}

对应的apply写法为:func.apply(func1,[var1,var2,var3])

call和apply、bind但是用来改变this的指向的,但也有一些小小的差别。下面我们来看看它们的差别在哪。

function sub(a,b){ 

对应的bind写法为: func.bind(func1,var1,var2,var3)() 

function fn(a,b,c,d){
  console.log(a,b,c,d);
}

    alert(a-b); 

 

//call
fn.call(null,1,2,3);

来举个栗子:

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

add.call(sub,3,1);

//'use strict'

var name = 'name1';
var obj = {
    name: 'name2',
    sayName: function(str1,str2){
        str1 = str1 || '';
        str2 = str2 || '';
        console.log(str1 + this.name + str2);
    }
};

obj.sayName();

obj.sayName.bind(window,'Hello: ',' !')();

obj.sayName.apply(this,['hello: ',' ,']);

obj.sayName.call(obj,'hello: ',' .');

//bind
var f = fn.bind(null,1,2,3);
f(4);

这个例子中的意思就是用 add 来替换 sub,add.call(sub,3,1) == add(3,1)
,所以运行结果为:alert(4);

将会输出:

结果如下:

// 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

威尼斯人官网 2

1 2 3 undefined
1 2 3 undefined
1 2 3 4

b、

 

前面说过第一个参数传的是一个你要借用的对象,但这么我们不需要,所有就传了一个null,当然你也可以传其他的,反正在这里没有用到,除了第一个参数后面的参数将作为实际参数传入到函数中。

function Animal(){

注1:但IE9(包括IE9)以上的才支持bind

call就是挨个传值,apply传一个数组,bind也是挨个传值,但和call和apply还有多少不同,使用call和apply会直接执行这个函数,而bind并不会而是将绑定好的this重新返回一个新函数,什么时候调用由你自己决定。

    this.name = “Animal”;

所以,在不支持bind的浏览器上,我们需要模拟一下

var objName = {name:’JS2016′};
var obj = {
  name:’0 _ 0′,
  sayHello:function(){
    console.log(this.name);
  }.bind(objName)
};
obj.sayHello();//JS2016

    this.showName = function(){

Function.prototype.Bind = function(context){
    var self = this,
                // 获取到bind第二个参数(中的所有参数)
        args = Array.prototype.slice.call(arguments,1);
        // 返回一个新的函数
    return function(){
        // 将相关参数赋给这个bind所在方法,并将执环境赋给context
        return self.apply(context,args);
    };
};

这里也就是为什么我要用bind的原因,如果用call的话就会报错了。自己想想这个sayHello在obj都已经执行完了,就根本没有sayHello这个函数了。

        alert(this.name);

注2:

这几个方法使用的好的话可以帮你解决不少问题比如:

    }

Function.prototype的apply和call是在1999年发布的ECMA262
Edition3中才加入的(1998年发布ECMA262 Edition2)。

正常情况下Math.max只能这样用

}

在此前的的浏览器如IE5.01(JScript
5.0)中是没有apply和call的。因此也会带来一些兼容性问题。所以,

Math.max(10,6)

function Cat(){

call的模拟:

但如果你想传一个数组的话你可以用apply

    this.name = “Cat”;

Function.prototype.Call = function(context){
       // 首先判断所给的context,即call的第一个参数
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
        // 最后要形成 一个eval字符串函数调用形式,以供动态执行
    for(var i=1,j=arguments.length; i<j; i++){
        temp.push('arguments[' + i + ']');
    }
        // 给context新增一个方法(拥有this值)
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    // console.log(evalStr);
    try{
               // 执行函数调用
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
               // 销毁该属性
        delete obj._apply;
    }
};

var arr = [1,2,30,4,5];
console.log(Math.max.apply(null,arr));

}

apply的模拟:

又或者你想让伪数组调用数组的方法

var animal = new Animal();

apply也类似,因为第二个参数是类数组的形式,所以也要变换为数组

function fn(){
  [].push.call(arguments,3);
  console.log(arguments); //[1, 2, 3]
}
fn(1,2);

var cat = new Cat();

// 第二个参数 args是为了方便使用
Function.prototype.Apply = function(context,args){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
        // 直接拿第二个参数数组的各个元素再进行组合join(',')
        // 为什么不直接用 arguments[1]呢?
        // 因为此时join也要用到 Array.prototype.join.call ,call又不一定支持
    for(var i=0,j=args.length; i<j; i++){
        temp.push('args[' + i + ']');
    }

    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    // console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};

再者:

通过call或apply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。

 

var arr = [‘aaabc’];
console.log(”.indexOf.call(arr,’b’)); //3

//输入结果为”Cat”

ok 来看一下对比效果

牛逼不,简直偷梁换柱,当然还有很多可以利用的,自己尽情花辉去吧。

animal.showName.call(cat,”,”);

Function.prototype.Bind = function(context){
    var self = this,
        args = Array.prototype.slice.call(arguments,1);
    return function(){
        return self.apply(context,args);
    };
};

Function.prototype.Call = function(context){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
    for(var i=1,j=arguments.length; i<j; i++){
        temp.push('arguments[' + i + ']');
    }
    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};

Function.prototype.Apply = function(context,args){
    context = (context == undefined) ? window : context;
    var temp = [],
        evalStr = '';
    for(var i=0,j=args.length; i<j; i++){
        temp.push('args[' + i + ']');
    }

    context._apply = this;
    evalStr = 'context._apply(' + temp.join(',') + ')';
    console.log(evalStr);
    try{
        eval(evalStr);
    }catch(e){
        throw new Error(e.message);
    }finally{
        delete obj._apply;
    }
};



var name = 'name1';
var obj = {
    name: 'name2',
    sayName: function(str1,str2){
        str1 = str1 || '';
        str2 = str2 || '';
        console.log(str1 + this.name + str2);
    }
};

obj.sayName();

obj.sayName.bind(window,'Hello: ',' !')();
obj.sayName.Bind(window,'Hello: ',' !')();

obj.sayName.apply(this,['hello: ',' ,']);
obj.sayName.Apply(this,['hello: ',' ,']);

obj.sayName.call(obj,'hello: ',' .');
obj.sayName.Call(obj,'hello: ',' .');

简单说一下这种偷梁换柱的原理吧,实际上浏览器内部根本就不在乎你是谁,它只关心你传给我的是不是我能够运行的,如下:

//animal.showName.apply(cat,[]);

威尼斯人官网 3

正常情况

call 的意思是把 animal 的方法放到cat上执行,原来cat是没有showName()
方法,现在是把animal 的showName()方法放到 cat上来执行,所以this.name
应该是 Cat。

 

var str = ‘aaabc’;
console.log(str.indexOf(‘b’));

c、实现继承

而这种情况其实做的事情和上面一模一样,看我来拆解。

function Animal(name){

var arr = [‘aaabc’];
”.indexOf.call(arr);

    this.name = name;

这句话就是说让arr调用字符串的indexOf方法,前面说过了浏览器内部不在乎你是谁,所以谁都可以来调用,但不是100%成功,具体看如下。

    this.showName = function(){ 

”.indexOf.call(arr,’b’)

        alert(this.name);

这里的arr就是[‘aaabc’],内部很可能拆成了’aaabc’,因此就成了下面的这段代码。

    }

‘aaabc’.indexOf(‘b’);

}

这就是它们的秘密。

function Cat(name){

这里得说一下bind在某些浏览器下不兼容。我们来模拟一个玩玩。

    Animal.call(this, name);

Function.prototype.$bind = function(obj){
    //保存当前this
  var _this = this;
    //截取除了第一个以外的所有实际参数
  var a = [].slice.call(arguments,1);
    //返回一个新函数
  return function(){
    //让当前那个调用的函数的this指向obj,并且把实参传给它,这里用了concat是因为,我们可能在绑定以后还传递参数,所以才把他们合并起来。如f(4)这个是在绑定以后传的参数,a这个argument是绑定时的。
    _this.apply(obj,a.concat([].slice.call(arguments)));
  };
};

}

function fn(a,b,c,d){
  console.log(a,b,c,d);
}

var cat = new Cat(“Black Cat”);

var f = fn.$bind(null,1,2,3);
f(4);

cat.showName();

这个方法和实际上的bind还是差别很大的,如

Animal.call(this) 的意思就是使用 Animal对象代替this对象,那么
Cat中不就有Animal的所有属性和方法了吗,Cat对象就能够直接调用Animal的方法以及属性了.

var arr = [‘JSS’];

d、多重继承

var index = ”.indexOf.$bind(arr,’S’);
console.log(index())

function Class10(){


    this.showSub = function(a,b){ 

function fff(){
  [].push.$bind(arguments,1);
  console.log(arguments);
}

         alert(a-b);

fff();

    }

}

function Class11(){

    this.showAdd = function(a,b){ 

         alert(a+b);

    }

}

function Class2(){ 

    Class10.call(this);

    Class11.call(this);

}

很简单,使用两个 call
就实现多重继承了当然,js的继承还有其他方法,例如使用原型链,这个不属于本文的范畴,只是在此说明call的用法。说了call
,当然还有 apply,这两个方法基本上是一个意思,区别在于
call的第二个参数可以是任意类型,而apply的第二个参数必须是数组,也可以是arguments。代码如下:

var func = new function(){

    this.a = “func”

}

var myfunc = function(x,y){

    var a = “myfunc”;

    alert(this.a);

    alert(x+y);

}

myfunc.call(func,”var”,” fun”);

//”func” “var fun”

myfunc.apply(func,[“var”,” fun”]);

//”func” “var fun”

二、bind

在EcmaScript5中扩展了叫bind的方法(IE6,7,8不支持),使用方法如下

function T(c){

    this.id = “Object”;

    this.dom = document.getElementById(“scroll”);

}

T.prototype={

    init:function(){

        //①

       this.dom.onmouseover = function(){

             console.log(“Over–>” + this.id);

       }

       //②

      this.dom.onmouseout = function(){

             console.log(“Out –>”+this.id);

      } .bind(this)

    }

};

(new T()).init();

结果:

通过①和②的对照加上显示的结果就会看出bind的作用:改变了上下文的this

bind与call很相似,,例如,可接受的参数都分为两部分,且第一个参数都是作为执行时函数上下文中的this的对象。

不同点有两个:

①bind的返回值是函数

//都是将obj作为上下文的this

functionfunc(name,id){console.log(name,id,this);

}varobj =”Look here”;

//什么也不加func(”    “,”–>”);

//使用bind是 返回改变上下文this后的函数

var a = func.bind(obj, “bind”, “–>”);

a();

//使用call是 改变上下文this并执行函数

var b = func.call(obj, “call”, “–>”);

结果:

②后面的参数的使用也有区别

functionf(a,b,c){console.log(a,b,c);}varf_Extend =
f.bind(null,”extend_A”)

f(“A”,”B”,”C”)  //这里会输出–> A B C

f_Extend(“A”,”B”,”C”)  //这里会输出–> extend_A A B

f_Extend(“B”,”C”)  //这里会输出–> extend_A B C

f.call(null,”extend_A”) //这里会输出–> extend_A undefined
undefined

call 是 把第二个及以后的参数作为f方法的实参传进去

而bind
虽说也是获取第二个及以后的参数用于之后方法的执行,但是f_Extend中传入的实参则是在bind中传入参数的基础上往后排的。

//这句代码相当于以下的操作

var f_Extend = f.bind(null,”extend_A”)

//↓↓↓

varf_Extend =function(b,c){returnf.call(null,”extend_A”,b,c);

}

举一个应用场景:例如现在有一个方法
根据不同的文件类型进行相应的处理,通过bind 就可以创建出简化版的处理方法

functionFileDealFunc(type,url,callback){if(type==”txt”){…}elseif(type==”xml”){…}

…..

}varTxtDealFunc = FileDealFunc.bind(this,”txt”);

//这样使用的时候更方便一些

FileDealFunc(“txt”,XXURL,func);  //原来

TxtDealFunc(XXURL,func); //现在

以下是兼容处理

if(!Function.prototype.bind)
{Function.prototype.bind=function(obj){var_self
=this,args=arguments;returnfunction(){           
_self.apply(obj,Array.prototype.slice.call(args,1));

}

}

}

(转载)

原文:http://www.cnblogs.com/jingwhale/p/4604917.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注