javascript-call、apply和bind方法的理解

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 相关定义:

  • call()
    语法:fun.call([thisObj[,arg1[, arg2[, [,.argN]]]]])
    定义:调用一个对象的一个方法,以另一个对象替换当前对象。
    说明: call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
  • apply()
    语法:fun.apply([thisObj[,argArray]])
    定义:应用某一对象的一个方法,用另一个对象替换当前对象。
    说明: 如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。 如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。
  • bind()
    语法:fun.bind(thisArg[, arg1[, arg2[, ...]]])
    定义:创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,可以在调用之前提供一个给定的参数序列。
    说明: 参数arg1, arg2, ...当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。bind返回由指定的this值和初始化参数改造的原函数拷贝。

1.2 差异:

  • call和apply:call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
  • bind和call、apply:bind返回对应函数,便于稍后调用而apply 、call 则是立即调用 。

1.3 应用场景:

  • 使用原生对象的方法
  • 实现继承
  • bind方法的简单使用

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 类数组

  • 类数组是一个对象,虽然该对象并不是由Array构造函数所创建的,但它依然呈现出数组的行为。类数组对象拥有一个特性:可以在类数组对象上应用(利用call、apply方法)数组的操作方法。
  • 在浏览器环境中,document.getElementsByTagName()语句返回的就是一个类数组对象。在function调用中,function代码内的arguments变量(保存传入的参数)也是一个类数组对象。在ECMAScript 5标准中,字符串string就是一个只读的类数组对象。

几种常见的类数组:

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 柯里化

  • 之前看函数式编程的时候了解过一点,大概意思就是把一个有n个参数的函数变成n个只有1个参数的函数。
    柯里化的简单实现:
// 普通函数
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 偏函数

  • 偏函数,固定函数中的某一个或几个参数,返回一个新的函数,接收剩下的参数, 参数个数可能是1个,也可能是2个,甚至更多。具体示例见bind方法简单使用-demo2

4. 相关知识

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容