this、apply、call、bind

原文出处:https://juejin.im/post/59bfe84351882531b730bac2

[TOC]

1、this指向

在ES5中,其实this的指向,始终坚持一个原理:this永远指向最后调用它的那个对象,来,读三遍:this永远指向最后调用它的那个对象this永远指向最后调用它的那个对象this永远指向最后调用它的那个对象。记住这句话,下面我们看一个例子:

//例1
var name = "windowsName";

function a() {
    var name = "Cherry";

    console.log(this.name); // windowsName

    console.log("inner:" + this); // inner:[object Window]
}
a();
console.log("outer:" + this) // outer:[object Window]

这个相信大家都知道为什么log的是windowsName,因为根据刚刚的那句话"this 永远指向最后调用它的那个对象",上例中最后调用a的地方a();,前面没有调用的对象那么就是全局对象window,这就相当于window.a();注意,这里我们没有使用严格模式,如果使用严格模式的话,全局对象就是undefined,那么就会报错Uncaught TypeError: Cannot read property 'name' of undefined。

//例2
var name = "windowsName";
var a = {
    name: "Cherry",
    fn: function () {
        console.log(this.name); // Cherry
    }
}
a.fn();

在这个例子中,函数fn是对象a调用的,所以打印的值就是a中的name的值。

做一个小小改动:

//例3
var name = "windowsName";
var a = {
    name: "Cherry",
    fn: function () {
        console.log(this.name); // Cherry
    }
}
window.a.fn();

由于这里最后调用fn的对象还是a,所以打印出来仍是Cherry。

//例4
var name = "windowsName";
var a = {
    // name: "Cherry",
    fn: function () {
        console.log(this.name); // undefined
    }
}
window.a.fn();

这里打印undefined是由于最后调用fn的对象是a,但是a又没有name属性,所以打印出来是undefined

这个例子还说明了:this永远指向最后调用它的那个对象,因为最后调用fn的对象是a,所以就算a中没有name属性,也不会继续向上一个对象寻找this.name,而是直接输出undefined。

//例5
var name = "windowsName";
var a = {
    name: "Cherry",
    fn: function () {
        console.log(this.name); // windowsName
    }
}

var f = a.fn;
f();

这里你可能会有疑问,为什么不是 Cherry,这是因为虽然将 a 对象的 fn 方法赋值给变量 f 了,但是没有调用,再接着跟我念这一句话:“this 永远指向最后调用它的那个对象”,由于刚刚的 f 并没有调用,所以 fn() 最后仍然是被 window 调用的。所以 this 指向的也就是 window。

由以上五个例子我们可以看出,this 的指向并不是在创建的时候就可以确定的,在 es5 中,永远是this 永远指向最后调用它的那个对象

//例6
var name = "windowsName";

function fn() {
    var name = 'Cherry';
    innerFunction();

    function innerFunction() {
        console.log(this.name); // windowsName
    }
}

fn()

这里是由于:fn()前面没有对象,那就是windows对象,所以打印出来的是windowsName。

2、怎么改变this指向

改变 this 的指向总结有以下几种方法:

  • 使用 ES6 的箭头函数
  • 在函数内部使用 _this = this
  • 使用 applycallbind
  • new 实例化一个对象
//问题提出
var name = "windowsName";

var a = {
    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        }, 100);
    }

};

a.func2() // this.func1 is not a function

上例中,由于this.func1()外面的是setTimeout,也就相当于是window调用的,但是window对象并没有func1(),所以报错。要解决这个问题可以使用以下几种方法。

(1)使用ES6的箭头函数

箭头函数:众所周知,ES6的箭头函数可以避免ES5中使用this的坑的。箭头函数的this始终指向函数定义时的this,而非执行时。箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

var name = "windowsName";

var a = {
    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(() => {
            this.func1()
        }, 100);
    }

};

a.func2() // Cherry

(2)在函数内部使用_this = this

除了使用ES6的箭头函数,在函数内部使用 _this = this; 应该是最简单不会出错的方式了。先将调用这个函数的对象保存在 _this中,然后在函数中都使用这个 _this ,这样 _this就不会改变了。

var name = "windowsName";

var a = {

    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        var _this = this;
        setTimeout(function () {
            _this.func1()
        }, 100);
    }

};

a.func2() // Cherry

在这个例子中,在func2中,首先设置 var _this = this; ,这里面的this的对象是调用a.func2()中的对象 a ,所以 _this 现在指向的对象也是 a了,所以 _this.func1() 调用的是 a的方法func1。

(3)使用apply、call、bind

(a)使用apply

var a = {
    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        }.apply(a), 100);
    }

};

a.func2() // Cherry

(b)使用call

var a = {
    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        }.call(a), 100);
    }

};

a.func2() // Cherry

(c)使用bind

var a = {
    name: "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        }.bind(a)(), 100);
    }

};

a.func2() // Cherry

(d)apply、call、bind 区别

apply 和 call 的区别:

apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。注意:apply是数组apply是数组apply是数组

// apply
var a = {
    name: "Cherry",
    fn: function (a, b) {
        console.log(a + b)
    }
}

var b = a.fn;
b.apply(a, [1, 2]) // 3
// call
var a = {
    name: "Cherry",
    fn: function (a, b) {
        console.log(a + b)
    }
}

var b = a.fn;
b.call(a, 1, 2) // 3

bind 和 apply、call 区别:

我们将刚才的例子使用 bind 试一下:

// bind 没加()的情况
var a = {
    name: "Cherry",
    fn: function (a, b) {
        console.log(a + b)
    }
}

var b = a.fn;
b.bind(a, 1, 2);    //发现并没有输出

为啥呢:MDN上面的文档说明:

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

也就是说,bind是创建一个新的函数,我们需要手动调用,换句话就是需要在后面加上()

var a = {
    name: "Cherry",
    fn: function (a, b) {
        console.log(a + b)
    }
}

var b = a.fn;
b.bind(a, 1, 2)() // 3

(4)new 实例化一个对象

// 构造函数:
function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}

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

推荐阅读更多精彩内容

  • this 的指向 在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象...
    fenerchen阅读 538评论 0 9
  • 1.什么是this this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,只能...
    淡然7698阅读 164评论 0 0
  • 怎么改变 this 的指向 1、使用 ES6 的箭头函数 2、在函数内部使用_this = this 3、使用ap...
    SailingBytes阅读 185评论 0 0
  • 一 、关于this Javascript中的this经常让人糊涂,但总的一个原则就是,this总是指向一个对象,而...
    Mycro阅读 375评论 0 2
  • 班里的小轩每节课都要去厕所,有时真心不愿让孩子养成这种习惯,对孩子本人对同学、老师都没好处。 开学后由于普小六年级...
    珊珊来迟_868e阅读 148评论 0 14