一、作用域
1.1 块级作用域
// ES6 块级作用域
if (true) {
let x = 10
}
console.log(x) // 报错
1.2 自由变量
- 一个变量在当前作用域没有定义,但被使用了;
- 向上级作用域,一层一层依次寻找,直到找到为止;
- 如果到全局作用域都没找到,报错 xx is not undefined;
二、闭包
作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
// 函数作为返回值
function create() {
const a = 100
return function() {
console.log('a: ', a)
}
}
let fn = create()
const a = 200
fn() // 100
// 函数作为参数
function print(fn) {
let a = 200
fn()
}
let a = 100
function fn() {
console.log(a)
}
print(fn) // 100
// 闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找
// 不是在执行的地方!!!
// 其实也不仅仅是闭包,是所有的自由变量的查找。。。。。。
三、this
this有这么几种使用场景,不是多复杂,而是使用场景较多。
- 作为普通函数
- 使用 call apply bind
- 作为对象方法去调用
- 在class方法中调用
- 箭头函数
this 取什么值,是在函数执行的时候确定,而不是定义的时候。
function fn1() {
console.log(this)
}
fn1() // window
fn1.call({x: 100}) // {x: 100}
const fn2 = fn1.bind({x: 200})
fn2() // {x: 200}
// bind和call有点不一样,bind返回的是一个函数
const zhangsan = {
name: '张三',
sayHi() {
console.log(this) // this即当前对象
},
wait() {
setTimeout(function() {
// setTimeout触发的函数执行,作为普通函数执行,window
console.log(this) // this 是 window
}, 10)
}
}
zhangsan.sayHi()
zhangsan.wait()
上图中的箭头函数:箭头函数的this,永远取的是它上级作用域的this。
三、bind
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值,例如,f.bind(obj),实际上可以理解为obj.f(),这时,f函数体内的this自然指向的是obj
手动实现bind
可以看到bind属于原型链中的
四、call
使用call方法,可以编写能够在不同对象上使用的方法。
call方法可以用来调用所有者对象作为参数的方法。
通过call,能够使用属于另外一个对象的方法。
比如下面,调用person对象的fullName方法,用于person1对象。
const person = {
fullName: function() {
return this.firstName + ' ' + this.lastName
}
}
const person1 = {
firstName: 'Bill',
lastName: 'Gates'
}
const person2 = {
firstName: 'Steve',
lastName: 'Jobs'
}
const res = person.fullName.call(person1)
console.log(res) // Bill Gates
五、实际开发中的闭包
数据隐藏,只提供API,数据内部自己管理
// 闭包隐藏数据,只提供API
function createCache() {
const data = {} // 闭包中的数据,被隐藏,不被外界访问
return {
set: function(key, val) {
data[key] = val
},
get: function(key) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a')) // 100
六、原型中的this
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat sth`)
}
}
class Student extends People {
constructor(name, number) {
// super:把名字给父类People,父类会帮我们处理,调用父类构造函数,但是学号number是学生特有的,自己处理
super(name)
this.number = number
}
sayHi() {
console.log(`姓名: ${this.name} 学号: ${this.number}`)
}
}
const xialuo = new Student('夏洛', 10010)
console.log(xialuo) // Student { name: '夏洛', number: 10010 }
console.log('........')
console.log(xialuo.sayHi()) // 姓名: 夏洛 学号: 10010
console.log(xialuo.__proto__.sayHi) // 会正确打印sayHi函数体,没问题
// [下行运行结果] 姓名: undefined 学号: undefined【this指向的问题,上面的this是xialuo,此行的this是xialuo.__proto__】
console.log(xialuo.__proto__.sayHi())
console.log(xialuo.__proto__.name) // undefined
console.log(xialuo.__proto__.number) // undefined
console.log(xialuo.__proto__.sayHi.call(xialuo)) // Student { name: '夏洛', number: 10010 }