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

一次搞定 JavaScript 中的 this #16

Open
venaissance opened this issue Aug 11, 2020 · 0 comments
Open

一次搞定 JavaScript 中的 this #16

venaissance opened this issue Aug 11, 2020 · 0 comments

Comments

@venaissance
Copy link
Owner

venaissance commented Aug 11, 2020

小白前端学习 JavaScript 过程中遇到的第一道坎,大概率就是 this 了。曾经我也被 this 搞的非常头大,还看过不少博客,但我觉得大多数文章写的有些复杂或者又引入了一些新的概念,加大了理解难度。本文就站在各位前辈大佬的肩膀上,做一下总结升华,目的就是让大家一次搞定 JavaScript 中的 this。

本文会引入的概念

  1. 函数调用,知道 fnfn() 不一样就行,fn() 是函数调用
  2. 隐式调用,不含有 callapplybind 的函数调用就是隐式调用,反之就是显示调用
  3. 箭头函数,知道长的像 fn = () => {} 这样的函数叫箭头函数就行
  4. call 的用法,知道 call() 的第一个参数可以指定 fn 的 this 就行,bindapply 同理
  5. 形参、实参,知道函数定义时的参数叫形参,调用时传入的参数叫实参就行

三步确定 this

1. 一“定”——函数调用this定

函数调用的时候确定 this 指向,可以把 this 看成一个隐藏参数。

var a = 10;
function fn() {
	var a = 1;
  	console.log(this.a);
}

fn() // 调用的时候才确定this,默认是undefined,浏览器把它改成了window,var声明的变量会自动挂到window下,所以输出10

2. 二“转”——箭头转外,隐式转call

箭头函数调用

看这个函数的类型,如果是箭头函数,看它外面的 this。

var fn = () => {
  console.log(this.id);
}
var id = 21;
foo(); // 21,因为外面的this是undefined,非严格模式下浏览器会自动转成window,var声明的变量被自动挂到window,所以输出21

普通函数隐式调用

  1. fn(1, 2) 转化成 fn.call(undefined, 1, 2)

  2. obj.fn('hello') 转化成 obj.fn.call(obj, 'hello')

  3. arr[0]() 转化成 arr.0.call(arr, 'x')

来一道综合题一次性说完:

var id = 10
var obj = {
  id: 1,
  foo: function(){
    console.log(this.id)
  }
}
function fn (){ console.log(this) }
var arr = [fn, fn2]

var bar = obj.foo
obj.foo() // 1
bar() // 10
arr[0]() // arr

3. 三“特例”——三种特例别踩坑

new 会改变 this

function Person (name) {
  this.name = name
}
var name = 'window'
var person1 = new Person('Varian')
console.log(person1.name) // Varian

严格模式保持 undefined

严格模式下值为 undefined 的 this 不会变成 window

"use strict"; // 开启严格模式
var a = 10;
function foo () {
  console.log('this1', this) // 'this1' undefined
  console.log(window.a) // 10
  console.log(this.a) // Uncaught TypeError: Cannot read property 'a' of undefined
}
console.log(window.foo) // f foo() {...}
console.log('this2', this) // 'this2' Window{...}
foo();

let const 不会被挂到 window

let x = 10
const y = 20

function foo () {
  console.log(this.x) // undefined
  console.log(this.y) // undefined
}

foo();
console.log(window.x) // undefined

看完有没有感觉 this 其实也并不难呢?来几道综合题巩固一下吧!

综合题

  1. 结合 DOM

    button.onClick = function(e) {
      console.log(this);
    }

    答案不是 button,而是不确定,因为都没有调用,怎么知道 this 呢?

    可以这么回答:因为没有调用,所以无法确定 this,如果采用隐式调用的话,打印 button,如果使用显示调用,则打印自定义的 this。

  2. 超难面试题

    let length = 10;
    function fn() {
      console.log(this.length);
    }
    
    let obj = {
      length: 5,
      method(fn){
      	fn()
        arguments[0]()
    	}
    }
    
    obj.method(fn, 1);

    这道题有几大坑:let、window.length、arguments.length

    所以答案也非常诡异

    不确定(window.length = 页面的iframe数量)
    2(arguments.length = 实参长度,非形参)

    关于 window.length 的值,可以参见 MDN

总结

记住一“定”,二“转”,三特例就行,核心是两点:

  1. 调用时确定 this
  2. 隐式调用上公式 obj.fn(1) = obj.fn.call(obj, 1)

这样一来 this 就真相大白了!


参考文章

  1. 《this 的值到底是什么?一次说清楚》
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant