关于this

this是什么

在函数运行时,基于调用位置的条件自动生成的内部对象,可以理解为动态绑定对象到this上。

需要强调的是:

  • 只针对函数,在函数内部使用
  • this由调用位置的上下文自动的决定,而不是函数声明的位置(代码书写的位置)
  • 必须是运行时才确定,而不是编写时
  • this绑定之后可理解为自动生成的内部对象

示例:

var foo = "golbal foo";  
var myObj = {foo : 'myObj foo'};  
var say = function(){  
    console.log(this.foo);  
}  
  
myObj.say = say;  
myObj.say(); //结果:myObj foo  
say(); //结果:golbal foo  ,相当于window.say(),内部的this->window对象

this的四个绑定规则及优先级

下面四个为this的绑定优先级规则,第一个优先级最高。判断执行流程需要做的就是找到函数的调用位置并判断使用哪条规则。

1. 函数是否通过new Base()方式绑定?如果是,this绑定新创建的对象

new的调用会自动执行下面代码(示例代码),Base函数中的this指向新创建的对象(一般):

var obj = new Base()

// 1. 创建(或者说构造)一个全新的对象;
var _obj = {}

// 2. 我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
_obj.__proto__ = Base.prototype

// 3. 我们将Base函数对象的this指针替换成_obj,然后再调用Base函数
var _return = Base.call(_obj)

// 4 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
if (typeof(_return) === 'object') {
    obj = _return
} else {
    obj = _obj
}

2. 函数是否通过callapplybind显式绑定?如果是,this绑定所指定的对象

强制将foo方法中的this绑定到obj对象上,即使后面再次更新绑定也不生效。

function foo(){  
    console.log(this.a);  
}  
var obj = { a:2 };  
var bar = function(){  
    foo.call(obj);  
};  
bar(); //2  
setTimeout( bar, 1000); //2  
bar.call( window ); //2  

注意:

bind绑定会返回新函数,对新函数无法更改内部的this,原因同上。但是对原函数可以随意切换绑定。

function base () {
  console.log(this.hello)
}
var a = {
  hello:'aaa'
}
var b = {
  hello:'bbb'
}

base.call(a) // aaa
base.call(b) // bbb
var bb = base.bind(b) // 强绑定,返回的bb函数已无法更改this
bb.call(a) // bbb
bb.call(b) // bbb
base.call(a) // aaa
base.call(b) // bbb

3. 函数是否在某个上下文对象中隐式调用?如果是,this绑定到那个上下文对象

function foo(){  
    console.log(this.a);  
}  
var obj1 = {  
    a : 2,  
    foo : foo  
}  
var obj2 = {  
    a : 1,  
    obj1 : obj1  
}  
obj2.obj1.foo();    //结果:2  

foo()执行时的上下文是obj1,因此函数内的this->obj1

注意:

隐式绑定会出现绑定丢失的问题,不过这个很好推理。

var a = "foo";  
function foo(){  
    console.log(this.a);  
}  
function doFoo(fn){     //var fn = obj.foo  
    fn();  
}  
var obj = {  
    a : 2,  
    foo : foo  
}  
doFoo(obj.foo); //"foo"  this->window

var bar = obj.foo
bar(); // "foo" 相当于:window.bar(), this->window
bar.call(obj); // "2" this->obj

setTimeout(obj.foo, 100);   //"foo"  

4. 上述全部不是,则this->window上,如果是严格模式,this->undefined

// 严格模式是?
var a = function ( ) {
    "use strict"
    //...
}

事件中的this

1. 作为DOM事件处理

在事件处理函数中的this指向绑定事件的对象event.currentTarget

document.getElementById('button').addEventListener('click',function (event) {
  console.log(this)
  console.log(event.target === event.currentTarget)
  console.log(event.target === this)
})

currentTarget:绑定事件的元素,恒等于this
target:触发事件的元素,可能是绑定事件元素的子元素接收到了事件

2. 作为内联事件处理

当代码在元素上进行调用处理,this指向的是这个DOM元素,this->$(button)

<button onclick="alert(this.tagName.toLowerCase());"> Show this</button>

function函数中返回,则this指向window,this->window

<button onclick="alert((function(){return this}()));">Show inner this</button>

IIFE中的this

不管IIFE写在哪,里面的this都指向window。相当于是在window下执行IIFE函数。此外,自执行函数返回值由内部函数返回。

注意点

1. 忽略this

nullundefined作为this的绑定对象传入callapplybind,调用时会被忽略,实际应用的是默认绑定规则this->window

function foo(){  
    console.log(this.a);  
}  
var a = 1;  
foo.call(null, 2);          // 1  this->window
foo.apply(undefined, [3]);  //1  this->window
foo.apply(window, [3]);  //1  this->window

2. 间接引用

function foo(){  
    console.log(this.a);  
}  
var a = 2;  
var o = { a : 3,foo : foo};  
var p = { a : 4};  
o.foo();            //3  
(p.foo = o.foo)();  //2 间接引用, 前面返回foo函数,相当于:(foo)(), this->window
var pfoo = o.foo;  
pfoo();         //2 隐式丢失  

3. 箭头函数

箭头函数中的this无法被修改,this指向由外层函数决定,常用于事件处理器或定时器等异步场景

