观感度:????????????????????
口味:清爽绿豆
烹饪时间:15min
你皮任你皮,我当你瓜皮。
众所周知,this
在 JavaScript 中的指向一直很难让人理解,想要学好 JavaScript,this
也是我们必须要搞清楚的。其实,this
并没有那么难,本文将力争带你治疗 this
的“皮”。
首先,来科普三个问题!
this 是什么?
this
是声明函数时附加的参数,指向特定的对象,也就是隐藏参数。
比如 LOL 中各种隐藏的彩蛋,当水晶先锋斯卡纳在草丛里停留超过 15 秒不动,会模仿宠物小精灵。
“皮卡!皮卡!皮卡丘!”
“斯卡!斯卡!斯卡纳!”
好,相信大家已经理解什么是 this
了,就是个隐藏参数,没有多么的神奇,其实 每个函数都可以访问this
。
为什么要使用 this?
this
提供了一种更加优雅的方式来隐式的传递对象引用。
通俗地说:就是说我们可以把 API 设计的更加简洁而且易于复用。
说人话:那就是 this
可以帮我们省略参数。
this 的指向
只需要理解并记住一句话,外加几种小情况,大家就可以完完全全的理解this
。
好,注意听讲。
这句话就是“this
的指向在函数声明的时候是不会被确定的,只有函数执行的时候才被确定,this
最终指向的是调用它的对象”。
有人说这也太长了,记不住。
好,那缩短点。
一句话。
“this
的指向决定于函数的调用方式”。
总结:
1.
this
是声明函数时附加的参数,指向特定的对象,也就是隐藏参数。2.
this
可以帮我们省略参数。3.
this
的指向决定于函数的调用方式。
是不是很简单,搞清楚了这三点,我们下面将从八种情况来彻底理解 this
的指向问题。
直接上代码。
一、直到世界尽头
对,没错,有没有想起灌篮高手主题曲
我们首先要了解一下世界观,分为三种情况。
1. 在非严格模式下,浏览器中的尽头当然就是
window
。2. 在严格模式下也就是开启了
"use strict"
的情况下,尽头就是undefined
。3.
node
的全局环境中尽头是global
。
下文中情况主要从第一种非严格模式下来对 this
的指向进行解释和说明。
// 我们来看下面的两个????。function demo(){
var user =“前端食堂”;
console.log(this.user); // undefined
console.log(this); // window
}
demo();
// 这里的函数 demo 实际上是被 window 对象使用点语法所点出来的,如下:function demo(){
var user =“前端食堂”;
console.log(this.user); // undefined
console.log(this); // window
}
window.demo();
// 可以发现和上面的代码结果一样
二、画龙点睛
var obj = {
user:”前端食堂”,
fn:function(){console.log(this.user); // 前端食堂
}
}
obj.fn();
// 这里的 this 指向对象 obj
// 注意看最后一行调用函数的代码
// obj.fn();
// 重要的事情说两遍!!// this 的指向在函数创建时是决定不了的
// 在调用的时候才可以决定,谁调用就指向谁
// this 的指向在函数创建时是决定不了的
// 在调用的时候才可以决定,谁调用就指向谁
三、点石成金
// 其实以上两点说的还不够准确,我们接着往下看
var obj = {
user:”前端食堂”,
fn:function(){console.log(this.user); // 前端食堂
}
}
window.obj.fn();
// 这段代码跟上面的代码几乎是一样的
// 但是这里为什么没有指向 window 呢?// 按你上面说的不是 window 调用的方法吗?// 大家先停下来打个 debugger 思考一下为什么
// 想不明白没关系我们带着疑问来看下段代码
var obj = {
a:1,
b:{
a:2,
fn:function(){console.log(this.a); // 2
}
}
}
obj.b.fn();
// 这里执行的时候同样是对象 obj 通过点语法进行的执行
// 但是 this 同样没有指向 window,这是为什么呢?// 好,我们有几种情况需要记住:// 1. 如果一个函数中有 this
// 但是它没有被上一级的对象所调用
// 那么 this 就会指向 window(非严格模式下)// 2. 如果一个函数中有 this
// 这个函数又被上一级的对象所调用
// 那么 this 就会指向上一级的对象
// 3. 如果一个函数中有 this
// 这个函数中包含多个对象
// 尽管这个函数是被最外层的对象所调用
// this 却会指向它的上一级对象
var obj = {
a:1;
b:{
// a:2,
fn:function(){console.log(this.a); // undefined
}
}
}
obj.b.fn();
// 我们可以看到,对象 b 中没有属性 a,这个 this 指向
// 的也是对象 b,因为 this 只会指向它的上一级对象
// 不管这个对象中有没有 this 要的东西
// 我们再来看一种情况
var obj = {
a:1,
b:{
a:2,
fn:function(){console.log(this.a); // undefined
console.log(this); // window
}
}
}
var demo = obj.b.fn;
demo();
// 在上面的代码中,this 指向的是 window
// 你们可能会觉得很奇怪
// 其实是这样的,有一句话很关键,再次敲黑板
// this 永远指向的都是最后调用它的对象
// 也就是看它执行的时候是谁调用的
// 上面的例子中虽然函数 fn 是被对象 b 所引用了
// 但是在将 fn 赋值给变量 demo 的时候并没有执行
// 所以最终 this 指向的是 window
四、青梅竹马
function returnThis(){return this;}
var user = {name:"前端食堂"};
returnThis(); // window
returnThis.call(user); // 前端食堂
returnThis.apply(user) ; // 前端食堂
// 这里就是 Object.prototype.call
// 和 Object.prototype.apply 方法
// 他们可以通过参数来指定 this
五、矢志不渝
function returnThis(){return this;}
var user1 = {name:"前端食堂"};
var user1returnThis = returnThis.bind(user1);
user1returnThis(); // 前端食堂
var user2 = {name:"前端小食堂"};
user1returnThis.call(user2); // still 前端食堂
// Object.prototype.bind 通过一个新函数来提供了永久的绑定
// 而且会覆盖 call 和 apply 的指向
六、乾坤大挪移
function Fn(){this.user = "前端食堂";}
var demo = new Fn();
console.log(demo.user); // 前端食堂
// 这里 new 关键字改变了 this 的指向
// new 关键字创建了一个对象实例
// 所以可以通过对象 demo 点语法点出函数 Fn 里面的 user
// 这个 this 指向对象 demo
// 注意:这里 new 会覆盖 bind 的绑定
function demo(){console.log(this);
}
demo(); // window
new demo(); // demo
var user1 = {name:"前端食堂"};
demo.call(user1); // 前端食堂
var user2 = demo.bind(user1);
user2(); // 前端食堂
new user2(); // demo
七、爱转角遇上 return
// 当 this 遇上 return 时
function fn(){
this.user = "前端食堂";
return{};}
var a = new fn;
console.log(a.user); // undefined
function fn(){
this.user = "前端食堂";
return function(){};
}
var a = new fn;
console.log(a.user); // undefined
function fn(){
this.user = "前端食堂";
return 1;
}
var a = new fn;
console.log(a.user); // 前端食堂
function fn(){
this.user = "前端食堂";
return undefined;
}
var a = new fn;
console.log(a.user); // 前端食堂
// 总结:如果返回值是一个对象
// 那么 this 指向就是返回的对象
// 如果返回值不是一个对象
// 那么 this 还是指向函数的实例
// null 比较特殊,虽然它是对象
// 但是这里 this 还是指向那个函数的实例
function fn(){
this.user = "前端食堂";
return null;
}
var a = new fn;
console.log(a.user); // 前端食堂
八、英雄登场
// 最后我们介绍一种在 ES6 中的箭头函数
// 这个箭头函数中的 this 被加里奥的英雄登场锤的不行
// 皮不起来了
// 而且,在代码运行前就已经被确定了下来
// 谁也不能把它覆盖
// 这样是为了方便让回调函数中 this 使用当前的作用域
// 让 this 指针更加的清晰
// 所以对于箭头函数中的 this 指向
// 我们只要看它创建的位置即可
function callback(qdx){qdx();
}
callback(()=>{console.log(this)}); // window
var user = {
name:"前端食堂",
callback:callback,
callback1(){callback(()=>{console.log(this)});
}
}
user.callback(()=>{console.log(this)}); // still window
user.callback1(()=>{console.log(this)}); // user
怎么样?this 其实不过如此吧。再皮也要治住他~
交流
欢迎来我的个人公众号交流,优质原创文章将同步推送。后台回复 福利,即可领取福利,你懂得~
你的前端食堂,记得按时吃饭。