call(),apply(),bind()
函数大家可能都有所了解,但是在平时搬砖过程中
很可能或者基本没用过
,学过但都淡忘了。
但是在大量第三方的框架(库),甚至js自己都在
源码中大量使用
call,apply
函数。所以今天和大家仔细讨论下它们在
开发中的应用场景
。
1.它们是啥意思
1.1 作用
-
他们的作用都是改变函数内部的
this
。
-
这三个函数都是
函数对象
的方法,也就是说只有函数才可以直接调用这些方法。
ps:call,apply,bing属于this显示绑定,还有好几种其他的this绑定方式,感兴趣的可以点这里。
1.2 三者区别
参数
三个函数的第一个参数都是需要绑定的
this
。
call 语法:
foo.call(this, arg1,arg2, ... ,argn );
apply 语法:
foo.apply(this, [ arg1,arg2, ... ,argn ] );
bind 语法:
foo.bind(this);
调用
-
call,apply
:调用后立即执行原函数。
-
bind
:调用后返回已经绑定好this的函数。
小例子一枚:
function foo(a,b){
console.log(a+b);
}
foo.call(null,'海洋','饼干');
// 海洋饼干 这里this指向不重要就写null了
foo.apply(null, ['海洋','饼干'] ); // 海洋饼干
var fun = foo.bind(null);
fun('海洋','饼干'); // 海洋饼干
2 .它们能干啥事
这是我们今天讨论的主题,这三个函数如何应用?什么情况下使用?
能改变
this
指向又能咋滴?
2 .1 处理伪数组 (最常用)
先考虑一个问题,如果你使用
vararr=document.getElementsByTagName('li')
获取了5个
li
元素,你现在需要获取其中的第2,3,4三个元素,你会怎么做?
这样
arr.slice(1,4);
? 啊哦,
TypeError--arr.sliceisnotafunction(slice不是函数)
,数组操作在日常搬砖中非常常见,我见过最傻的解决这个问题的方式是使用循环,将需要的元素一个个添加到一个新数组里0.0,
下面我介绍的方法完全可以在实战中使用,可以给你的代码加分哦,非常方便简洁
(中高级前端程序员中,算是基本操作了)。
先要介绍一个概念(
伪数组
),这也是为什么我们刚刚slice切割数组时出错的原因: (对新手来说算是干货了,知道的可以跳过)
什么是伪数组?( 字面的意思已经呼之欲出了 )
-
有length属性
-
能按索引存储数据
-
能像遍历数组一样来遍历
-
不能使用数组的
push()、slice()
等方法
简单来说就是可以像数组一样操作的对象,但是没有数组的方法。
js中存在大量伪数组,如 :
1. function的arguments对象。
2. getElementsByName(),getElementsByTagName(),childNodes/children 等方法的返回值。
3. 还有比较常见的jquery,使用它获取的元素也是伪数组。
回到原来的问题,如何截取伪数组中的元素:
伪数组没有这些方法,我们'借用'Array的slice不就行了
[].slice.call(arr,1,4); // 推荐写法
不想借用你可以直接给伪数组添加一个slice函数,如
arr.slice = [].slice;
arr.slice(1,4);
当然,'借用' 更方便,直接添加会导致伪数组对象'污染'。
如果可以随意改变原对象,可以
直接将其转成真正的数组对象
。
[].slice.call(arr);
2 .2 继承
继承方式多种多样,我们现在讨论的这种是其中很重要的一种实现方式,用
call
实现 js 构造函数继承 。
单继承
function person(name){
this.name = name
}
function man(name){
this.age = '男';
person.call(this,name); // 继承 man
}
var me = new man('海洋饼干');
console.log(me.name,me.age); // '海洋饼干' '男'
多继承
function person(name){
this.name = name
}
function man(name){
this.age = '男';
}
function manProgrammer(name){
this.girlfriend = null;
person.call(this,name); // 继承 person
man.call(this,name); // 继承 man
}
var me = new manProgrammer('海洋饼干');
console.log(me.name,me.age,me.girlfriend); // '海洋饼干' '男' null
2 .3 this 硬绑定 --- bind
将一个对象强制且永久性绑定到函数的this上,使用call,apply或者其他的绑定方式都无法改变(除了
new
绑定,当然,可以手动撸一个
new
都无法改变的硬绑定)
直接看例子:
var fun ;
var obj = {
a : 1,
foo : function(){
var _this = this; //平时有没有过这种写法? 为了防止this指向问题
//将this赋值给一个变量,间接维持了this的安全性
fun = function(){
console.log(_this.a);
}
}
}
obj.foo();
fun(); // 1
var obj1 = { a : 2}
obj.foo.call(obj1); // 直接修改_this所绑定的值,boom了
fun(); // 2
但是这种方法感觉上是在逃避问题,直接不使用this了 ? 这真的不是什么好的解决问题的态度。下面使用我们的bind来优化一下:
var fun ;
var obj = {
a : 1,
foo : function(){