function foo(){  
    setTimeout(()=>{  
        console.log(this.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj);  

等价于:

function foo(){  
    var self = this;  
    setTimeout(function(){  
        console.log(self.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj);  

补充

1. bind()实现?

function bind(f, o){  
    if(f.bind){  
        return f.bind(o);  
    }else{  
        return function(){  
            return f.apply(o, arguments);  
        }  
    }  
}  

2. 什么是严格模式?

为了向新版JS语法过度的模式。

放置位置

严格模式编译指示: "use strict"

  • 放置在脚本第一行
  • 声明在函数体中第一行
var a = function ( ) {
    "use strict"
    //...
}

与非严格模式的区别

  • 无法创建全局变量,变量必须使用var声明
"use strict"
var a = 123
console.log(a)
console.log(a === window.a)
  • 静默失败都会抛出错误
  • 禁止使用with
var obj= { };obj.a=1;obj.b=2;
with(obj){
  alert(a+b)
}

with的作用是将obj对象中的变量在{}中展开可直接访问,注意这没有影响window对象。类似于作用域拼接。

  • 严格模式下,eval内部有自己的作用域
  • 默认的this指向undefined,而不是window,避免window变量被污染
  • 禁止在函数内部使用callee、caller、arguments这些属性
  • 对象属性名唯一,函数传参名唯一
  • 禁止八进制表示法
  • 不允许对arguments赋值
  • arguments不在追踪参数的变化
  • 不能再非函数的代码块中声明函数
var a= 6;

if(a>2){
    function fn ( ) {
        alert('hi');
    }
    fn( );
} //报错
  • 禁止使用保留关键字

测试题

1. 为什么要使用this,而不是传参解决问题

  • 复用

在不同的对象环境下执行了它们,达到了复用的效果,而不用为了在不同的对象环境下执行而必须针对不同的对象环境写对应的函数了。

  • 模拟类

2. 基础call

function identify() {
    return this.name.toUpperCase();
}
function sayHello() {
    var greeting = "Hello, I'm " + identify.call( this );
    console.log( greeting );
}
var person1= {
    name: "Kyle"
};
var person2= {
    name: "Reader"
};
identify.call( person1); // KYLE
identify.call( person2); // READER
sayHello.call( person1); // Hello, I'm KYLE
sayHello.call( person2); // Hello, I'm READER

函数在哪里调用才决定了this到底引用的是啥

3. this不是指向函数本身

function fn(num) {
    console.log( "fn: " + num );
    // count用于记录fn的被调用次数
    this.count++;
}
fn.count = 0;
var i;
for (i=0; i<10; i++) {
    if (i > 5) {
        fn( i );
    }
}
// fn: 6
// fn: 7
// fn: 8
// fn: 9
 
console.log( fn.count ); // 0 -- 耶?咋不是4捏?
  • fn.count是函数本身的属性,因为函数也是对象
  • this.count是fn函数构造器中的变量, 也是全局变量,this->window

4. 继承+引用+this

function Parent() {
            this.a = 1;
            this.b = [1, 2, this.a]; // this.a只在函数体内存在,这里相当于设置“默认值”
            this.c = { demo: 5 };
            this.show = function () {
                console.log(this.a , this.b , this.c.demo );
            }
        }
 function Child() {
     this.a = 2;
     this.change = function () {
         // this中变量就近引用,如果没有就从原型链继续找
         this.b.push(this.a);
         this.a = this.b.length;
         this.c.demo = this.a++;
     }
 }
 Child.prototype = new Parent(); 
 var parent = new Parent();
 var child1 = new Child();
 var child2 = new Child();
 child1.a = 11;
 child2.a = 12;
 parent.show(); // 1 [1,2,1] 5
 child1.show(); // 11 [1,2,1] 5
 child2.show(); // 12 [1,2,1] 5
 child1.change(); // a->5, b->[1,2,1,11], c.demo->4,this就近+继承+引用
 child2.change(); // a->6, b->[1,2,1,11,12], c.demo->5,this就近+继承+引用
 parent.show(); // 1 [1,2,1] 5
 child1.show(); // 5 [1,2,1,11,12] 5
 child2.show(); // 6 [1,2,1,11,12] 5

需要注意的是:

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

推荐阅读更多精彩内容

  • 从学习前端开始已经大概要两个月了,这几天突然发现this并没有想想中这么简单,之前很长一段时间对于this的认识停...
    悲欢自饮丶阅读 266评论 0 0
  • this作为全局对象window的几种情况 作为函数调用的this 作为内部嵌套函数的this 在setTimeo...
    羞涩的涩阅读 696评论 0 0
  • 什么是this this是JS中一个非常重要的关键字。this 就是你 call 一个函数时,传入的 contex...
    字母31阅读 170评论 0 1
  • 〈光明〉 我始终相信 不远处 会有 我要的光明 让它永永远远 照亮大地 〈木香〉 小河桥的檀木 缕缕幽香 被我拈下...
    冰影灯下阅读 330评论 1 4
  • 念与酒 稀烛残光 独影微晃 烧酒入喉 我一人享 月落屋房 孤芳自赏 回...
    孤十四阅读 143评论 0 2