美文网首页
关于this

关于this

作者: 烈风裘 | 来源:发表于2018-01-01 15:00 被阅读48次

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

相关文章

  • 关于关于关于

    他们爱他们自己,不爱你 他们爱你是他们的母亲妻子女儿姐妹 他们不爱你 直到你死的时候,爱才产生,与遗忘同时 那也不...

  • 光明人生

    关于出生 关于成长 关于求学 关于青春期 关于恋爱 关于择业 关于婚姻 关于养生 关于家庭 关于人际 关于教子 关...

  • 「梦雅的简动力」打卡计时65天

    * 关于人生 * 关于梦想 * 关于方向 * 关于创业 * 关于投资 * 关于成败 * 关于个性 * 关于高度 *...

  • 关于

    关于两个人? 关于100步? 关于回头? 关于深情? 关于家庭? 关于孩子? 关于成长? 关于伤痛? 关于怀抱? ...

  • 2017新手妈妈年终总结

    关于购物 关于体重 关于减肥 关于纪念日 关于生活态度 关于上班 关于职场晋升加薪 关于睡眠 关于抱孩子 关于发型...

  • 2018-11-28

    关于流浪、关于随心、关于自由、关于世俗、关于规则、关于坦诚、关于真我、关于好奇心、关于对这整个世界的态度、关于整个...

  • 一首歌的时间

    认真的 想理出点思绪 关于今天关于明天 关于工作关于梦想 关于冬天关于夜晚 关于阳光关于浪花 关于木马关于窗花 关...

  • 最近的各种关于

    关于运动,关于中文阅读,关于英文听力,关于口算。 关于专注,关于目标,关于举家迁移。 关于对正确的过于执着,关于对...

  • 『关于』

    关于雨落,关于天晴; 关于入夜,关于天明; 关于齐眉,关于耳鬓; 关于缘定,关于今生。 (早安~诸君!)

  • 关于关于的关于

    关于这篇文章究竟该起个什么名字,想了很长时间也没答案,于是只能暂且搁置,等到写完之后再说。其实不仅是文章的名字,就...

网友评论

      本文标题:关于this

      本文链接:https://www.haomeiwen.com/subject/bijtnxtx.html