作用域
作用域链
子级可以拿到父级作用域的变量或者方法
现实应用场景:嵌套函数内部读写各外层上下文中的变量
变量提升、函数提升
变量提升优先级高于函数提升
块级作用域 --let/const 值作用于当前作用域
var无块级作用域,污染全局变量
js-编码解析
静态创建
作用域链确认:当前变量和所有父级变量
变量申明
动态执行(区别于其他语言js的this是动态的)
this/context/指针 执行上下文
this指向问题
函数表达式、匿名函数、嵌套函数
this为其执行上下文-window
function a(){
console.log(this) // this指向其执行环境-window
b() // this指向其执行环境-func-window
}
function b(){
c() // this指向其执行环境-func-window
}
function c(){
console.log(this)
}
a()
隐式绑定
function fn(){
console.log('print-this: ', this)
}
const obj = {
a: 1,
fn
}
obj.fn() // 函数作为对象方法,this为该对象实例
let fn1 = obj.fn
fn1() // 取出函数,this为上下文window
class中的this
// 指向new之后生成的实例
class Course{
constructor(name){
this.name = name
console.log('构造函数中的this:', this)
}
test(){
console.log('类方法中的this:', this)
}
asyncTest(){
console.log('异步方法外this:', this) // 类实例的this
setTimeout(function(){
console.log('异步方法外this:', this) // this指向window
}, 2000)
}
}
tips: 类异步方法上下文变为window
解决方法:
记录this并传入;箭头函数;
问题:this指向案例
const obj2 = {
name: 'obj2',
fn: function(){
console.log('obj2 this: ', this, this.name)
return this.name
}
}
const obj3 = {
name: 'obj3',
fn: function(){
return obj2.fn()
}
}
const obj4 = {
name: 'obj3',
fn: function(){
let fn1 = obj2.fn
return fn1()
}
}
console.log('obj2.fn: ', obj2.fn()) // this为o1,name为obj2
console.log('obj3.fn: ', obj3.fn()) // this为o1,name为obj2
console.log('obj4.fn: ', obj4.fn()) // this为window,name为undefined
改变this指向
// 1.直接拿过上下文
const obj3 = {
fn: o1.fn
}
// 2.call、apply、bind
// error:console.log('obj3.fn: ', obj3.fn.call(obj3, null)) -- 操作不到实际调用者
const obj3 = {
name: 'obj3',
fn: function(){
return obj2.fn.call(this)
}
}
call、apply、bind
三者区别:
call、apply 传参不同:依次传入、数组传入
bind:返回方法
手写bind
分析:改变this指向,返回一个方法-未执行
Function.prototype.newBind = function(){
const _this = this
const args = Array.prototype.slice.call(arguments)
const newThis = args.shift()
return function(){
return _this.apply(newThis, args)
}
}
手写apply
分析:改变this指向,返回一个方法-未执行
Function.prototype.newApply = function(context ){
// 边缘检测,参数检测
if(typeof this !== 'function') throw new Error('type new error')
// 参数检测
context = context || window
context.fn = this
let result = arguments[1]? context.fn(…arguments[1]): context.fn()
delete context.fn
return result
}
闭包
与作用域\上下文相关,函数和周围状态-上下文产生关联捆绑
// 最简单闭包
function bibbao(){
let context = 'bibbao'
return function(){
console.log(context)
}
}
// 实现私有变量,利于项目模块封装功能
fucntion bibao(){
const items = []
return {
getItem(){
},push(val){
items.push(val)
},setItem(){
}
}
}
优点:函数外部可以获取到内部作用域中的变量
缺点:导致内部局部变量不能被回收
闭包场景
// 函数作为参数传递 抽离,单一职责原则
let content;
function envelop(fn){
content = 1;
fn();
}
function mail(){
console.log(content)
}
envelop(mail)
// 函数嵌套
包裹函数模块-高内聚
let counter = 0
function outerFn(){
function innerFn(){
counter ++
console.log(counter)
}
return innerFn
}
outerFn()()
// 事件处理(异步)的闭包
// 上下文产生一定的捆绑
let lis = document.getElementById('myli')
for(var i = 0; i < lis.length; i++)
{
(function(i){
lis[i].onclick= function(){
console.log(i)
}
})(i)
}
立即执行函数 --模块化的开端
让内部内容生成块级作用域
(function immediateFn(a){
return (function immediateFn2(b){
console.log(b)
})(200)
})(100)