搞懂this、call、apply

this

this 指向的是一个对象,只用当函数执行时才能确定this指向的对象,与函数声明的环境无关。

  • this 指向
    有4中情况:对象的方法调用、普通函数调用、构造函数实例化、call和apply

1.对象的方法调用

//指向对象本身
    var obj = {
        name: 'bby',
        getName: function(){
            return this.name;
        }
    };
    obj.getName() // bby
  1. 普通函数调用
// 一般指向全局对象,window
window.name = 'globalName';
var getName = function(){
    return this.name;
};
console.log( getName() ); // globalName

当调用一个对象的方法时,this本来是指向该对象,怎样修改可以指向 window?

window.name = 'globalName';
var myObject = {
    name: 'sven',
    getName: function(){
        return this.name;
    }
};

var getName = myObject.getName;
console.log( getName() ); // globalName

在全局域中定义个变量,指向对象的方法引用,最后在执行个变量,此时this指向window。
思考:怎么修改上面的代码,this还是指向myObject ?这个其实要求访问私有变量,可以使用闭包,返回一个函数:

window.name = 'globalName';

var myObject = {
    name: 'bby',
    getName: function(){
        var _this = this;
        return function(){
            console.log(_this.name)
        }
    }
};

var getName = myObject.getName();
console.log( getName() ); // bby   

接下来还有一个例子,不是很好理解,直接看代码:

<html>
    <body>
        <div id="div1">我是一个div</div>
    </body>
    <script>
    window.id = 'window';
    document.getElementById( 'div1' ).onclick = function(){
        alert ( this.id ); // 输出:'div1'
        var callback = function(){
            alert ( this.id ); // 输出:'window'
        }
        callback();
    };
    </script>
</html>

点击事件的this指向的是点击的对象,既然 callback 是在绑定的函数中定义的,那this应该也是指向#div1对象?
这样理解是错的,因为this的指向与函数声明环境无关,只有函数执行环境有关,问题又来了,callback是在绑定函数中执行的,按理说 此时的 this 应该指向 #div1 对象?但实际上指向的是window,为什么?
好吧,我暂时也没有想清楚,不过怎么修改,到时会的,用变量把this保存下来。

<html>
    <body>
        <div id="div1">我是一个div</div>
    </body>
    <script>
    window.id = 'window';
    document.getElementById( 'div1' ).onclick = function(){
        var that = this; // (1) 保存div 的引用
        var callback = function(){
            alert ( that.id ); // (2) 输出:'div1'
        }
        callback();// (3)
    };
    </script>
</html>

改变(1)(2)(3) 处代码的顺序,可以得到不同的结果,这个与预解析有关。

  1. 构造函数的调用
    this 指向的就是实例对象,但是需要理解new操作的实际过程,简单理解分为以下4步:

1.创建一个对象obj ; 2. obj.proto 指向函数的原型对象;3. 指向构造函数,并将this指向obj ; 4.判读执行返回的结果,如果是个对象,就返回这个对象,不是的话,返回obj对象

看下面这个例子,显示地返回对象:

    var MyClass = function(){
        this.name = 'bby';
        return { // 显式地返回一个对象
            name: '365'
        }
    };
    var obj = new MyClass();
    alert ( obj.name ); // 输出:365
  1. Function.prototype.call 或Function.prototype.apply 调用

可以动态改变传入函数的this


  • 丢失 this

写代码过程中,经常遇到this指向不明确的,原因是一开始我们就忽略了this的指向。
比如:封装document.getElementById(),一般都会习惯这样写:

var getId = function( id ){
    return document.getElementById( id );
};
getId( 'div1' );

有没有想过,下面这样的写法:

var getId = document.getElementById;
getId( 'div1' );

这种写法会报错,因为getId 和document.getElementById 指向同一个引用,document.getElementById方法内部现实时需要用到this,且this要指向document。但是直接用上面的方法,执行getId() , this 指向的是window 。
所以可以这样修改:

var getId = document.getElementById;
getId.apply(document, ['div1'] );

call 和 apply

call 和 apply 比较常用,基本区别就是传参的形式不同。

  • call 和 apply 的用途
    有三种情况:一是改变this指向;二是封装Function.prototype.bind();三是借用其他对象的方法。
  1. 改变 this 指向
    场景一:div 上绑定onclick 事件,this 指向的是div。如果onclick中定义了函数func,func调用时,func 内部 this指向window;这时可以修改this指向。
document.getElementById('div1').onclick = function(){
    console.log(this.id); // div1
    var func = function(){
        console.log(this.id) // undefined
    }
    func()
}

// 改变 this 指向
document.getElementById('div1').onclick = function(){
    console.log(this.id); // div1
    var func = function(){
        console.log(this.id) // undefined
    }
    func.call(this)
}

2.Function.prototype.bind()
首先将 this 保存,返回一个新函数fn ,fn执行时,实际上执行的是原函数。

Function.prototype.bind = function(){
    var self = this, // 保存原函数
    context = [].shift.call( arguments ), // 需要绑定的this 上下文
    args = [].slice.call( arguments ); // 剩余的参数转成数组
    return function(){ // 返回一个新的函数
        return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) );
            // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this
            // 并且组合两次分别传入的参数,作为新函数的参数
    }
};

var obj = {
    name: 'sven'
};

var func = function( a, b, c, d ){
    alert ( this.name ); // 输出:sven
    alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 );

func( 3, 4 );
  1. 借用其他对象的方法
    场景一:借用构造函数,用在构造函数继承
var A = function( name ){
    this.name = name;
};

var B = function(){
    A.apply( this, arguments );
};

B.prototype.getName = function(){
    return this.name;
};

var b = new B( 'sven' );
console.log( b.getName() ); // 输出: 'sven'

场景二:arguments 类数组 使用数组的方法

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

推荐阅读更多精彩内容