F2e踩坑之路程序员Web前端之路

js方法call、apply和bind理解

2017-12-14  本文已影响78人  家里有棵核桃树

1. 介绍

刚出来找前端工作的时候,最常见的面试题就是“谈谈你对call和apply的理解”,以前只知道这些名词,但是一点也不理解。随着对jquery的熟悉发现jquery源码中很多都用到了apply方法,就顺便总结了一些功能类似的call和bind方法的使用。

JavaScript中的每一个Function对象的原型上都有一个apply()方法和一个call()方法,call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。call和apply是为了动态改变this而出现的,当一个对象没有某个方法,但是其它对象有,我们可以借助call或apply用其它对象的方法来操作。
bind方法也可以用来改变当前函数执行的上下文,和call、apply不同的是bind返回对应函数,便于稍后调用而apply 、call 则是立即调用 。

1.1 相关定义:

1.2 差异:

1.3 应用场景:

2. 使用

2.1 使用原生对象的方法

(1)类数组使用Array原生方法

let arrayLike = {0: "hello", 1: "sunny", 2: "~", length: 3};
let arrayLikeToArray1_1 = Array.prototype.slice.call(arrayLike); // ["hello", "sunny", "~"]
let arrayLikeToArray1_2 = Array.prototype.slice.call(arrayLike, 0, 2); // ["hello", "sunny"]
let arrayLikeToArray2_1 = Array.prototype.slice.apply(arrayLike); // ["hello", "sunny", "~"]
let arrayLikeToArray2_2 = Array.prototype.slice.apply(arrayLike, [0, 2]); // ["hello", "sunny"]

(2)扩展Array对象属性,使类数组也可以使用这些静态方法
测试的时候只扩展了部分属性,如果有其它需求自己扩展呦~~~方法类似。

let array1 = ["hello", "sunny", "~"];
Array.join = function (a, sep) {

    // return Array.prototype.join.call(a, sep);
    return Array.prototype.join.apply(a, [sep]);
};
Array.slice = function (a, start, end) {

    // return Array.prototype.slice.call(a, start, end);
    return Array.prototype.slice.apply(a, [start, end]);
};
Array.map = function (a, callback, thisArg) {

    // return Array.prototype.map.call(a, callback, thisArg);
    return Array.prototype.map.apply(a, [callback, thisArg]);
};
console.log(Array.join(array1, "-")); // hello-sunny-~
console.log(Array.slice(array1, 0, 1)); // ["hello"]
Array.map(array1, function (value, index) {

    console.log("index=" + index + ",value=" + value);
}); // index=0,value=hello index=1,value=sunny index=2,value=~

说明:用call和apply都是可以扩展的哈,注释掉的方法也是可行的。

2.2 实现继承

子类使用父类的方法,严格来讲不算是继承,只是调用其它对象的方法(该方法内的this指向使用者)。

function Animal(name) {

    this.name = name || "Animal";
    this.showName = function() {

        console.log(this.name);
    }
}
function Cat(name) {

    this.name = name || "Cat";
}
let animal = new Animal();
let cat1 = new Cat();
let cat2 = new Cat("cat2");
// 子类使用父类的方法,也可以延伸至某些对象调用其它对象的方法
animal.showName.call(cat1); // Cat
animal.showName.apply(cat1); // Cat
animal.showName.call(cat2); // cat2
animal.showName.apply(cat2); // cat2

2.3 bind方法的简单使用

说明:在实际开发中很少看到bind的使用,所以只做了两个简易的demo。对于bind的更高级用法,需要大家自己去摸索了,我的了解就仅限于这里了。
(1)创建绑定函数

window.name = "window";
let user = {
    name: "user",
    getName: function() {

        console.log(this.name);
    }
};
user.getName(); // user
let getUserName1 = user.getName; // this指向全局作用域
getUserName1(); // window
let getUserName2 = user.getName.bind(user);
getUserName2(); // user

(2)偏函数

function add() {

    let sum = 0;
    for (let i = 0, len = arguments.length; i < len; i++) {

        sum += arguments[i];
    }
    console.log(this + ":sum=" + sum);
}
let add1 = add.bind("add1", 2, 3);
add1(); // add1:sum=5
add1(5); // add1:sum=10
let add2 = add.bind("add2", "hello");
add2(); // add2:sum=0hello
add2(" sunny"); // add2:sum=0hello sunny

3. 扩展

3.1 类数组

几种常见的类数组:

let obj = {0: "hello", 1: "sunny", 2: "~", length: 3};
let anchor = document.getElementsByTagName("a");
let msg = "hello";
function test() {

    console.log(arguments); // arguments
}

3.2 柯里化

// 普通函数
function sum1(x, y, z) {

    console.log("sum1:x+y+z=" + (x + y + z));
}
sum1(1, 3, 4); // sum1:x+y+z=8
// 柯里化后的函数
function sum2(x) {

    return function (y) {

        return function (z) {

            console.log("sum2:x+y+z=" + (x + y + z));
        }
    }
}
sum2(1)(3)(4); // sum2:x+y+z=8

3.3 偏函数

4. 相关知识

上一篇 下一篇

猜你喜欢

热点阅读