JS变量知识回顾
# 变量类型
# 原始类型
在JS中,变量分为原始类型和引用类型,其中原始类型有下面几种
number
string
boolean
null
undefined
symbol
BigInt
(es10新增)
# 引用类型
引用类型也叫object
类型,普通对象(Object),数组(Array), 函数(Function), 日期(Date)都是引用类型的一种
# 变量的存储位置
# 原始类型
原始类型都是直接存放在栈中的,栈内存的特点如下
- 存储的值大小固定
- 空间较小
- 可以直接操作其保存的变量,运行效率高
- 由系统自动分配存储空间
因为原始值一般不大,为了操作方便,所以会直接把数据存放在函数的执行栈里。在变量定义时,栈就为其分配好了内存空间。
# 引用类型
引用类型的值被存放在堆里,相对栈来说,堆有下面的特点
- 存储的值大小不定,可动态调整
- 空间较大,运行效率低
- 无法直接操作其内部存储,使用引用地址读取
- 通过代码进行分配空间
而在栈中声明的变量,只是保存了一个指向堆里某个地址的指针
# 数据类型的判断
# typeof操作符
typeof在MDN文档中的描述如下
可以看出typeof判断大部分情况下还是没问题的,只是不太精确
// Numbers
typeof 37 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // Despite being "Not-A-Number"
// Strings
typeof '' === 'string';
typeof 'bla' === 'string';
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
// Symbols
typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// Objects
typeof {a: 1} === 'object';
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object';
// Functions
typeof function() {} === 'function';
// bigint
typeof 42n === 'bigint';
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
使用时只要记住,除了typeof null是'object',typeof function是'function',其他情况下都和JS中的变量分类一样就行了
# instanceof操作符
instanceof操作符主要用来检查构造函数的原型是否在对象的原型链上,也就是说只对引用类型有效, 不过当原型链被修改后,可能产生错误的判断
// defining constructors
function C() {}
let o = new C()
// true, because: Object.getPrototypeOf(o) === C.prototype
o instanceof C
o instanceof Object // true
C.prototype = {}
let o2 = new C();
o2 instanceof C // true
// false, because C.prototype is nowhere in
// o's prototype chain anymore
o instanceof C;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
instanceof对原始类型无效
let simpleStr = 'This is a simple string'
let myString = new String()
simpleStr instanceof String // returns false, string literal is not an object
myString instanceof String // returns true
2
3
4
5
# constructor属性
function Animal (name) {
this.name = name
}
const cat = new Animal('cat');
console.log(cat.constructor === Animal) // true
2
3
4
5
6
7
# toString方法
function type (obj) {
return Reflect.apply(Object.prototype.toString, obj, []).replace(/^\[object\s(\w+)\]$/, '$1').toLowerCase()
}
2
3
这种方法基本可以判断所有的内置类型
console.log(type(1)) // number
console.log(type("123")) // string
console.log(type(true)) // boolean
console.log(type(undefined)) // undefined
console.log(type(null)) // null
console.log(type(Symbol("s"))); // symbol
console.log(type(11n)); // bigint
console.log(type({})); // object
console.log(type([])); // array
console.log(type(function () {})); // function
console.log(type(/reg/)); // regexp
console.log(type(new Date())); // date
2
3
4
5
6
7
8
9
10
11
12
但是对自定义的构造函数无效
function Animal(name) {
this.name = name;
}
console.log(type(new Animal("sena"))); // object
2
3
4
5
# toString方法 + Symbol.toStringTag
我们可以设置es6中通过Symbol暴露的Symbol.toStringTag来改变toString的输出
Animal.prototype[Symbol.toStringTag] = 'Animal';
type(rabbit) // animal
2
现在我们几乎可以用toString满足所有的需求了
# 隐式类型转化规则
转换前类型 | 转换前值 | 转换后(Boolean ) | 转换后(Number) | 转换后(String ) |
---|---|---|---|---|
Number | 123 | true | - | "123" |
Number | Infinity | true | - | "Infinity" |
Number | 0 | false | - | "0" |
Number | NaN | false | - | "NaN" |
BigInt | 123456n | true | 123456 | "123456" |
String | "" | false | 0 | - |
String | "123" | true | 123 | - |
String | "123abc" | true | NaN | - |
String | ”abc” | true | NaN | - |
Boolean | ture | - | 1 | "true" |
Boolean | false | - | 0 | "false" |
Undefined | undefined | false | NaN | "undefined" |
Null | null | false | 0 | "null" |
Object | {} | true | NaN | [object Object] |
Array | [] | true | 0 | "" |
Array | ["abc"] | true | NaN | "abc" |
Array | ["abc", null] | true | NaN | "abc," |
Function | function(){} | true | NaN | "function(){}" |
Symbol | Symbol() | true | TypeError | TypeError |
Boolean 转字符串这行结果我指的是 true 转字符串的例子,不是说 Boolean、函数、Symblo 转字符串都是
true
在转换成Boolean时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。
对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:
- 如果已经是原始类型了,那就不需要转换了
- 如果需要转字符串类型就调用 x.toString(),转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用 valueOf,结果不是基础类型的话再调用 toString
- 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
- 如果都没有返回原始类型,就会报错
- 你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。
# 四则运算符
它的特点如下
- 运算中其中一方为字符串,那么就会把另一方也转换为字符串
- 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串(优先数字)
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
2
3
# 类型比较
之前写过了:https://www.sakura-snow.com/archives/142
# 垃圾回收
# 引用计数
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象,对象将被垃圾回收机制回收。
function test(){
var a = {} ; // a的引用次数为0
var b = a ; // a的引用次数加1,为1
var c =a; // a的引用次数再加1,为2
var b ={}; // a的引用次数减1,为1
}
2
3
4
5
6
但是这个算法有一个严重的问题,那就是循环引用
function f(){
var o = {};
var o2 = {};
o.a = o2; // o 引用 o2 o2的引用次数+1
o2.a = o; // o2 引用 o o的引用次数+1
return "abc";
}
f();
2
3
4
5
6
7
8
9
10
在这个例子里,o和o2不会被回收,因为他们的引用次数都不为0。
IE 6, 7使用引用计数方式对 DOM 对象进行垃圾回收。该方式常常造成对象被循环引用时内存发生泄漏
var app;
window.onload = function(){
app = document.getElementById("#app");
app.circularReference = app;
app.lotsOfData = new Array(10000).join("*");
};
2
3
4
5
6
因为app自己使用circularReference属性引用了自己,所以它的被引用次数始终不为0,即使被从dom树中删去也不会被回收,现在这种回收算法只有很老的浏览器才会使用了
# 标记清除
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
这个算法的垃圾回收分为2个阶段,分别是标记和清除。
- 标记 :从根节点开始标记引用的对象。
- 清除 :未被标记引用的对象就是垃圾对象,可以被清理。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。
# GC优化
和其他语言一样,javascript的GC策略也无法避免一个问题:GC时,停止响应其他操作,这是为了安全考虑。而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。
# 分代回收
这个和Java回收策略思想是一致的,也是V8所主要采用的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。
# 增量GC
这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”。
这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题。 因为每种方案都其适用场景和缺点,因此在实际应用中,会根据实际情况选择方案。
- 01
- 009-Palindrome Number[回文数]03-10
- 02
- 008-String to Integer (atoi)[字符串转整数]03-10
- 03
- 004-Reverse-integer[整数反转]03-09