原稿:语雀 · JS 知识点 · 原目录:JavaScript › JS 知识点

JS 数据类型

JavaScript 中有八(7+1)种基本的数据类型(七种基本数据类型,也称为原始类型,object 为复杂数据类型)

  • number:用于整数或浮点数,在 ±(253-1) 范围内的整数
  • bigInt:用于任意长度的整数
  • string:用于字符串
  • boolean:用于 true 和 false
  • null:只有一个 null 值的独立类型
  • undefined:只有一个 undefined 值的独立类型
  • symbol:用于唯一的标识符
  • object:用于更复杂的数据结构

如何理解原型?

  • 构造函数:任何情况下创建一个函数,都会在内部为其创建一个prototype属性,该属性是一个指向原型对象的引用。使用原型对象可以允许上面定义的属性与方法被对象实例共享。
  • 原型对象:原型对象会默认自动获得一个constructor属性,该属性是指回构造函数的引用。自定义构造函数的原型对象默认只会获得constructor属性,其余方法都继承自Object
  • 原型对象的原型:原型对象本身是Object或父类的实例,它也有一个暴露为__proto__的属性指向父类构造函数的原型对象(隐式原型)。特别的,Object的原型对象是原型链的起点,其constructor指向Object()构造函数,同时Object原型对象的不再有原型,其不再具有__proto__
  • 对象实例:每个被构造的实例内部也有__proto__,同样指向其构造函数原型对象

参考ES5原型链继承图示理解:

JS 知识点 配图

var、let、const

var

  • 声明作用域:以var声明的变量会自动添加到最近的全局/函数上下文中
  • 变量提升:var支持“变量提升”,会把所有变量声明提升至作用域顶,从而实现先调用后声明不报错
  • 冗余声明:作用域内外可多次使用var声明一个变量

let

  • 声明作用域:let的作用域是块级的,即根据{ }限定,全局作用域内let声明的变量也不会存入window,而是在script中
  • 变量提升:严格意义上let也会提升变量,但由于暂时性死区,无法在声明前使用let变量
  • 冗余声明:let在同一作用域内不能冗余声明一个变量,会抛出SyntaxError

const

基本和let一致,但必须在声明同时初始化,且在生命周期中无法重新赋值


new 过程

new一个对象时发生了 2. 在内存中创建一个新对象 4. 新对象内部的

