说在前面
我们先来说一下三个方法的用途,即改变函数内部this指向。由此可见这三个方法都是函数的方法,且是非继承而来的方法。那么,先看一下函数的属性
函数属性
length
:形参个数name
:函数名prototype
:对于引用类型而言,prototype保存了所有实例方法,在原型上定义的方法都是当前这个类的实例的公有方法;prototype属性不可枚举,for-in无效;_proto_
:把函数当做一个普通对象,指向Function这个类的原型- 函数的三种用法:
function Fn() {
var num = 500;
this.x = 100;
}
Fn.prototype.getX = function () {
console.log(this.x);
this.a=101;
console.log(this); //Fn {x: 100, a: 101} _proto_:Object
}
Fn.aaa = 1000;
var f = new Fn; //Fn作为一个类
//f.getX();调用过后下面代码打印出101,不调用时,undefined
console.log(f.a); // undefined
console.log(f.x);//100
console.log(f.num);//undefined 私有变量访问不到
console.log(f.aaa); //undefined aaa没有添加在原型上,不会被实例访问到
- 三角色
- 普通函数:对于Fn而言,它本身是一个普通的函数,执行的时候会形成私有的作用域,然后进行形参赋值、预解析、代码执行、执行完成后内存销毁;
- 类:它有自己的实例,f就是Fn作为类而产生的一个实例,也有一个叫做prototype 的属性是自己的原型,它的实例都可以指向自己的原型;
- 普通对象:Fn和 var obj = {} 中的obj一样,就是一个普通的对象(所有的函数都是Function的实例),它作为对象可以有一些自己的私有属性,也可以通过__proto__找到Function.prototype;每个函数都有一个公共的 prototype —— Function,而这个原型自带有好几个属性和方法,其中就有 bind、call、apply 方法。
例子
看例子,我们可能以为这里的 this 指向 box,实际上此处 this 指向 window。
box.onclick = function(){
function fn(){
alert(this);
}
fn();
}
此时,我们一般会保存一下此处this值,如下
box.onclick = function(){
var that = this;
function fn(){
alert(that);
}
fn();
}
很明显,apply、call、bind也可以修复这个问题,如下:
box.onclick = function(){
function fn(){
alert(this);
}
fn.call(this);
}
call()、apply()、bind()区别
参数传递:
- call()方法: 可以接受多个参数,第一个对象为新this对象 后面是形参列表,也就是说传递参数时要逐个列举出来。要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。如果没有提供Obj参数,那么 Global 对象被用作Obj。
function.call(Obj,arr0,arr1,arr2,···);
- apply()方法: 最多接受两个参数 新this对象和arr数组,即使第二个参数只有一个参数也要写进数组里。
function.apply(Obj,[arr])
- bind()方法:bind() 函数的参数跟 call() 一致,第一个参数也是绑定 this 的值,后面接受传递形参列表。
function.bind(Obj,arr0,arr1,arr2,···);
看下面这个例子:
function fn(num1, num2) {
console.log(num1 + num2);
console.log(this);
}
fn.call(obj , 100 , 200);
fn.apply(obj , [100, 200]);
var tempFn = fn.bind(obj,100,200);
tempFn();
call()挨个传值,apply传入数组,bind()挨个传值。call()和apply()都是立马调用对应的函数,而bind() 会生成一个新的函数,将绑定的this重新返回给新函数,然后在任何需要的地方调用。
基本使用
例子一
var arr = {1,2,3,4,5};
arr.pop();
arr
这个实例首先原型上查找,实例中没有找到,然后通过原型链的查找机制找到Array.prototype
上的pop
方法,然后执行,删除arr
最后一个元素并返回。
使用call后
var obj = {age:16};
function f(){
console.log(this.age);
}
f();//this-->window
//obj.f();//Uncaught TypeError: obj.f is not a function
f.call(obj);//this-->{age: 16} this.age-->16
首先寻找call
方法,最后通过原型链在Function
的原型中找到call
方法,然后让call
方法执行,在执行call
方法的时候,f
方法中的this
变为第一个参数obj
,最后执行f
函数。
例子二
正常情况下,Math.max只能这样用
Math.max(3,2);
但是当你想传入一个数组时,就可以使用apply
var arr=[1,3,5,7,9];
Math.max.apply(null,arr);
或者想让伪数组调用数组方法
function(){
[].push.call(arguments,3);
console.log(arguments);//[1,2,3]
}
fn(1,2);
var arr = ['aabbc'];
console.log(''.indexOf.call(arr,'c')); //4 arr调用字符串的indexOf方法 与正常情况str.indexOf('b')相同