Go to comments

JavaScript this

this

this是单独的知识块,这块知识探究完了,基本上底层的大多数难的问题已经全解决了,还差一个数组(这个简单了)。this一共就四条知识点(非常简单),我们探究在不同情况下this到底指向的是什么,把指向弄清楚了,以后this用的越来越灵活了而且它很强大。


一、函数预编译过程 this —> window

函数预编译过程中,this指向的是window

function test(c){

    var a = 123;

    function b(){}

}

test(1);


/*----------------------------------------------------------------

01/
函数执行前要经过预编译,预编译会把一些东西该提升的提升。
预编译的时候形成一个AO,AO里面形参实参相统一,把函数b挂上面,这是预编译的一个过程。

AO{
    c : 1,
    a : undefined,
    b : function(){}
}


02/
预编译的时候还会有一个过程,在预编译形成之后,这个AO里不只是有这些东西,
如果只有这么些东西,那arguments上哪访问的?arguments实参列表是放到AO里面的。

AO{
    arguments : [1], // 在预编译时候,arguments就是这样的了
    c : 1,
    a : undefined,
    b : function(){}
}


03/
然后还有,在预编译过程中,OA里面还有一个this,这个this指向的是window

AO{
    arguments : [1],
    this : window,  // 这个this指向的是window
    c : 1,
    a : undefined,
    b : function(){}
}

----------------------------------------------------------------*/


如果把test当做构造函数 new test(1) 操作,这时候AO里面的this会发生一个变化

function test(c){

    var a = 123;

    function b(){}

}

new test(1);



/*----------------------------------------------------------------

01/
在new操作后,在test函数体内第一行隐式的"var this = Object.create(test.prototype)"

function test(c){

    // var this = Object.create(test.prototype); 其实最精准的应该是这样

    // 直接这样写差不多this等于一个对象,最精准的是上面的写法
    // var this = {
    //     __proto__ : test.prototype
    // }

    var a = 123;

    function b(){}

}
new test(1);


02/
"new test"操作后,test函数第一行隐式"var this = Object.create(test.prototype)",就会把AO里面原来的this指向window替换掉(但是不new操作原来的this就指向的是windows)

AO{
    arguments : [1],
    this : Object.create(test.prototype), 替换原来的this指向
    c : 1,
    a : undefined,
    b : function(){}
}

function test(c){
    // var this = Object.create(test.prototype);
    var a = 123;
    function b(){}
}

----------------------------------------------------------------*/

这是第一条语法预编译过程中this指向window,测验一下

function test(){

    console.log(this);

}

test(); // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

直接打印出window也是这个

console.log(window); // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

window是对象,window是GO,window就是全局

二、全局作用域里 this —> window

在全局作用域里,在GO里面也有一个this,它天生指向也是window

在全局直接打印this,结果还是window

console.log(this); // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

三、call/apply 可以改变函数运行时this指向

call/apply可以改变函数运行时this的指向,

不管预编译是什么,call/apply一下就变了。

四、obj.func(); (func()里面的this指向obj)

第四条是一个这样的小例子。

var obj = {
    a : function(){
        console.log(this.name); // 2. a函数里面this是谁?就看谁调用的a
    },
    name : "abc"
}

obj.a(); // 1. "obj.a()"执行,a函数里面有个this,这个this是谁?
         // 3. 如果obj调用的a函数,那this就是obj

第四条讲述的是,谁调用的这个方法,这个方法里的this执向的就是谁。

如果没人调用一个方法,这个方法自己像定义的函数,空执行呢?

function test(){
	console.log(this);
}

test();

test方法在全局的范围内,执行test()它里面的this是谁?没人调用test自己空执行,自己空执行走预编译,预编译里面this指向window。


obj.a() 这样对象调用a函数,a函数还是会走预编译。但是走完预编译后,它里面的this会再一次变化成调用者,这种obj.a()情况下是谁调用的a函数this就指向谁

var obj = {
    a : function(){
        console.log(this.name);
    },
    name : "abc"
}

obj.a(); // abc

五、this笔试题讲解

这就是考this的,这道题弄明白后,this就一马平川,一点问题都不会有,

请写出代码输出结果

var name = "222";

var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var fun = a.say;
fun();
a.say();

var b = {
    name : "333",
    say : function(fun){
        fun();
    }
}

b.say(a.say);
b.say = a.say;
b.say();

// 打印结果
//	222
//	111
//
//	222
//	333



