作用域与闭包问题

作用域

作用域

全局作用域

window对象,document对象

函数作用域

在函数中定义的变量

块级作用域

if (1) {
	let x = 100
}
console.log(x) // 报错

创建10个标签,点击时弹出对应序号

// 错误写法:i是全局作用域,点击事件发生时i已经是10了
let i,a;
for(i=0;i<10;i++){
  a = document.createElement('a')
  a.innerHTML = i + '<br>'
	// 点击时才会执行,此时的i已经为10
  a.addEventListener('click', function(e){
    e.preventDefault();
    alert(i)
  })
  document.body.appendChild(a)
}
// 正确写法
let a;
for(let i=0;i<10;i++){
  a = document.createElement('a')
  a.innerHTML = i + '<br>'
	// 点击时才会执行,此时的i已经为10
  a.addEventListener('click', function(e){
    e.preventDefault();
    alert(i)
  })
  document.body.appendChild(a)
}

闭包

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

函数作为返回值

function create() {
  let a = 100;
  return function() {
    console.log(a)
  }
}
let fn = create()
let a = 200
fn() // 100

函数作为参数

function print(fn) {
  let a = 200
  fn()
}
let a = 100
function fn() {
  console.log(a)
}
print(fn) // 100

闭包隐藏数据,只提供API

const createCache = () => {
  const data = {};
  return {
    set : (key,val) => {
      data[key] =  val
    },
    get : (key) => {
      return data[key]
    }
  }
}

const c = createCache()
c.set('a',100)
console.log(c.get('a')) // 100
console.log(c.data) // undefined

this

  1. 当做普通函数被调用
  2. 使用call apply bind
  3. 作为对象方法调用
  4. 在class的方法中调用
  5. 箭头函数

this取什么样的值是在函数执行时决定而不是函数定义时。

function fn() {
  console.log(this)
}
fn() // window
fn.call({x:100}) // {x:100}
const fn2 = fn.bind({x:200}) 
fn2() // {x:200}

在宏任务时this指向window

const Zhangshan = {
  name: "Zhangshan",
  sayhi() {
    console.log(this)
  },
  waitSayhi() {
    setTimeout(function() {
      console.log(this)      
    } )
  }
}
Zhangshan.sayhi() // object Zhangshan
Zhangshan.waitSayhi() // window

箭头函数永远指向上一级

const Zhangshan = {
  name: "Zhangshan",
  sayhi() {
    console.log(this)
  },
  waitSayhi() {
    setTimeout(() => {
      console.log(this)      
    } )
  }
}
Zhangshan.sayhi() // object Zhangshan
Zhangshan.waitSayhi() // object Zhangshan

手写bind

**bind()**方法创建一个新的函数,在 bind()被调用时,这个新函数的 this被指定为 bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

Function.prototype.bind() - JavaScript | MDN

// 模拟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 { x: 100 }
10 20 30
this is fn1