为了账号安全,请及时绑定邮箱和手机立即绑定

高效准备前端技术一面:JS 基础知识面试题(三)

标签:
JavaScript

什么是作用域?什么是自由变量?

全局作用域

声明在任何函数之外的顶层作用域的变量就是全局变量,这样的变量拥有全局作用域

所有未定义直接赋值的变量拥有全局作用域

所有 window 对象的属性拥有全局作用域

全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突

函数作用域

在函数内部定义的变量称为局部变量,拥有函数作用域

只有函数被调用的时候才会形成函数作用域

内层作用域可以访问外层作用域,反之不行

块级作用域

使用 ES6 中新增的 let 和 const 指令可以声明块级作用域

块作用域内的变量只要出了自己被定义的那个代码块,那么就无法访问了。

在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制在循环内部

自由变量

在函数中被使用,但它既不是函数参数、也不是函数的局部变量,而是一个不属于当前作用域的变量,此时它相对于当前作用域来说,就是一个自由变量

如何理解闭包

自由变量的查找规则:自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方=>js是词法作用域

::: tip 词法作用域和动态作用域

  • 词法(静态)作用域: 在代码书写的时候完成划分,作用域链沿着它定义的位置往外延伸
  • 动态作用域: 在代码运行时完成划分,作用域链沿着它的调用栈往外延伸
var name = 'fan';

function showName() {
    console.log(name);
}

function changeName() {
    var name = 'hang';
    showName();
}

changeName(); // 词法: fan 动态:hang

:::

在函数中被使用,但它既不是函数参数、也不是函数的局部变量,而是一个不属于当前作用域的变量,此时它相对于当前作用域来说,就是一个自由变量。而引用了自由变量的函数,就叫闭包

LHS、RHS是什么?

LHS、RHS,是引擎在执行代码的时候,查询变量的两种方式。其中的 L、R,分别意味着 Left、Right。这个“左”和“右”,是相对于赋值操作来说的。当变量出现在赋值操作的左侧时,执行的就是 LHS 操作,右侧则执行 RHS 操作:

name = 'xiuyan';

在这个例子里,name 变量出现在赋值操作的左侧,它就属于 LHS。LHS 意味着 变量赋值或写入内存,

它强调的是一个写入的动作,所以 LHS 查询查的是这个变量的“家”(对应的内存空间)在哪。

var myName = name
console.log(name)

在这个例子里,第一行有赋值操作,但是 name 在操作的右侧,所以是 RHS;第二行没有赋值操作,name 就可以理解为没有出现在赋值操作的左侧,这种情况下我们也认为 name 的查询是 RHS。RHS 意味着 变量查找或从内存中读取,它强调的是读这个动作,查询的是变量的内容。

闭包代码输出问题

常见的循环体输出问题

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

console.log(i); //5 5 5 5 5 

创建 10 个 a,点击弹出对应的序号

<div id="root"></div>
      const root = document.getElementById('root')
      for (let i = 1; i <= 10; i++) {
        const a = document.createElement('a')
        a.innerText = i
        a.style.display = 'block'
        a.onclick = function (e) {
				e.preventDefault()
        alert(i)
      }
        root.appendChild(a)
      }

手写bind函数

::: tip 提示
bind() 方法创建一个新的函数(这个包装了目标函数),在 bind()被调用时,这个新函数的 this 被指定为 bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
:::

  • bind 的用法
function fn1(a, b, c) {
  console.log('this', this)
  console.log(a, b, c)
  return 'this is fn1'
}
const fn2 = fn1.bind({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res)
  • 手写 bind
  // 剩余参数是ES6的新特性,一般说来,不支持bind的情况一般也不支持剩余参数,所以,不推荐这种写法  
Function.prototype.bind1 = function () {
  // 将参数拆解为数组
  // call支持最低版本chorme-1 
  const args = Array.prototype.slice.call(arguments)
  //   获取this,数组第一项
  const _this = args.shift()
  const self = this
  // 返回一个函数
  return function () {
    // apply支持最低版本chorme-1 
    // bind支持最低版本chorme-7
    self.apply(_this, args)
  }
}
// ----------------------------------------
const obj = {
  a: 2,
  b() {
    console.log(this.a)
  },
}
const _obj = {
  a: 3,
  b() {
    console.log(this.a)
  },
}
obj.b.bind1(_obj)()

this指向问题

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

  • 作为普通函数使用,指向window

call apply bind 三者的用法和区别open in new window

call()apply()bind() 都是用来重定义 this 这个对象的

bind 返回的是一个新的函数,你必须调用它才会被执行 ,其余两个都是立即执行的

bindcall 参数形式一致,apply 需要把参数写到数组里

  • call apply bind
  • 作为对象方法被调用,指向当前对象
  • class 方法中调用,指向当前对象
  • 箭头函数的this永远取它上层作用域的this
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消