javascript复习笔记(3)

作者: Juliiii | 来源:发表于2017-10-01 22:46 被阅读0次

this

上次讲了闭包,其实我感觉我上次没讲什么,闭包的确神秘,但是又不是很复杂,需要长篇大论去解释。刚好,我看到了js中的另一个重点,this。this也是会让初学者头疼的一个知识点。有时候感觉莫名其妙,直觉上应该是这个答案,实际上却是错的。

错误的认识

  • this不只在js中出现,java, c++ 这些强类型语言也是有this的,不过它们都是叫this, 本质是不太一样的。学过这些强类型语言的同学很容易就认为this是指向自己的。然而,这在js中,是不正确的。下面的代码会打脸。
function fun (val) {
  this.a = val;
}
fun(0);
fun.a++;
console.log(fun.a);
console.log(a);

如果this是指向自己的话,那么现在fun.a应该是1咯,对吧。然而真的是这样么? 打开chrome, F12将代码拷贝在控制台看看结果,fun.a的输出是NaN,第二个log出却是0。傻眼了吧?好吧,其实当时我也是一脸懵逼。这个结果,告诉了我们js的this不如你想象的那样。这是你不知道的javaScript的this。

  • 除了认为this指向自己,《你不知道的javaScript》中也提到另一种错误的理解。this指向函数的作用域。详细点说,就是一个函数,它的this是指向它的父级作用域。嗯,好吧,我觉得这种理解很自然,很舒服。下面的代码,就是这种错误理解导致的错误写法。
function a () {
  console.log(this.c);
}
function b() {
  let c = 1;
  this.a();
}
b(); // undefined

其实上面的结果不会输出1,所以说明了这个理解也是错误的。

this的正确使用姿势

this的指向呢,其实一句话就概况了。

this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

其实this的指向就看谁调用了它吧,总结下来,有以下4种情况:

  1. 默认绑定
    默认绑定,我曾经也听到说成是直接函数调用。下面的代码就是默认绑定
function fun () {
  console.log(this.a);
}
var a = 1;
fun() // 1

默认绑定的情况,函数中的this是绑定在window上,当然是在非严格模式下。在严格模式下,this是指向undefined, 上面的代码就会抛出一个错误。说回非严格模式下的默认绑定,由于this指向了window。所以, this.a其实就是window.a。那么自然输出了1。

  1. 隐式绑定
    当一个函数是某个对象的属性时,我们直接对象.函数名调用该函数时,该函数的this是指向该对象的。其实,我个人认为,默认绑定的情况下 fun() 等价于 window.fun(),这也正好解释了为什么默认绑定时,this是指向了全局对象。下面是隐式绑定的一个例子:
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

obj.func() // '123'

注意:隐式绑定当使用不当时,会出现this的丢失现象。比如:

// 情况一
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}
func = obj.func;
func(); // '1234'

// 情况二
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }
  }
}
func = obj.func();
func(); // '1234'

// 情况三
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

setTimeout(obj.func, 1000); // '1234'

情况一:不是隐式绑定了obj么,输出应该是123才对啊。结果却输出了1234。嗯,我们要根据代码来分析,this的指向要对比四种情况,然后去找到对应的情况来确定。func()很明显就是之前我们说的默认绑定,所以输出了全局对象中的a属性的值。
情况二:情况二其实就是返回了一个函数,然后这个函数在全局作用域直接调用了,所以就是默认绑定,如果需要绑定原来的obj,可以用以下的办法:

// 解法1
window.name = '1234';
let obj = {
  name: '123',
  func () {
    let that = this;
    return function () {
      console.log(that.name); 
    }
  }
}
func = obj.func();
func(); // '123'

// 解法2
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return () => console.log(this.name); 
   }
 }
func = obj.func();
func(); // '123'

// 解法3
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }.bind(this);
   }
 }
func = obj.func();
func(); // '123'

情况三: setTimeout的第一个参数是一个函数,其实setTimeout的内部实现的伪代码应该是这样的

