作用域和闭包

136 阅读3分钟

题目:

  • this的不同应用场景,如何取值?
// 创建10个<a>标签,点击的时候弹出来对应的序号
let i, a
for (i = 0;i < 10; i++) {
	a = document.createElement('a')
  a.innerHTML = i + '<br>'
  a.addEventListener('click',function (e) {
  	e.preventDefault()
    alert(i)
  })
	document.body.appendChild(a)
}
  • 作用域和自由变量
  • 闭包
  • this

作用域

代表了某个变量的合法范围

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)
// ES6 块级作用域
if (true) {
  let x = 100
}
console.log(x) //会报错

自由变量

  • 一个变量在当前作用域没有定义,但是被使用了
  • 向上级作用域,一层一层依次寻找,直至找到为止
  • 如果在全局作用域都没有找到,则报错xx is not defined

闭包

  • 作用域应用的特殊情况,由两种表现:
  • 函数作为参数被传递
  • 函数作为返回值被返回
// 函数作为返回值
function create() {
  let a = 100
  return function () {
    console.log(a)  // 在这里定义,所以向上级进行查找
  }
}

const fn = create()
const a = 200
fn(); //100
// 函数作为参数
function print(fn) {
  const a = 200
  fn()
}
const a = 100
function fn() {
  console.log(a)  // 在这里定义,所以向上级进行查找
}
print(fn) //100

闭包

自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方~~

this

  • 作为普通函数 返回window
  • 使用 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}
const zhangsan = {
  name: '张三',
  sayHi() {
    //this 即当前对象
    console.log(this)
  },
  wait() {
    setTimeout(function(){
      //this === window
      console.log(this)
    })
  }
}
console.log(zhangsan)
const zhangsan = {
  name: '张三',
  sayHi() {
    //this 即当前对象
    console.log('this 即当前对象')
    console.log(this)
  },
  wait() {
    // 箭头函数 上级作用域
    setTimeout(()=>{
      //this 即当前对象
      console.log(this)
    })
  }
}
console.log(zhangsan)
class People {
  constructor(name) {
    this.name = name
    this.age = 20
  }
  sayHi() {
    console.log(this)
  } 
}
const zhangsan = new People("张三")
zhangsan.sayHi() //zhangsan 对象

手写bind函数

Skip welcome & menu and move to editor
 
Textarea editor mode
JS Bin features
Getting started
Keyboard Shortcuts
Exporting/importing gist
 
Pro features
Private bins
Vanity URLs
Upgrade to pro now
 
Blog
The Return and The Refactor
Help
Delete a bin
Debbugging your preferences
 
Donate to JS BinSupport JS Bin to keep the project open source & MIT for all
Follow @js_bin on twitter
By using JS Bin you agree to our legal terms
​
​
// 模拟bind
Function.prototype.bind1 = function () {
//   将参数拆解为数组
   const args = Array.prototype.slice.call(arguments)
   
//    获取this (数组第一项)
   const t = args.shift()
   
   // fn1.bind(...)中的fn1
   const self = this
   
   // 返回一个函数
   return function () {
     return self.apply(t,args)
   }
}
function fn1(a,b,c){
  console.log('this',this)
  console.log(a,b,c)
  return 'this is fn1'
}
​
const fn2 = fn1.bind1({x:100},10,20,30)
const res = fn2()
console.log(res)
//"this"
//[object Object] {
//  x: 100
//}
//10
//20
//30
//"this is fn1"

实际开发中闭包的作用

  • 隐藏数据
  • 做一个简单的cache工具
// 闭包隐藏数据,只提供 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',1000)
console.log(c.get('a'))
// 创建10个<a>标签,点击的时候弹出来对应的序号
let  a
// 每一次都是不同的块作用域
for (let i = 0;i < 10; i++) {
      a = document.createElement('a')
      a.innerHTML = i + '<br>'
      a.addEventListener('click',function (e) {
        e.preventDefault()
        alert(i)
      })
      document.body.appendChild(a)
}

小结

  • 作用域和自由变量
  • 闭包:两种常见形式&自由变量查找规则
  • this