JS 作用域,闭包,this

作用域,闭包,this

3.1 面试题

1.this 的不同应用场景,如何取值
2.手写 bind 函数
3.实际开发中闭包的应用场景,举例说明
4.程序题

// 打印输出
for (var i = 0; i < 10; i += 1) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

 

3.2 知识点

1.作用域和自由变量
2.闭包
3.this
 

3.3 作用域和自由变量

1.作用域

全局作用域
函数作用域
块级作用域(ES6 新增,即用大括号包含的范围)

2.自由变量

在当前作用域要用到的变量 x,并没有在当前中声明,要到上层作用域中找到他,这个变量 x 就是自由变量

const a = 'a';
{
	// 在这个块级作用域未找到a,则向外层找直到找到全局作用域结束
  console.log(a);
}

 

3.4 闭包

什么是闭包?概念太难去描述,现在排除掉闭包的定义,仅仅来看看闭包的实现以及其作用

1.实现闭包
 
1.将函数作为返回值

function create() {
  const ele = 'element';
  return function () {
    console.log(ele);
  }
}

const ele =  'new element'
const fn = create();
fn(); // element

2.将函数作为参数传入

function print(fn) {
  const ele = 'element';
  fn(ele);
}

const ele =  'new element';
function paramFn(param) {
  console.log(param);
}

print(paramFn); // element

 
以上两种方式,
1.都可以读取函数 print 内部的变量 ele;
2.并且让这些变量的值始终保持在内存中,不会在 print 调用后被自动清除。
 

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

function print(fn) {
  const ele = 'element';
  fn();
}

const ele =  'new element';
function paramFn() {
  console.log(ele);
}

print(paramFn); // new element

 

3.5 this

this 取什么值,是在函数执行的时候确定的,不是在定义的时候确定!!!
 

1.当作普通函数被调用,this 指向 window

function fn() {
  console.log(this);
}
fn(); // window

 
2.使用call, apply, bind,可以修改 this 指向

function fn() {
  console.log(this)
};
fn.call({x: 'x', y: 'y', z: 'z'}); 		// {x: "x", y: "y", z: "z"}
fn.bind({x: 'x', y: 'y', z: 'z'})(); 	// {x: "x", y: "y", z: "z"}
fn.apply({x: 'x', y: 'y', z: 'z'}); 	// {x: "x", y: "y", z: "z"}

 
3.作为对象方法调用
 
3.1 在对象方法中使用setTimeout,这里面的this就和普通函数被调用一样(见第一点);而在 eat 函数本身里面 this 则指向对象本身

const zhangsan = {
  name: '张三',
  age: 16,
  eat() {
    console.log(this);	// 对象本身
    setTimeout(function() {
      console.log(this);  // window
    }, 0);
  },
}
zhangsan.eat();

3.2 在对象方法中使用setTimeout,但传入的是箭头函数,这是由于箭头函数内的 this 永远指向其外层第一个普通函数的 this

const zhangsan = {
  name: '张三',
  age: 16,
  eat() {
    console.log(this);  // 对象本身
    setTimeout(() => {
      console.log(this);  // 对象本身
    }, 0);
  },
}
zhangsan.eat();

 
4.在class的方法中使用

class People {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(this.name, this)
  }
}
const zhangsan = new People('张三');
zhangsan.say();	// 张三 People {name: "张三"}

this 指向实例后的对象本身
 

5.箭头函数
 
箭头函数内的 this 永远指向其外层第一个普通函数的 this (参见第三点)
 

补充:在原型上使用this

class People {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(this.name, this)
  }
}
const zhangsan = new People('张三');
zhangsan.say(); // 张三 People {name: "张三"}
zhangsan.__proto__.say(); // undefined {constructor: ƒ, say: ƒ}

可以看到,通过 __ proto __ 上,虽然通过原型链可以找到 say 这个函数,但是 由于 this 指向的实例后的实例对象本身,因此通过 __ proto __ 使用 say 函数,this上就没有了 name 这个属性,因此打印出来 undefined

3.6 题目解答

 
2.手写 bind 函数

Function.prototype.bindSelf = function () {
  // 将参数解析为数组
  const args = Array.prototype.slice.call(arguments);
  // 提取首元素,按规定即为this所指向的对象
  const newThis = args.shift();
  // self,这个this即为当前对象
  const self = this;
  // bind 返沪一个函数,apply与call用法一致,区别在于参数的传递方式
  return function () {
    return self.apply(newThis, args);
  }
}

function fn(a, b) {
  console.log(this, a, b)
};

fn.bind({x: 'x', y: 'y', z: 'z'}, 'a', 'b')();
fn.bindSelf({x: 'x', y: 'y', z: 'z'}, 'a', 'b')();

 
3.闭包的应用
 
1.隐藏数据
2.如做一个简单的 cache 工具

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

const c = createCache();
c.set('a', 1);
c.set('b', 2);
console.log(c.get('a'), c.get('b'));

 
4.程序题

for (var i = 0; i < 10; i += 1) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

一秒钟之后会打印十个10。
这是因为,i 是一个全局变量,js执行速度非常快,1秒之内就会执行完 for 循环,这个时候 i 已经增加到10,再打印的时候就会打印出十个 10,

如何解决这个问题,给出两个方法:

1.使用闭包

for (var i = 0; i < 10; i += 1) {
  (function(param) {
    setTimeout(() => {
      console.log(param);
    }, 1000);
  })(i);
}

2.使用 let

for (let i = 0; i < 10; i += 1) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

欢迎大家点赞,收藏,关注!!!

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值