function setTimeout (fn, delay) {
  // 等了 delay 毫秒
  fn()
}

所以其实也是变成了默认绑定嘛。

  1. 显式绑定
    显式绑定就是用js的call, apply, bind的这些函数来绑定this啦。
window.name = 'window';
let xiaoming = { name: 'xiaoming' };
function bar() { console.log(this.name); }
// call
bar.call(xiaoming); // 'xiaoming';
// apply
bar.apply(xiaoming); // 'xiaoming';
// bind
bar.bind(xiaoming)(); // 'xiaoming';

它们间的异同:

    • 它们三者可以用来改变函数this的指向, 第一个参数都是要指向的对象
    • apply 和 call 接受的参数不太一样,call只能一个一个传入参数,apply可以传入一个参数的数组
    • bind返回的是绑定后的函数,apply 和 call是绑定时同时执行。
  1. new 绑定
    在js中,我们使用new运算符时,其实,经过了下面的操作

1、创建(或者说构造)一个全新的对象
2、这个新对象会被执行[[原型]]连接
3、这个新对象会被绑定到函数的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

综上,那么下面的代码,

function fn (a) { this.a = a; }
let a = new fn(1);
console.log(a.a); // 1

就是上面的四个步骤过一遍的结果了,this绑定到了的这个新对象a上。

总结

逼逼了这么多。。其实this的指向只要弄清楚这四种情况,绝大部分的情况都可以自己判断出来。其实还是会有些很奇葩的情况的,如果我没记错,但是根据我目前的开发经验,只要你或者你同事不作死,就不会写出那么奇葩的情况。如果说是面试题,当我没说。来来来,最后总结遍,四个方法就是:

  • 如果是new出来的对象,那么this指向该对象无误。
  • 如果有用显式绑定,那么this指向绑定的对象
  • 是否作为一个对象的属性来调用,是的话,this指向该对象。当然要注意this丢失的情况。
  • 如果上面都没有,那么默认绑定应该无误。

多练习慢慢就会一眼就看出this的指向了,工多手熟。如果有误,请指出。还有祝大家国庆快乐!

相关文章

  • javascript复习笔记(3)

    this 上次讲了闭包,其实我感觉我上次没讲什么,闭包的确神秘,但是又不是很复杂,需要长篇大论去解释。刚好,我看到...

  • JavaScript

    仅为个人学习笔记,复习用 一.语法 1.插入javascript 2. 引用JS外部文件 JavaScript代码...

  • javascript复习笔记(1)

    目的最近完成了一个项目和即将要进入一个新的项目,在这段时间内相对来说,可以静下来看看书,索性重头过一遍js。故记录...

  • javascript复习笔记(2)

    闭包 网上讲闭包的文章一搜一大把。而且面试时,比较传统的问题都有很大几率提到闭包的问题。的确,闭包是js中一个很重...

  • Javascript 复习笔记(一)

    内置数据类型 1. Number 2. String 模版字符串 操作字符串 3. Boolean true 或者...

  • Javascript 复习笔记(二)

    函数 函数的定义 第一种: 第二种: 函数的调用� 特殊的参数 arguments 和 rest argument...

  • JavaScript复习笔记(一)

    [TOC] 题外话: 其实我觉得能后通过自己感兴趣的事情给自己获取资源是一件很幸福的事情,这也是自己未来努力的方向...

  • 线性代数

    考研复习笔记-线性代数 作者创建时间复习1复习2复习3复习4林加贤2015-08-31 复习时修改笔记,并添加相应...

  • NJUPT《软件工程(双语)》

    1/3 考前复习 复习课笔记https://wwb.lanzouh.com/iMVah06936ej 2/3 课堂...

  • 一建第241天

    一、学习 笔记3复习[绝热工程章节]+(1-42题) 笔记3复习[锅炉砌筑工程章节]+(1-52题) (1-24页...

网友评论

    本文标题:javascript复习笔记(3)

    本文链接:https://www.haomeiwen.com/subject/rdcaextx.html