JavaScript this
this
1.函数预编译过程 this —> window
2.全局作用域里 this —> window
3.call/apply 可以改变函数运行时this指向
4.obj.func(); (func()里面的this指向obj)
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