es基础之作用域和闭包

作用域和闭包 #

执行上下文 #

变量提升

console.log(a) // undefined var a 提升
var a =1
fn(1) //1 function提升
function fn(v){}

js在执行之前,会先预编译,把即将执行的变量和函数声明都拿出来:变量先给undefined,函数先声明等待使用。预编译完了才开始执行。

this #

定义时候无法确认this,只有执行时候才能确认。

我们可以通过 call apply bind 来改变this的指向

关于this:

  • 直接调用foo,不管函数放到什么地方,this是Window
  • 对于,被调用的方式,谁调用指向谁
  • 通过 new 的方式,this始终绑定到 实例上,不会被改变

作用域和作用域链 #

内部能访问外部,外部不能访问内部,这就是作用域。 内部使用了一个属性,内部找不到就一层层向上查找,这就是作用域链。

闭包 #

function F1(){
    var a =1
    return function(){
        console.log(a)
    }
}
var f1 = F1()
var a = 200
f1()

闭包有两个应用场景:

  • 函数作为返回值
  • 函数作为参数传递

给大家开个眼

function a(){
    let a = 1
    window.b = function(){
        console.log(a)
    }
}
a()
b()

什么是闭包,函数a内部有函数b,b可以访问到a的变量,那么函数b就是闭包。

注意,很多人误认为闭包就是,函数嵌套函数,然后返回函数,这是有误解。

闭包的作用,让我们间接访问函数内部的变量。

用闭包解决问题

for(var i =0;i<=5;i++){
    setTimeout(function timer(){console.log(i)},i*1000)
}

运行会返回一堆6,很明显异步函数执行的太晚了。

第一种用闭包来解决

for (var i = 1; i <= 5; i++) {
  ;(function(j) {
    setTimeout(function timer() {
      console.log(j)
    }, j * 1000)
  })(i)
}

第二种。setTimeout 传第三个参数。

for (var i = 1; i <= 5; i++) {
  setTimeout(
    function timer(j) {
      console.log(j)
    },
    i * 1000,
    i
  )
}

第三种,就是用let了

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

call apply bind #

手写一个

call #

Function.prototype.myCall = function(ctx) {
    if (typeof this !== "function") {
        throw new TypeError("error");
    }
    ctx = ctx || window;// 不传参数就是 window
    ctx.fn = this; // 创建fn
    const args = [...arguments].slice(1);// 多参数
    const result = ctx.fn(...args);
    delete ctx.fn;
    return result;
};

apply #

Function.prototype.myApply = function(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  context = context || window
  context.fn = this
  let result
  // 处理参数和 call 有区别
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

bind #

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

new #

自己写一个new?

  • 新生成对象
  • 链接到原型
  • 绑定this
  • 返回新对象
functon create(){
    let obj={} // 1 新对象
    let Con=[].shift.call(arguments) // 获取传入参数,构造函数
    obj.__proto__=Con.prototype // 绑定空对象原型
    let result = Con.apply(obj,arguments) //绑定this并执行构造函数
    return result instanceof Object?result:obj
}

instanceof #

自己实现一个 instanceof

function myInstanceof(left,right){
    let prototype=right.prototype
    left = left.__proto__
    while(true){
        if(left===null || left===undefined)
            return false
        if(prototype===left) return true
        left=left.__proto__
    }
}

获取右边的原型。获取左边的原型。循环判断原型,直到null