浅谈call apply bind

初学call apply的时候看的是书,书上讲的很浅显,在网上看了很多博客讲的都很是高深,这里标题说是浅谈,其实本来想写的是深入,但是写到最后感觉自己所了解的远远不够,这里的this究竟指的是什么,与作用域又有什么关系?因为目前知识水平的限制,我就暂时先总结了一些。(当然啦,感兴趣的童鞋可以自己再往这方面专研一下)更加深层次的东西,我也将继续挖掘 ~~

其实,call()和apply()就是改变函数的执行上下文,(也就是函数内部的this的指向)。他们两个是Function对象的方法,每个函数都能调用。他们的第一个参数就是你要指定的执行上下文。


call方法

第一个参数是要绑定给this的值,第二部分参数要一个一个传。(当第一个参数为nll,nudefined的时候,默认指向window)

这是一个简单的例子:

//例1
var obj = {
    message: 'This is a people: '
}

function person(name, age) {
    console.log(this.message + name + ' ' + age)  //This is a people: xiaoming 18
}

person.call(obj, 'xiaoming', 18)

了解了它的基本语法后,我们来看这样一段代码:

//例2
    function A(a,b) {
        this.a = a;
        this.b = b;
    }
    function B() {
        this.a = 5;
        this.c = 3;
        A.call(this,1,2);
    }
    var s = new B();
    console.log(s.a+","+s.b+","+s.c);
  • 输出是什么,是5,2,3 还是 1,2,3?
    答案是后者,这是因为给B调用了A的call方法,使A的this指针指向了B,虽然我们在前面定义了B的a为5,但是在调用call方法之后又将B的a重写为了1。

上面两个都懂了 看一下第三个例子:

//例3
function A(){
        this.show = function(){alert(this.name)}
    }
    function B(){
        this.name = 'b';
    }
    var C = new A();
    var D = new B();
    C.show.call(D);//b
    alert(D.show);//undefined
    
//这段代码中,让C.show中的this指向了D,改变了匿名函数里面的this.name的值
//但是并没有给D写入C.show,所以第一个输出b第二个输出undefined

apply

接受两个参数,第一个参数是要绑定给this的值,所有参数都必须放在一个数组里面作为第二个参数传进去。(当第一个参数为null、undefined的时候,默认指向window。)

call 和apply是立即调用


bind

类似于call,第一个参数是this的指向,从第二个参数开始是接收的参数列表。(区别在于bind方法返回值是函数以及bind接收的参数列表。)

var obj = {
    name: 'xiaoming'
}

function person() {
    console.log(this.name)
}

var people = person.bind(obj)  
console.log(people)  //ƒ person() {...}
people()             //xiaoming

  • bind是新创建一个函数,然后把它的上下文绑定到bind()括号中的参数上,然后将它返回。所以,bind后函数不会执行,而只是返回一个改变了上下文的函数副本。(一般需要一般变量来接受它的返回值) 而原函数 printName 中的 this 并没有被改变,依旧指向全局对象 window。

bind中参数的使用:

来,我们再来看一个好玩的东西:

function fn(a, b, c) {
    console.log(a, b, c);
}
var fn1 = fn.bind(null, 'xiaoming'); //相当于已将fn1中第一个参数的值确定好了

fn('A', 'B', 'C');            // A B C
fn1('A', 'B', 'C');           // Dot A B  //传入的实参依次向后对应一位
fn1('B', 'C');                // Dot B C
fn.call(null, 'xiaoming');    // Dot undefined undefined

对call 来说它会严格遵照将第二个及其以后的参数作为实参传入fn,而对于bind方法 fn1的实参是在bind中已确定值的参数 基础上向后排的。


三者的使用区别

  1. 都是用来改变函数的this对象的指向的;(第一个参数都是this要指向的对象)
    2.都可以利用后续参数传参;
  2. bind是返回对应函数,便于稍后调用,apply、call是立即调用;

看了这么多,你或许还回小瞧它们,难道应用它们仅仅是为了改变this指向,哈哈,当然不是了!

有何用处?

1. 将类数组转化为数组

将这个之前我们先来简单回答一下类数组和数组的关系:类数组(1)拥有length属性 (2)不具有数组所具有的方法

常见的类数组有哪些呢?
参数的参数 arguments,DOM 对象列表(比如通过 document.getElementsByTags 得到的列表),jQuery 对象(比如 $(“div”))

原生JS中的方法:
var arr = Array.prototype.slice.call(类数组);

该方法等价于:
var arr = [].slice.call(类数组);

刚看到设个方法的时候,自己很是疑惑,为啥这样就可以。再网上看到一篇文章博主是这样解释的,觉得很有道理:

Array.prototype.slice.call(arguments);

它会将一个类数组形式的变量转化为真正的数组。为啥呢,其实书上并没有说slice还有这样的用法,也不知道是谁发明的。slice的用法可以顺便上网查一下,就能查到。但要更正一点,网上的介绍说slice有两个参数,第一个参数不能省略。然而我不知道是我理解的问题还是咋地,上面这段代码不就是典型的没传参数吗!!!arguments是传给call的那个上下文,前面讲过,不要弄混(由于arguments自己没有slice方法,这里属于借用Array原型的slice方法)。而且经过测试,若果你不给slice传参数,那就等于传了个0给它,结果就是返回一个和原来数组一模一样的副本。这之后的代码就很好理解,返回一个函数,该函数把传给bind的第一个参数当做执行上下文,由于args已经是一个数组,排除第一项,将之后的部分作为第二部分参数传给apply。

2. 数组追加(当然这里可以用数组的拼接函数concat)

var arr1 = [1,2,3];
var arr2 = [4,5,6];
var total = [].push.apply(arr1, arr2);//6
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]

在这里增加一个知识:
push 添加只能将添加项作为一个子元素,增加到母数组的后面

var arr1=[1,2,3,4];
    var arr2=[5,6,7,8];
    
    arr1.push(arr2);
    console.log(arr1); //(5) [1, 2, 3, 4, Array(4)]

3.利用call apply做继承

function Person(name,age){
    // 这里的this都指向实例
    this.name = name
    this.age = age
    this.sayAge = function(){
        console.log(this.age)
    }
}
function Boy(){
    Person.apply(this,arguments)//将父元素所有方法在这里执行一遍就继承了
}
var dcbryant = new Boy('dcbryant',22)

猜你喜欢

转载自blog.csdn.net/qq_41853863/article/details/83385845
今日推荐