Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this: 嘿嘿嘿,你怕不怕我? #8

Open
lizhongzhen11 opened this issue Jul 3, 2018 · 0 comments
Open

this: 嘿嘿嘿,你怕不怕我? #8

lizhongzhen11 opened this issue Jul 3, 2018 · 0 comments
Labels
js基础 Good for newcomers

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Jul 3, 2018

怕!(认真脸)

一开始想自己写关于this总结的,奈何自己水平有限,写出来也不够全面系统,所以这里为了this单独开一个issue,用于收罗我所遇到的关于讲解this较好的文章,最好做到由浅入深,如果有看客能看到并有更好的资源,请在评论里贴出来,让我加进来,哈哈哈哈哈,大家互帮互助嘛~

  1. this 的值到底是什么?一次说清楚 by 方应杭
  2. 你怎么还没搞懂 this? by 方应杭
  3. 深入浅出 JavaScript 关键词 -- this 重点看
  4. JavaScript中的this陷阱的最全收集--没有之一

真的明白了吗?巩固下!

例1:

var obj1 = {
  fn: function() {
    console.log(this)
  }
}
obj1.fn(); // obj1
var o = obj.fn;
o(); // window 非严格模式下

能分别解释吗?
我尝试下:

  1. obj1.fn(); 打印输出obj1这个对象,因为此时fnobj1对象调用,所以this指向obj1
  2. o()在非严格模式下打印输出window,因为此时变量o引用obj1对象的fn属性所指向的函数,即此时oobj1.fn都只是指向存在内部的函数的引用,所以,此时直接o()相当于window.o()即window在调用堆中的函数,所以函数内打印的thiswindow。这里的var o = obj1.fn其实并不是保存obj1对象!!!而是告诉o,你可以跟我的fn属性引用同一个函数,但咱两不一样。

例2:

var obj2 = {
  fn: () => { console.log(this) }
}
obj2.fn(); // window 非严格模式下

例2为什么是window呢?
老实说,我以前也一直不知道为什么,只会死记硬背,知道我看了深入浅出 JavaScript 关键词 -- this一文,文中重点加粗的强调:

箭头函数按词法作用域来绑定它的上下文,所以 this 实际上会引用到原来的上下文

词法作用域?什么鬼???
一开始我也懵逼的,但是我依稀记得好像看过那么一丢丢《你不知道的js》,书上就有,我还写过一篇博客来记录,可惜当时菜,其实没怎么懂,接下去的内容又没去看,所以记忆不深,怪自己!

词法作用域在你在写代码时将变量写在哪里来决定的,而不是当执行时才确定! 一定要记牢这句话!!!

也就是说,当我们此时obj2.fn()时,我们以为它和例1一样,但由于内部fn函数时箭头函数,所以跟我们原先的理解完全不同了。该箭头函数其实在定义好obj2对象时就确定了它的词法作用域

它的词法作用域是什么?
这里,必须牢记,js里面没有块级作用域,只有全局作用域和函数作用域(witheval不提)。那么,当我定义obj2时,箭头函数的词法作用域是谁?
全局作用域!所以,这里打印输出thiswindow

有人会说es6的let声明能形成块级作用域,如果上例用let来声明会不会表现的不同呢?我来试试:

let obj2 = {
  fn: () => { console.log(this) }
}
obj2.fn(); // window 非严格模式下

即使换成let声明,结果依然不变!

例3:

如果把它放进函数里面呢?试试:

function test () {
  var obj2 = {
    fn: () => {console.log(this)}
  }
    return obj2.fn
}
test()(); // window 非严格模式下

这里obj2明明已经放进函数test里面了啊,它的词法作用域应该就是test函数作用域啊,那么为何打印输出依然是window呢?
其实,这里等价于function test() {console.log(this)}!为什么这么说?因为此时我们都知道箭头函数词法作用域是test的函数作用域,而该函数作用域的this是什么?当然是看它被谁调用了啊!这里是window调用的它!

例4(与例3对比):

明白了吗?不明白看个对比可能更好点:

var f = {
  fn : function () {
    var obj = () => {console.log(this)}
    return obj
  }
}
f.fn()(); // {fn: ƒ}

这里的箭头函数的词法作用域fn所指向的函数的作用域,那么只要看谁调用fn谁就是this

例5

箭头函数告一段落,继续回到正常的函数中,看看闭包中的this

var f = {
  fn : function () {
    var obj = function(){console.log(this)}
    return obj
  }
}
f.fn()();

这里为什么是undefined呢?
注意,这里可不是箭头函数了!其实这里等价于var fn = f.fn(); fn();这样一拆分,是不是很好理解了呢?

例6:

var arr = [1,2,3];
arr.map(function(){
  console.log(this)
})
// 打印三次window 非严格模式

why???
这里估计得先了解下map函数的实现才能很好理解,我先前贴出的链接里面有讲解,仔细看的肯定会知道原因。
其实map里面的匿名函数是作为参数传递进去的,然后在map内部直接执行没有其他变量去调用它,所以在非严格模式下自然就是window了。

例7

看一个正常函数的闭包变异案例:

function Thing() {};
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function(){
  var info = 'attempting to log this.foo:'
  function doIt() {
    console.log(info, this.foo)
  }
  console.log(this)
  doIt()
}
thing.logFoo();
// Thing {}
// attempting to log this.foo: undefined

注意,这里不是箭头函数,不用考虑词法作用域!只需要看谁调用它即可。此例中直接在函数内部执行了doIt(),没有谁调用它。所以doIt内部的this在非严格模式下指向window,而window内没有foo属性,所以是undefined

例8

var a = 20;
var obj = {
  a: 10,
  c: this.a + 20,
  fn: function () {
    return this.a;
  }
}

console.log(obj.c);
console.log(obj.fn());
// 40
// 10

我想,上例第二个打印10应该都能理解了,但是可能会在obj.c打印40这里容易懵逼。上例中cobj的属性,但是它并没有引用一个函数,当普通函数被调用时,this指向调用该普通函数的对象,但是如果调用的不是普通函数,那么情况就和箭头函数一样了,此时this应该由词法作用域确定!
牢记上面这段话,回到例子中来,obj.c指向的并不是普通函数,所以在它被写下来时就已经确定了它的词法作用域是全局作用域,所以thiswindowthis.a + 20等价于window.a + 20

例9

擦擦擦,又犯错误了!!!

function fn (){ console.log(this) }
var arr = [fn]
arr[0]()
// [f]

这里想错了,fn是普通函数,应该看它被谁调用!这里其实是arr在调用!!!

注意,不能对 this 直接赋值!例如:this = null,会报左边赋值无效!

总结

this是js的一个难点,有很多人凭工作经验去理解它,一般情况下他可能不会踩什么大坑,但一旦出现出现经验无法解决的坑,就很捉急了,所以建议在有一段开发经验后不要忘了去看一下深入基础的知识,好好巩固,把基础打牢,才能建高楼大厦!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers
Projects
None yet
Development

No branches or pull requests

1 participant