前言
在上一篇《JavaScript基础篇-闭包》中我们有提到过作用域,但只是通过匆匆描述而已,接下来我们就来仔细谈谈JavaScript中的作用域 —— this
介绍
- 定义:当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。
- 理解:作用域(this)可以拿到当前环境的所有参数和方法
- 调用场景:this 取值是在执行的时候被确认的,并不是在定义的时候
- 常见的几种方式
- 全局对象 window:在定义全局变量的时候,变量的 this 就指向 window
- 隐式传入:在使用对象进行定义方法是,其实已经默认将对象当成作用域传入方法中,方法中的 this 指向对象
- 显示传入:通过使用 call、apply、bind 几种方法进行 this 的一个传递
- new 实例改变:构造函数生成实例,其实是在内存中新开辟了一块内存,并且生成一个 this指向新开辟的这个构造函数
延伸
var、let、const 的区别
在 ES6 之前定义方式只有 var(如果不是var 默认是全局)定义变量,但是在 ES6 中新增了两个定义方式 let、const,它们与 var 有什么区别呢?
- ES5 中作用域有:全局作用域、函数作用域。没有块作用域的概念
- ES6 中新增了块级作用域。块作用域由 { } 包括,if语句和 for语句里面的{ }也属于块作用域
- ES6 中的 let 和 const 区别在于,const 定义后的变量不可更改,let 可以更改 由于 ES5 中没有 块级作用域,所以它们可以跨块进行访问,所以在 {} 使用 var 定义的变量,在 块 外面可以访问,全局作用域下使用 var 定义的变量默认挂载在 window 属性中
常见的问题
由于 var 没有块级作用域,所以他们的当中的 i 都默认挂在在了 window 属性上,并且 setTimeout 是异步事件,它要等到所有同步事件执行完之后才执行,所以在第一种方式中它们调用的 i 是同一作用域下的 i,而使用 let 定义的 i 具有块级作用域,所以他们相互独立,互不影响
for(var i = 0;i < 10;i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 输出 10个10
for(let i = 0;i < 10;i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 输出 0、1、2、3、4、5、6、7、8、9
代码示例
全局对象
// 在全局变量中,变量会自动挂载到 window 属性上
var name = 'zhangsan'
console.log(name === window.name) // ture
console.log(this.name === window.name) // ture
隐式传入
// this 是在执行的定义的,当执行函数时,函数内部的 this 是取决与函数前面中传入的作用域
function fn() {
console.log(this.name)
}
fn() // undefined,直接调用方法相当于 window.fn(),所以它的 this 指向 window
var obj = {
name: 'zhangsan',
fn: fn
}
obj.fn() // 'zhangsan',直接调用方法相当于 window.obj.fn(),所以它的 this 指向 obj
call、apply、bind 传入
// 将外部的 this 传入到函数中
function fn1() {
console.log(this.name)
}
function fn2() {
this.name = 'lisi'
// call 传入
fn1.call(this) // lisi
// apply 传入
fn1.apply(this) // lisi
// bind 传入
var f = fn1.bind(this)
f() // lisi
}
fn2()
new 实例改变
function People(name) {
this.name = name
this.age = 20
}
var p1 = new People('zhangsan')
var p2 = new People('lisi')
console.log(p1.name) // zhangsan
console.log(p2.name) // lisi