\[Prototype\[Prototype

]特性被赋值为构造函数的prototype属性 6. 构造函数内部的this被赋值为这个新对象(即this指向新对象) 8. 执行构造函数内部的代码 10. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象

手写new():原生重写


this

参考链接:深入理解this机制系列第一篇——this的4种绑定规则

this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。 this 的绑定在构建执行上下文时确定,在实际开发中,this 的指向可以通过四种调用模式来判断:

  • 默认绑定:当一个函数不是一个对象的属性时,直接作为函数来调用时,调用行为发生在全局上下文中,this 默认绑定到全局对象
  • 隐式绑定:被直接对象作为方法调用的函数,其上下文的 this 隐式绑定到该对象上
  • 显式绑定:通过 call()/apply()/bind() 把指定对象绑定到 this 上,叫做显式绑定
  • new 绑定:new 构造一个新对象时,构造函数内部的 this 会自动绑定到新建的对象上

绑定优先级:new绑定>显式绑定>隐式绑定>默认绑定


闭包

定义:当一个函数可以访问到其他函数作用域中自由变量时,即构成闭包,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。

意义:闭包的意义就是“隐藏变量”,对外隐藏状态。当前函数能够访问到外部函数词法环境中的变量,甚至在外部函数执行上下文已经离开执行栈时,此时外部函数的变量称为当前函数的“私有变量”,而外部函数的词法环境也会被一致保存在内存中

场景: 2. 实现装饰器函数:比如防抖和节流装饰器,外部 decorator 中的 timer 被返回的 wrapper 函数所引用着,timer 变成了一个私有变量,只有调用 wrapper 才能改变计时器的状态 4. 解决循环中多个闭包共享词法作用域的问题:典型的场景就是 for 循环中使用 var 声明 i,通过 setTimeout 打印时,为了实现每次输出值的不同,使用更多的闭包解决这个问题;构造一个立即执行函数,构成计时器回调的闭包,这个立即构造函数中的 i 就成了私有变量,对外隐藏,只有回调执行时才能访问

for (var i = 0; i < 5; i++) {
  (function (i) {
    setTimeout(() => {
      console.log(i);
    }, 1000 * i);
  })(i);
}
  1. 私有变量与特权函数:对于构造函数内不想被外界直接访问修改的变量,同时又需要操作,通过 this 提供特权函数,让特权函数构成闭包,其具备访问外部构造函数中私有变量的能力
function Counter() {
  let cnt = 0;
  this.add = function () {
    cnt++;
  };
  this.now = function () {
    return cnt;
  };
}
let counter = new Counter();
counter.add();
counter.add();
console.log(counter.now());

箭头函数和普通函数

  • 箭头函数不能使用 arguments、super 和 new.target,也不能用作构造函数。此外也没有 prototype 属性
  • 箭头函数相对于普通函数语法更简洁优雅
  • 箭头右侧可直接跟一个表达式,并隐含返回计算结果
  • 当右侧是多行表达式或复杂函数,需要使用 {} 并显式返回结果,否则返回 undefined
let sum = (a, b) => a + b;
// 等价于
let sum = function(a, b) {
  return a + b;
};
  • 箭头函数没有this,其this需从外部作用域获取,因此取决于箭头函数定义位置的上下文,下面是babel转化ES6箭头函数的结果

箭头函数this继承自外层第一个普通函数的this;如果外层没有普通函数,它的this会指向window

 

// ES6

const obj = {

   getArrow() {

       return () => {

           console.log(this === obj);

       };

   }

}

// ES5,由 Babel 转译

var obj = {

   getArrow: function getArrow() {

       var _this = this;

       return function () {

           console.log(_this === obj);

       };

   }

};


Object 对象

对象属性的特征

  • \[Configurable\[Configurable]:属性是否可通过 delete 删除并重新定义,默认 true
  • \[Enumerable\[Enumerable]:属性是否可通过 for-in 或 Object.keys() 枚举,默认 true
  • \[Writable\[Writable]:属性是否可被修改,默认 true
  • \[Value\[Value]:属性值,默认 undefined

下面是三种对 Object 的静态方法,用于限制对象属性

Object.keys()

返回一个由一个给定对象的自身可枚举属性组成的数组

作为对比 for-in 循环则会遍历对象自身+其原型链上可枚举属性,每次获取的索引都是字符串类型

Object.freeze()

冻结对象,使其不能被修改(冻结一层,)

  • 不能新增、删除属性
  • 不能修改已有属性:不能修改已有属性的\(\[可枚举性\)]、\(\[可配置性\)]、\(\[可写性\)]、\(\[值\)],但是当属性为对象,该嵌套对象的属性可修改,除非嵌套对象被冻结
  • 对象 \[prototype\[prototype] 不可修改

Object.seal()

封闭对象

  • 不能新增属性
  • 现有属性标记为不可配置,即不可删除
  • 对象 \[prototype\[prototype] 不可修改,已有可写属性可以修改

Object.preventExtensions()

让一个对象不可扩展

  • 不能新增属性
  • 可删除已有属性
  • 对象 \[prototype\[prototype] 不可修改,其余属性都可修改

从限制的严格性来说:Object.preventExtensions()

\[不可增,可删可改\]

< Object.seal()

\[不可增不可删,可改\]

< Object.freeze()

\[不可增不可删不可改\]

数字处理

  • IEEE 754

JS 的 Number 类型遵循的是 IEEE 754 标准,使用的是 64 位固定长度来表示,见Ch2 信息表示与处理