重读JavaScript高级程序设计,反向碾压面试官?
第三章语言基础终于读完了,很多面试题都是来自这一章。但是会变成面试题的又似乎都是一些“坑”。我做了一些笔记,也选择性的忽略了一些,比如说类型转换。对于基础的语句和操作符就没有单独记下来了。
var、let、const
本身这不是一个问题,但是由于 var 的特殊性,ES6有了let和const之后,反而变成了一个面试问题。
本来想把这个坑给忽略掉,但是考虑到很多面试官都是从var时代来的 ̄□ ̄||
var 和 let
- var 声明的范围是函数作用域,let 声明的是块作用域。块作用域是函数作用域的子集。
if (true) {
// 这里是块作用域
let age = 26;
}
console.log(age); // ReferenceError: age没定义
function test() {
// 这里是函数作用域
var message = 'hi';
}
test();
console.log(message); // 报错
- var 声明提升,用 var 声明的变量会自动提升到函数作用域顶部。let 则不会。
function test() {
console.log(age);
var age = 26;
}
test(); // undefined
不会报错是因为ECMAScript运行时把它看成等价于如下代码:
function test() {
var age;
console.log(age);
age = 26;
}
test(); // undefined
- 可以多次使用var声明同一个变量。let 则会报错。
- let 暂时性死区。原因是2,let声明变量不会在作用域中被提升。
console.log(name); // ReferenceError: name没定义
let name = 'Hello';
- 全局声明。使用 var 在全局作用域中声明的变量会成为你 window 对象的属性。let声明的则不会。
在 var 时代,如果不控制好作用域,经常会发生全局变量冲突。有了let之后就不用担心这个问题了,用let重复声明同一个变量会报错。同3。
- 不能使用 let 进行条件式声明。因为1、let 声明的是块作用域。
- for 循环中的 let 声明
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 5、5、5、5、5
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 0、1、2、3、4
const
和let行为基本相同。
- const 声明变量是必须初始化。
- const 声明的变量不允许修改。
如果,const变量引用的是一个对象,修改这个对象的内部属性并不违反2限制。
总结
有了ES6之后,基本不必再使用var了。上面大部分的内容都可以忽略掉。比如变量提升和全局声明成为window对象的属性这样的特性尤其不建议使用了。
数据类型
ECMAScript共有7中数据类型,其中 Symbol 是ES6新增的。
- Undefined
- Null
- Boolean
- Number
- String
- Symbol
- Object
typeof 操作符
typeof 操作符会返回一个字符串,它可能是:
- “undefined”
- “boolean”
- “string”
- “number”
- “object”
- “function”
- “symbol”
有什么不对?少了一个 Null。 因为 typeof null 返回的是“object” 多了一个 “function” 。 函数不代表一种数据类型,但是可以通过 typeof 辨别出来
浮点值的精度问题
0.1 + 0.2 == 0.3 // false
这是因为使用了IEEE 754数值,这个错误并非ECMAScript独有,其它使用相同格式的语言也有这个问题。
- 整数和浮点值都是Number类型·
- 浮点值的精确度最高可达17位小数,但还是远不如整数精确。因此,对于非整数的Number类型无法用 == 来比较
- 我们可以使用最小精度值来实现上述比较:Number.EPSILON Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON
NaN == NaN // false
NaN 不等于包括它自己在内的任何值。可以通过 isNaN() 来进行判断。
字符串
ES6新增了模板字面量。使用反引号
标识
- 保留换行符,可以跨行定义字符串。
- 支持字符串插值,可以在 ${} 中放入JavaScript表达式
Symbol类型
符号是ES6新增的数据类型。它的作用是确保对象属性使用唯一标识,不会发生属性冲突。
- Symbol 可以传入一个字符串作为描述,但是这个描述和符号的定义或标识无关。
- Symbol 函数不能与new 关键字一起当做构造函数用
- 全局符号注册表,使用Symbol.for()方法
数值转换
- parseInt() 和 parseFloat() 可以将字符串转换为数值。
- Number() 可以将任何数据类型转换为数值。
如果可以的话,不建议使用
操作符
相等和不相等 == 和 != ,它们在比较值钱会执行类型转换。
“55” == 55 // true
如果可以的话,不建议使用
全等和不全等 === 和 !==
用这个!
for - in
用于枚举对象中的非符号键属性
- 符号键即Symbol类型的键
- 属性包含原型链上的属性
可以通过 hasOwnProperty 方法来区分
for -of
用于遍历可迭代对象的元素。 什么是可迭代对象?
定义了Symbol.iterator 方法的对象! 我们给一个普通对象定义他的 Symbol.iterator 方法,就可以使用 for-of来遍历了。
let range = {
from: 1,
to: 5
};
// 1. for..of 调用首先会调用这个:
range[Symbol.iterator] = function() {
// ……它返回迭代器对象(iterator object):
// 2. 接下来,for..of 仅与此迭代器一起工作,要求它提供下一个值
return {
current: this.from,
last: this.to,
// 3. next() 在 for..of 的每一轮循环迭代中被调用
next() {
// 4. 它将会返回 {done:.., value :...} 格式的对象
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// 现在它可以运行了!
for (let num of range) {
console.log(num); // 1, 然后是 2, 3, 4, 5
}
总结
读完第三章,没有太多惊喜,认真学习了还没用过的Symbol类型。因为这章讲的是语言基础,基本都是在平常使用中遇到过的坑了~