/*------------------------------------------------------------

01/
var fun = a.say;
1). "a.say"是个function,
2). "a.say"代表这个function的函数引用,代表这个函数体
3). 然后相当于把a.say放到了变量fun上了,fun()在全局范围内执行
4). 就相当于把这个函数(say的值)拿到全局里去执行,跟谁调用的它没关系
	function(){
		console.log(this.name);
	}
5). "fun()"在全局执行,也没人调用它,所以肯定打印222,函数里面的this指向window



02/
"a.say()"就是a调用say方法执行,这个最简单返回111



03/
第三个了就稍微有点复杂了b.say(a.say);
1). b.say()执行,既然b调用这个方法,这个方法里的this指向的是b
	var b = {
		name : "333",
		say : function(fun){
			// this -> b 这个方法里的this指向b
			fun();
		}
	}

2). 然后b.say(a.say)执行了里面的this是b,但是里面传了一个参数,传的参数是"a.say",

3). 传的是把对象a里面的say:function方法,把这个方法挪到对象b里面的say:function方法里了,在里面去执行
	var b = {
		name : "333",
		say : function(fun){
			// this -> b
			
			// 传这里面来,在这执行
			function(){
				 console.log(this.name);
			}
		}
	}

4). 对象b的say方法里面的this确实是b,但是没写this.fun()执行啊!
	直接是fun()执行,谁也没调用fun,所以fun()执行走的是预编译环节。
	预编译this指向的是window所以返回222
	b.say(a.say)返回222

	var b = {
		name : "333",
		say : function(fun){
			// this -> b this确诊指向b
			fun() // 但是这里没写this.fun()执行
		}
	}



04/
"b.say = a.say"把a.say的函数体拷贝覆盖掉b.say的函数体,然后在调用b.say()
	var a = {
		name : "111",
		say : function(){  // 1. 把这个拷贝到
			console.log(this.name);
		}
	}

	var b = {
		name : "333",
		say : function(){  // 2. 拷贝覆盖到这
			console.log(this.name);
		}
	}

	b.say = a.say;
	b.say(); 执行打印,结果返回333

------------------------------------------------------------*/


b.say(a.say) 第三个重来再说一遍

var name = "222";

var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var b = {
    name : "333",
    say : function(fun){
		fun();
    }
}

b.say(a.say);


/*------------------------------------------------------------

01/
首先理解"a.say"当做一个参数传进来了,这个参数长什么样子?
b.say(
	// 相当于这个样子,传进来了。
	function(){
		console.log(this.name);
	}
);

02/
传进来之后,在b.say函数体里面执行,b.say()调用里面的this是b,里面console.log(this)打印的肯定是b
var b = {
    name : "333",
    say : function(fun){

        // this -> b
        // 传进来的a.say函数体,在这里面执行
        // console.log(b) -> b
        fun();

    }
}


03
然而,下面fun()执行,也就是说参数执行(参数是一个函数),
函数体执行,只不过是在b.say的方法体里执行,没有人调用它是空执行,空执行走预编译的过程,
没人调动就是预编译,跟环境没有关系。
var b = {
    name : "333",
    say : function(fun){
        // this -> b
        // console.log(b) -> b

        // 传进来的a.say函数体在这执行,
        // fun = function(){
        //      console.log(this.name);
        // }
		
		// 没人调用就是预编译,跟环境没有关系,this指向的window
        fun();

    }
}

04/
最后fun()执行,函数执行只不过是在对象b内的方法体里执行,谁也没调用他,没人调走的是预编译
b.say(a.say); // 222

------------------------------------------------------------*/

问题天使婉絮

var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var b = {
    name : "333",
    say : function(fun){
		
        console.log(this); // b.say()执行this的指向的是谁?打印b "{name: "333", say: ƒ}"
 
    }
}

b.say();

在全局定义一个test函数打印this,在b.say里面执行test(),执行结果是什么?

function test(){
    console.log(this);
}

var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var b = {
    name : "333",
    say : function(fun){

        // 在这里面执行test()函数,执行的结果是什么?打印的结果是window
        test();
        
    }
}

b.say(); // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

为什么打印的是window?

再里面test()就是自己执行,谁也没调用它,跟b.say里面的this没关系,b.say里面的this环境是b的,但test跟这个this没关系,只是在b.say函数体里面执行test()这个函数,走的是预编译环节。

因为确实没人调用test(),只是在这个区域里面执行,也不是被人调用,test()相当于正常执行,正常执行走预编译环节,预编译环境this指向的是window,

接着不用test()了用fun()执行

var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var b = {
    name : "333",
    say : function(fun){
        fun();
    }
}

b.say(a.say); // 222

这个fun就是a.say的方法体,把a.say的方法体到b.say这里,然后执行这个a.say方法体。

相当于在b.say里面,执行了a.say这样的一个方法体,走的还是预编译过程,this指向的还是window,所以打印出来的是222


问题天使的问题,b.say = a.say然后执行b.say()

var name = "222";
var a = {
    name : "111",
    say : function(){
        console.log(this.name);
    }
}

var b = {
    name : "333",

    // 原生b.say被覆盖了
    // say : function(fun){
    //     fun();
    // }
	
    // 相当于把a.say的方法体拿过来,覆盖掉b.say的方法体。
    // b.say的方法体已经和a.say是一样的了
    // 然后b调用b.say()方法,里面的this的指向肯定是b,所以打印的是333
    
    say : function(){
        console.log(this.name);
    }
}

b.say = a.say;
b.say(); // 333

学习群里图片一张

微信图片_20211225112220.jpg





Leave a comment 0 Comments.

Leave a Reply

换一张