es基础之作用域和闭包

作用域和闭包

执行上下文

变量提升

1
2
3
4
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始终绑定到 实例上,不会被改变

作用域和作用域链

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

闭包

1
2
3
4
5
6
7
8
9
function F1(){
var a =1
return function(){
console.log(a)
}
}
var f1 = F1()
var a = 200
f1()

闭包有两个应用场景:

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

给大家开个眼

1
2
3
4
5
6
7
8
function a(){
let a = 1
window.b = function(){
console.log(a)
}
}
a()
b()

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

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

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

用闭包解决问题

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

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

第一种用闭包来解决

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

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

1
2
3
4
5
6
7
8
9
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}

第三种,就是用let了

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

call apply bind

手写一个

call

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
  • 返回新对象
    1
    2
    3
    4
    5
    6
    7
    function 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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

请我喝杯咖啡吧~