前言
前几天,@ConardLi 发布了一篇名叫《一名【合格】前端工程师的自检清单》的文章,才发现自己的知识体系原来有如此多的漏洞。这里也感谢作者的分享。由此,我也萌发了对照列表,记录和学习一遍,让自己没那么迷茫。
一、JavaScript基础
1. JavaScript规定了几种语言类型 (7 种)
基本数据类型(6种):string, number, boolean, null, undefined, symbol
引用数据类型(*):Object (万物皆对象...)
复制代码
2. JavaScript对象的底层数据结构是什么
散列表(哈希表)
复制代码
3. Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol
我已知使用的地方,组件属性(mixin 模式)、常量定义。
要手写必须先了解Symbol.
复制代码
Symbol
特点 | 实例 |
---|---|
函数 | Symbol() |
不能通过 new 关键字调用函数 | new Symbol() => TypeError |
instanceof 指向为 false | let a = Symbol('a');a instanceof Symbol => false |
返回值类型为 "symbol" | typeof Symbol() === 'symbol' |
唯一 | Symbol() != Symbol() |
可传参,toString可查看 | Symbol('foo').toString() === 'Symbol(foo)' |
Symbol 不可以运算 | 'Im a' + Symbol('foo') => TypeError |
Symbol 可显式转 Str 和 Bool, 不能转 Num | String(Symbol('foo')) || Boolean(Symbol('foo')) |
Symbol 属性不能用常规方法遍历 (for in) | 无 |
Symbol.for([desc]) 设置唯一Symbol | Symbol.for('foo') === Symbol.for('foo') |
Symbol.keyFor([Symbol]) 查找Symbol 的key | let a = Symbol.for('foo');Symbol.keyFor(a) === 'foo' |
此外我们需要先看一下Symbol 的规范。
Symbol ( [ description ] )
When
Symbol
is called with optional argument description, the following steps are taken:
- If NewTarget is not undefined, throw a TypeError exception. (不能 new)
- If description is undefined, let descString be undefined. ()
- Else, let descString be ToString(description).
- ReturnIfAbrupt(descString). (报错就返回)
- Return a new unique Symbol value whose [[Description]] value is descString. (返回一个唯一Symbol值,Description 的值就是 descString 的值)
(function (w) {
// 10. 可以通过 Symbol.for 来设置全局唯一的 Symbol. ( 核心: 做一个映射表即可 )
let globalSymbolMap = {}
// ok 1. 函数 ok3
function MySymbol(description) {
// ok 2. 无法使用 new 关键字
if(this instanceof MySymbol)
throw new TypeError('MySymbol is not a constructor')
// ok 3. instanceof 返回 false. 因此只能对象
// err 4. 无法实现 typeof MySymbol() === 'symbol',
// ok 5. 返回的对象不是同一个,当然不一样
// ok 6. `Symbol('foo').toString() === 'Symbol(foo)'` 实现
let symbol = Object.create({
toString() {
return 'MySymbol(' + ( this.__Description__ || '' )+ ')'
},
//ok 7. 运算会调用 valueOf , 覆写即可。
//ok 8. 可以显示转 String 和 Boolean, 不能转 Number (因为复写了 valueOf)
valueOf() {
throw new Error('Cannot convert a MySymbol value')
}
})
// err 9.无法做到无法被变遍历。 因为遍历对象是不确定的,对象的属性设置也是需要控制那个对象才可以。
Object.defineProperties(symbol, {
'__Description__': {
value: description === undefined ? undefined : String(description),
writable: false,
enumerable: false,
configurable: false
}
})
return symbol
}
// 10 设置for
Object.defineProperties(MySymbol, {
for: {
value: function(desc) {
if (globalSymbolMap[desc])
return globalSymbolMap[desc]
globalSymbolMap[desc] = MySymbol()
return globalSymbolMap[desc]
},
},
// 11. 设置 keyFor
keyFor: {
value: function(symbol) {
for(let key in globalSymbolMap)
if (symbol === globalSymbolMap[key])
return key
},
}
})
w.MySymbol = MySymbol
})(window)
复制代码
4. JavaScript中的变量在内存中的具体存储形式
基本数据类型 和 引用数据类型。
内存中会被开辟两块地方,一个是栈区,一个是堆区。基本数据类型的值会放在栈,引用数据类型的值会放在堆,它在堆的地址会放在栈。
// 自己随便画的,大家不要喷我。。。。。
复制代码
5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作
之前有说过基本数据类型有6种。其中,Symbol 较为特殊。他必须通过 Symbol() 获取,因为没有拆箱和装箱。
复制代码
- null => Null
- undefined => Undefined
上面两个类型下都只有一个值,也没方法,就不及与讨论。
- number => Number
- string => String
- boolean => Boolean
拆箱操作:valueOf()或者toString()
装箱操作:调用其他操作对当前数据进行操作的方法.(例如,String.toFixed , String.substring )
6. 理解值类型和引用类型
根据第5题,值类型的数据直接存放在栈。引用类型在栈中存放了真实数据的地址
复制代码
7. null和undefined的区别
表象:
null: 是关键字,typeof null 是 object。
undefined:是全局变量,为了用于区分空值和未定义而引入的。
复制代码
根据 js权威指南中的描述:
null 和 undefined 都表示“值的空缺”
你可以认为undefined是表示系统级的、出乎意料的或类似错误的值的空缺,而null是表示程序级的、正常的或在意料之中的值的空缺。
8. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
- typeof
- 优点:简单....(也不知道算不算)
- 缺点:区分不了 null 和 object 和 array,
- instanceof (判断变量是否为 某对象的实例)
- 优点:null instanceof Object === false; [] instanceof Array === true;
- 缺点:区分不了基本数据类型
- Object.prototype.toString.call(data) 其中,data 为入参
- 优点:可以分辨大部分类型
- 缺点:引用数据类型不能辨别具体得一些区别.(例如, 自定义的 Person 函数,返回的是 [object Object])
9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
下图是《Javascript 权威指南》关于数据转换的表
个人总结就是
-
string + 其他类型,加号就是做字符串拼接。其他数字类型就会转换成 string类型再相加。
- "string" + 1 => "string1"
- "string" + true => "stringtrue"
- "string" + null => "stringnull"
- "string" + undefined => "stringundefined"
- "string" + [] => "string"
- "string" + {} => "string[object object]"
-
number: 一般用一下类型,都会转Number.
- +/- 在前
- [+ - * / %] (算术运算符)
- [> < >= <= == != === !===] (关系运算符)
如下:
- +"1" => 1
- -true => -1
- 1 + true => 2
- 1 + ["9"] => 19
- 1 + [9] => 19
- +[9] + 1 => 10
- 1 + null => 1
- 1 + {} => 1[object Object]
关系运算符
- "2" > 10 => false // 转数字比较
- "2" > "10" => true // 比的是 ASCII码
- NaN == NaN => false
- undefined == null => false
看到有一些史诗级坑的例子
- [] == 0 //true
- ![] == 0 // true
- [] == ![] // true
- [] == [] // false
- {} == !{} // false
- {} == {} // false
10.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法
-
背景: JS 遵循的是 IEEE 754 规范,采用双精度存储,占用 64 bit,也就是8位。虽然很多看似有穷的数据,对于计算机来说确实无穷的。如,大整数,浮点数。计算机只有0和1,所以十进制里面的四舍五入,在计算机里面就会变成 “0舍1入”了。
-
js 里面最大的数字是 2^53。原因:64位,1位表示正负,11位表示指数,52位表示尾数。52 + 1 = 53
-
js 里面最大的安全数字是 2^53 - 1
-
在我个人工作中,如若有大数字和浮点精度要求较高都是用 String存入。
其他
文章有误之处,敬请指正,以免误导大家~