涅槃重生(一):JS中this的那些事

前言

经历了一年多的各种尝试之后,最终还是决定回到程序员的行列中来。一年中的许许多多事情不是由我自己主动结束就是最终走向了失败幻灭。

从小开始教科书就教育我们做人不能三心二意、眼高手低,成功露出水面的诀窍也只能是他们在水面下苦苦构筑的巨大冰山。回想从一开始起,就是以工程化为目的,奉行拿来主义,大部分情况下不知道自己放置于工程中的部分代码所引用的库中究竟是用什么手法达到我想要的目的的。

故以此为契机重拾博客写作,记录我重新学习的全过程,计划中的学习内容当然不仅于前端。苦思冥想很久,决定以昨天面试官问的JS中的this问题作为系列博客的第一篇吧!

JS中为什么要有this

根据红宝书介绍,在函数的内部有两个特殊的对象: arguments 和 this,其中this引用的是函数据以执行的环境对象(也译为执行上下文)。我们来看一个红宝书中的例子:

//例一

window.fruit = "apple";
var a = {fruit: "orange"};

function say(){
    alert(this.fruit);
}

say();    //"apple"

a.say = say;
a.say();  //"orange"    

例子中的 say() 运行与全局域下,默认指向 window 对象,相当于执行 window.say(),所以这时候 this.fruit 指向 window.fruit。而 a.say() 执行时此时 say() 执行于a对象之中,所以 this 对象指向 a 对象,这个时候当然输出的就是 a.fruit 的值了。

通过这个例子我们已经简单了解了 this 是什么了,那么它是怎么在我们的日常编程中起到举足轻重的作用呢?

我们通过一个打印字符例子来看一看:

//例二

//实现打印字符
var str = "Hello"

function PrintA(){
    if(){
        console.log(this.str);
    }else{
        console.log("str not exist");
    }
}

PrintA();//"Hello"

我们通过调用 this 获取到了上下文对象,输出了其中 str 字段的值。这样我们的函数的返回值就不是固定的值了,而是由执行时候上下文的具体数据决定,大大提高了函数的灵活性!

This的具体指向判定

This 的具体指向一直是各路面试的重点问题,下面我带大家来捋一捋 this 指向的原理
这里有一个很重要的原理请大家务必谨记!

This的指向在创建时无法确定,只有具体调用时才能决定

This 的指向共有以下几种情况:
1、如果一个函数有 this,且未被上一级对象所调用,那么这个 this 指向顶层对象,一般为window。
2、如果一个函数有 this,且被上一级对象调用,那么这个 this 指向上一级对象。
3、如果一个函数有 this,这个函数被包含于多层对象中,尽管是被最外层对象所调用,但this仍指向上一级对象。

在理解这几种情况之前,需要先明确我们的函数本质上也是顶层对象的一个属性,我们常用的 alert 函数其实也是 window 对象的一个属性,完整写法应为 window.alert(),若 alert 中存在 this,那么很明显应该指向 window 对象。

在例一中已经有1、2的情况了,那么3又是什么情况会出现呢?

//例三

//本例 this指针指向上一级对象即 b,但 b中无 a属性
//故结果为 undefined
var sample = {
    a: 10,
    b: {
        fn: function() {
            console.log(this.a);
        }
    }
}

sample.b.fn();//undefined

以上举的都是比较简单的 this 的应用,下面我们来看看实际应用中 this 都是如何指向的。

不同调用方式的 This 指向

一般地,函数调用有以下几种方式:

  • 普通函数调用
  • 以方法的形式调用
  • 构造函数调用
  • 函数中return对象
  • 箭头函数调用(ES6)

下面来看看几种调用的具体例子

普通函数调用

function Normal(){
    this.number = 10;
    console.log(this); //window
    console.log(this.number);//10
}

Normal(); //window 10

以方法的形式调用

var str = "hello";
var obj = {
    str = "hi",
    fn: function(){
        console.log(this.str); 
    }
}

obj.fn(); //"hi"

构造函数调用

function Example(num){
    this.num = num;
}

var num = Example(10);

console.log(num.num); //undefined
console.log(window.num); //10

函数中含return一个对象
*NULL对象仍指向原函数实例

//此时this指向返回的对象
function Obj1(){
    this.user = "LiMing";
    console.log(this); //{user: "LiMing"}
    return {};
}

var a = new Obj1();
console.log(a.user); //undefined
function Obj2(){
    this.user = "LiMing";
    console.log(this);//{user: "LiMing"}
    return 1;
}

var a = new Obj2();
console.log(a.user); //LiMing
function Obj3(){
    this.user = "LiMing";
    console.log(this); //{user: "LiMing"}
    return undefined;
}

var a = new Obj3();
console.log(a.user); //LiMing

箭头函数调用

//箭头函数this始终指向其定义时的上下文对象
//箭头函数this一旦定义无法改变
function Person(){
    this.age = 0;       //此this指向全局
    setInterval(()=>{
        this.age++;     //此this指向构造函数的变量,所以等于指向全局
    },1000);
}

var person = new Person;

This 指向修改

call&apply
值得注意的是,call 方法和 apply 方法作用完全相同,只是接收的参数格式不同
fun.apply( this指向, [一个数组或类数组的带下标的集合])
fun.call( this指向, ...随便传)

来个例子

var obj1 = {name: "John"};
var obj2 = {name: "Clancy"};

this.name = "Jenny";

var getName = function(){
    console.log(this.name);
}

getName(); //Jenny
getName.call(obj1); //John
getName.apply(obj2); //Clancy

Function.prototype.bind()

bind方法会创建一个新函数,当调用这个新函数时,新函数会以创建他时的第一个参数为 this ,后面传入的参数当做是原函数的参数进行调用。
这是 API 上对于 bind 方法的解释,也就是用于改变函数内部的 this 指向。下面来个例子

var name = "John";

function Person(name){
    this.name = name;
    this.sayName = function(){
        setTimeout(function(){
            console.log(this.name);
        },bind(this),500) //此时该匿名函数this始终指向Person对象
    }
} 

var person = new Person("Clancy");
person.sayName(); //"Clancy"

Eval方法
该函数执行时,this 会绑定到当前作用域的对象上
这个方法由于性能问题所以基本不考虑了,但是还是以防万一面试的时候被问到

var name = "John";
var person = function(){
    name: "Clancy",
    sayName: function(){
        eval(this.name);
    }
}

person.sayName(); //"Clancy"
var a = person.sayName();
a(); //"John"


以上便是关于 this 的所有总结了,其中有些地方还是需要细细琢磨。
最近获得的最大的人生经验,就是要温故而知新,很可能有些你学过的知识你并没有真正的掌握。
所以偶尔复习一下才是上上之策。


Clancy Lin
2019.1.8
I can be whatever I want to be

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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