Skip to content

Symbol类型

符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符 符号就是用来创建唯一记号,进而用作非字符串形式的对象属性

特点: 1.使用for...in循环和Object.getOwnPropertyNames()方法都得不到 Symbol 键名,需要使用Object.getOwnPropertySymbols()方法 2.使用Reflect.ownKeys()也可以返回所有类型的键名 3.Symbol.for(),Symbol.keyFor();声明同key的Symbol(同一个Symbol复用)、返回Symbol的key

Object类型

每个Object实例都有如下属性和方法 1.constructor,构造函数 2.hasOwnProperty(propertyName),判断对象是否有某个属性 3.isPrototypeOf(object),判断是否是另一个对象的原型 4.propertyIsEnumerable(propertyName),判断属性能否被for...in枚举 5.toLocaleString(),返回对象的字符串表示,反映对象所在的本地化执行环境 6.toString(),返回对象的字符串表示 7.valueOf():返回对象对应的字符串、数值或布尔值表示

操作符

前缀自增(减)、后缀自增(减)操作符 前缀:变量的值都会在语句被求值之前改变 后缀:变量的值在语句被求值后才发生 如果只是单自增自减,没有区别,区别在混用的时候

// 前缀
let num1 = 2; 
let num2 = 20; 
let num3 = --num1 + num2;  // num3 = 21
let num4 = num1 + num2;    // num4 = 21
// 后缀
let num1 = 2; 
let num2 = 20; 
let num3 = num1-- + num2;  // num3 = 22
let num4 = num1 + num2;    // num4 = 21
操作符

1.按位非(~) 相当于取相反数并减1 2.按位与(&) 3.按位或(|) 4.按位异或(^) 4.有符号右移(>>)、左移(<<) 右移:相当于除以2的n次幂取整 左移:相当于乘以2的n次幂 5.无符号右移(>>>) 正数相当于有符号右移(>>) 6.逻辑非(!)、与(&&)、或(||) 7.乘(*)、除(/)、取余(%) 8.指数操作符(**)

let sqrt = 16; 
sqrt **= 0.5; 
console.log(sqrt); // 4

9.等于(==)和不等(==),全等(===) 先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。全等===不转换

语句

标签语句
start: for (let i = 0; i < count; i++) {   
    console.log(i);  
}
// 配合break用来结束循环
with语句

with语句的用途是将代码作用域设置为特定的对象 ⚠️警告:由于with语句影响性能且难于调试其中的代码,通常不推荐在产品代码中使用with语句。

变量、作用域与内存

1.ECMAScript变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是最简单的数据,引用值(reference value)则是由多个值构成的对象。原始值的变量是按值(by value)访问的,引用值是保存在内存中的对象。在操作对象时,实际上操作的是对该对象的引用(reference)而非实际的对象本身。为此,保存引用值的变量是按引用(by reference)访问的。

2.ECMAScript中所有函数的参数都是按值传递的(意味着对传递进来的数据更改,不会影响到外面的数据) 如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。 这里有一个难点,就是对象传递到函数里之后,会怎么样? 第一种情况:

function setName(obj) {   
    obj.name = "Nicholas";  
} 
let person = new Object(); 
setName(person); 
console.log(person.name);  // "Nicholas"

这个对象被传给setName()方法,并被复制到参数obj中。在函数内部,obj和person都指向同一个对象。结果就是,即使对象是按值传进函数的,obj也会通过引用访问对象。当函数内部给obj设置了name属性时,函数外部的对象也会反映这个变化,因为obj指向的对象保存在全局作用域的堆内存上。 第二种情况:

function setName(obj) {   
    obj.name = "Nicholas";  
    obj = new Object(); 
    obj.name = "Greg"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name);  // "Nicholas"

如果person是按引用传递的,那么person应该自动将指针改为指向name为"Greg"的对象。可是,当我们再次访问person.name时,它的值是"Nicholas",这表明函数中参数的值改变之后,原始的引用仍然没变。当obj在函数内部被重写时,它变成了一个指向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了。

⚠️注意:ECMAScript中函数的参数就是局部变量。

执行上下文与作用域

变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。 每个上下文都有一个关联的变量对象(variable object),而这个上下文中定义的所有变量和函数都存在于这个对象上。 虽然无法通过代码访问变量对象,但后台处理数据会用到它。 上下文在其所有代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)

内存管理(如何优化代码性能)

1.如果数据不再必要,那么把它设置为null,从而释放其引用。这也可以叫作解除引用(适合全局变量和全局对象的属性)

2.通过const和let声明提升性能

3.隐藏类和删除操作

使用V8 JavaScript引擎。V8在将解释后的JavaScript代码编译为实际的机器码时会利用“隐藏类”。 两个基于一个类实例化的对象会共用一个“隐藏类”,享相同隐藏类的对象性能会更好 如果动态添加了属性或者delete删除了属性,两个对象就不共用一个“隐藏类” 所以尽量不要动态添加属性(类一次声明好) 不要delete删除属性,最佳实践是把不想要的属性设置为null

4.注意内存泄漏

常见内存泄漏: 4.1 意外声明全局变量 4.2 定时器(调用闭包,引用外部变量,只要定时器一直运行,外部变量就不会被清除) 4.3 js闭包

5.静态分配与对象池

为了提升JavaScript性能,最后要考虑的一点往往就是压榨浏览器,关键问题就是如何减少浏览器执行垃圾回收的次数 合理使用分配的内存,同时避免多余的垃圾回收,就可以保住因释放内存而损失的性能 如果有很多对象被初始化,然后一下子又都超出了作用域,那么浏览器就会采用更激进的方式调度垃圾回收程序运行,这样当然会影响性能 5.1 一个策略是使用对象池。在初始化的某一时刻,可以创建一个对象池,用来管理一组可回收的对象。应用程序可以向这个对象池请求一个对象、设置其属性、使用它,然后在操作完成后再把它还给对象池。由于没发生对象初始化,垃圾回收探测就不会发现有对象更替,因此垃圾回收程序就不会那么频繁地运行。

总结:

  • 原始值大小固定,因此保存在栈内存上。
  • 从一个变量到另一个变量复制原始值会创建该值的第二个副本。
  • 引用值是对象,存储在堆内存上。
  • 包含引用值的变量实际上只包含指向相应对象的一个指针,而不是对象本身。
  • 从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象。
  • typeof操作符可以确定值的原始类型,而instanceof操作符用于确保值的引用类型。
  • 任何变量(不管包含的是原始值还是引用值)都存在于某个执行上下文中(也称为作用域)。这个上下文(作用域)决定了变量的生命周期,以及它们可以访问代码的哪些部分。
  • 执行上下文分全局上下文、函数上下文和块级上下文。
  • 代码执行流每进入一个新上下文,都会创建一个作用域链,用于搜索变量和函数。
  • 函数或块的局部上下文不仅可以访问自己作用域内的变量,而且也可以访问任何包含上下文乃至全局上下文中的变量。
  • 全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。
  • 变量的执行上下文用于确定什么时候释放内存。
  • 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
  • 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
  • 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript引擎不再使用这种算法,但某些旧版本的IE仍然会受这种算法的影响,原因是JavaScript会访问非原生JavaScript对象(如DOM元素)。
  • 引用计数在代码中存在循环引用时会出现问题。
  • 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。