JavaScript 知识点集锦二

6 篇文章 0 订阅
5 篇文章 0 订阅


浮点数精度

浮点数精度导致的问题:

console.log(0.1 + 0.2 === 0.3) // false

ECMAScript 中的 Number 类型使用 IEEE754 双精度来表示整数和浮点数值

  • 不是ECMAScript才存在的问题
  • 0.1转二进制时是一个无限循环的数,但是ECMAScript会保存64位字节,因此在这个地方就发生了精度丢失

简单的解决方法:
将小数转为整数再进行判断

console.log(0.1 * 10 + 0.2 * 10 === 0.3 * 10) // true

作用域

概念

作用域:当前的执行上下文。值和表达式在其中 “可见” 或可被访问到的上下文
作用域链: 当访问一个变量时,在当前作用域访问不到时,就在父级的作用域进行查找,直到找到该变量,或者全局作用域也找不到该变量为止;

变量提升

  • var声明的变量/函数声明会提升到函数或者全局代码的开头位置
  • function 操作符的函数提升,会提升赋值操作,var 操作符声明的函数,提升后,不会提升赋值操作;
  • letconst 不会变量提升
  • 同名变量,声明会被提升,后边会忽略
  • 同名函数前面的函数会被覆盖
a = 2
console.log(a) // 2
var a;
var d;
console.log(d) // undefined
d = 4;

console.log(b) // b is not defined

let b = 'let';
const c = 'const';

全局作用域 和 函数作用域

变量拥有全局作用域的情况:

  • 最外层函数 和在最外层函数外面定义的变量
  • 未声明直接赋值的变量
  • window 属性

函数作用域,是指声明在函数内部的变量,和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到

块语句(大括号“{}”中间的语句),如ifswitch条件语句或 forwhile 循环语句,不像函数,它们不会创建一个新的作用域。在块语句中定义的变量将保留在它们已经存在的作用域
如下:

if (true) {
    // 'if' 条件语句块不会创建一个新的作用域
    var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'

块级作用域

块级作用域可通过新增命令letconst声明,所声明的变量在指定块的作用域外无法被访问,通过命令

  • 允许在块级作用域中声明函数
  • 外层作用域无法获取到内层作用域,非常安全明了。即使外层和内层都使用相同变量名,也都互不干扰
  • ES5中,函数只能在顶级作用域和函数作用域中声明,不能在块级作用域中声明。但是在ES6中,函数可以在块级作用域中声明
  • letconst声明的变量禁止重复声明
  • letconst声明的变量不会变量提升

注意:ES6中允许函数在块级作用域中可以声明的条件是必须在大括号里面,否则就会报错


this指向

this

  1. 全局上下文
    全局上下文默认this指向window, 严格模式下指向undefined
  2. 直接调用函数
    let obj = {
      a: function() {
        console.log(this);
      }
    }
    let func = obj.a;
    func();
    
    this相当于全局上下文的情况
  3. 对象.方法的形式调用
     obj.a();
    
  4. DOM事件绑定(特殊)
    onclickaddEventerListenerthis 默认指向绑定事件的元素
  5. new构造函数绑定
    构造函数中的this指向实例对象
  6. 箭头函数
    箭头函数没有this, 因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到 就是window(严格模式是undefined)

闭包

闭包是指那些能够访问自由变量的函数。

作用:

  • 一个是前面提到的可以读取函数内部的变量
  • 另一个就是让这些变量的值始终保持在内存中

经典实例: 用for循环输出函数值的问题

var fnArr = [];
for (var i = 0; i < 10; i++) {
  fnArr[i] =  function(){
    return i
  };
}
console.log( fnArr[3]() ) // 10
// fnArr[3]执行匿名函数 function()时,循环已经结束,则i的结果已经是10

使用IIFE实现闭包

// 使用闭包,保证每次循环都生成私有作用域,保存当前的i值在内存中
var fnArr = [];
for (var i = 0; i < 10; i++) {
  fnArr[i] = (function(){
    var j = i
    return function(){
        return j
     }  
  })()
}
console.log(fnArr[3]()) //3

事件循环机制

JavaScript语言的一大特点就是单线程,即同一时间只能做一件事情,因此有了事件循环机制

  • 异步任务
    • 微任务
      promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver

    • 宏任务
      script 脚本的执行、setTimeoutsetIntervalsetImmediate 一类的定时事件,还有如 I/O操作、UI 渲染等

js 先执行同步任务,再 执行微任务,最后执行宏任务

process.nextTick指定的异步任务总是发生在所有异步任务之前

过程

  • 首先js 是单线程运行的,在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
  • 在执行同步代码的时候,如果遇到了异步事件,js引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务
  • 当同步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行。
  • 任务队列可以分为宏任务队列微任务队列,当当前执行栈中的事件执行完毕后,js引擎首先会判断微任务队列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。
  • 当微任务队列中的任务都执行完成后再去判断宏任务对列中的任务。

参考资料

Scope(作用域)

深入理解JavaScript作用域和作用域链

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值