基础知识
1. 标识符
标识符(identifier)是变量、函数、属性、参数的名称,需遵循以下格式:
- 首字符必须为字母,或者下划线(_)或者$;
- 所有字符必须为字母、数字、下划线或$符号。
- 上述“字母”可以是 ASCII 或 Unicode 编码(所以中文也可做标识符),但不推荐使用。
- 推荐使用驼峰命名。
2. 变量声明
2.1 var 声明
2.1.1 函数中使用 var
声明变量,在函数中使用 var
时,函数执行时创建变量,执行后变量销毁
function test() {
var a = 2;
}
test();
console.log(a); // error
变量前不使用任何关键字时,将创建全局变量。
function test() {
a = 2;
}
test();
console.log(a); // 2
上述代码中,函数执行时创建变量a,函数执行完成后仍能访问到a,但不推荐不使用任何关键字直接声明变量。
2.1.2 var 的变量提升
所谓变量提升(hoisting),就是在声明变量时,会把声明自动放在(代码或函数的)顶部。
function foo() {
console.log(age);
var age = 26;
}
foo(); // undefined
上述代码等同于:
function foo() {
var age;
console.log(age);
age = 26;
}
foo(); // undefined
即代码var age = 26
可看作两部分:var age
和 age = 26
前者为声明变量,后者为变量赋值;声明变量时会被提升到(代码或函数)的顶部。这就是变量提升。
2.2 let 声明
2.2.1 let 作用域
let
和 var
类似但有一些不同,最重要的区别是 let
是块级作用域(比如在if中、在for中),var
是函数级作用域。
// 在块级作用域中使用 var
if(true) {
var name = 'Matt';
console.log(name);
}
console.log(name); // Matt
// 在块级作用域中使用 let
if(true) {
let age = 26;
console.log(age);
}
console.log(age); // ReferenceError: age is not defined
2.2.2 let 变量提升
let
也存在变量提升,但和 var
不同的是, let
提升后,在未赋值前访问不到(被放入'temporal dead zone
'),直到被赋值为止。所以在赋值前访问 let
声明的变量会报错,而在赋值前访问 var
声明的变量得到 undefined
。一句话,let
变量提升时访问不到
console.log(name); // udefinde
var name = 'Paul';
console.log(age); // ReferenceError: age is not defined
let age = 26;
2.2.3 let 不能全局声明
用 var
声明变量,变量会作为属性赋给window对象,let
则不会。
var name = 'Matt';
console.log(window.name);// 'Matt'
let age = 26;
console.log(window.age); // undefined
2.2.4 let 不能重复声明
var
允许重复声明变量,而 let
不能重复声明变量。
这是由于var
的变量提升作用,js会把所有的重复声明都变成单一的一个声明。
var a = 1;
var a = 2;
上述代码等同于:
var a;
a = 1;
a = 2;
所以var
可以重复声明变量;但是再多人协作开发时,重复声明一个用途不同的变量,会导致各种问题。
而let
很好的解决了重复声明问题。
let age = 26;
let age = 27; // Uncaught SyntaxError: Identifier 'age' has already been declared
2.2.5 for循环中的let
for
循环中用 var
声明迭代变量时:
for (var i = 0; i < 5; i++) {
// do somthing
}
console.log(i); // 5
for
循环中用 let
声明迭代变量时:
for (let i = 0; i < 5; i++) {
// do somthing
}
console.log(i); // ReferenceError: i is not defined
常见问题:for 循环中使用 setTimeout
:
for (var i = 0; i < 5; i++) {
setTimeout(()=>{console.log(i)},0)
}
//5, 5, 5, 5, 5
setTimeout
是异步代码,for
循环为同步代码,即使等待时间为0,也要等for
循环执行完后才会执行setTimeout
中的函数;此时i已经等于5了,所以全部输出5。
let
的出现很好的解决了这一问题:
for (let i = 0; i < 5; i++) {
setTimeout(()=>{console.log(i)},0)
}
//1, 2, 3, 4, 5
let
循环中,每循环一次会创建一个块级作用域,循环结束后,有5个块级作用域,每个块级作用域中的i
都有不同的值,所以输出1,2,3,4,5
2.3 const 声明
const
和 let
一样,也是块级作用域;但有一个重要区别—声明时必须赋值,而且不可重新赋值,更不可重复声明。
const age = 26;
age = 43; // TypeError: assignment to a constant
// const 是块级作用域
const name = 'Matt';
if (true) {
const name = 'Mike';
}
console.log(name); // 'Matt'
如果 const
声明了一个对象(引用类型)数据,该对象可以修改其属性(因为实体指向的是对象的地址)。
const p = {};
p.name = "Matt";
3. 数据类型
6种简单数据类型:Undefined
Null
Bollean
Number
String
Symbol
1种复杂数据类型:Object
( function 本质上也是一个 Object )
3.1 typeof 操作符
用于判断数据的类型,返回值有:"undefined", "boolean", "string", "number", "object", "function", "symbol"
注意:
typeof null
返回"object"
,null
是一个空对象引用(地址指向一个不存在的对象);typeof
本身是一个操作符,而非一个函数
3.2 Undefined
变量已声明但未赋值,该变量将会有一个特殊的值—undefined。
let msg;
conso.log(msg == undefined); // true
上述代码等同于:
let msg = undefined;
conso.log(msg == undefined); // true
undefined
和 ReferenceError: xxx is not defined
,前者是声明但为赋值,后者是压根就没声明。但使用 typeof
操作符判断一个未声明的值,返回 undefind
let a;
console.log(a);// undefined
consol.log(b); // ReferenceError: b is not defined
typeof(b); //undefined
3.3 Null
null
值是一个空对象指针,所以typeof null
返回 "object"
。
undefined
是 null
的衍生值, ECMA-262 规定二者表面上相等:
console.log(null == undefined) // true
3.4 Boolean
Boolean 有 true
和 false
两种值,还可以通过内置 Boolean()
方法将其他值转换为Boolean
let msg = "Hello";
console.log(Boolean(msg)); // true
Boolean()
方法对应的返回值:
数据类型 | true | false |
---|---|---|
String | 任何非空字符串 | 空字符串 |
Number | 非0数值 | 0, NaN |
Object | 任何对象 | null |
Undefined | / | undefined |
这种转换有助于理解一些条件语句中的判断为何能生效,比如:
let msg = "Hello";
if(msg) {
console.log("Value is true"); // 该语句能够输出
}
因为在条件语句中,会自动通过Boolean()
方法将判断条件转换为Boolean
。
3.5 Number
js中的Number包括整数型和浮点型,整数型支持8进制和16进制的表示方式,8进制必须以0开头,16进制必须以0x开头。
3.5.1 浮点类型
对于下面的写法,js会将浮点型自动转为整数类型以节省内存:
let f1 = 1.;
let f2 = 10.0;
支持科学计数法:
let floatNum = 3.125e7; // equal to 31250000
浮点类型精确到小数点后17位,从而导致了其运算会出现极小的不精确,如 0.1 + 0.2 会得到 0.30000000000000004。所以要避免数值计算作为条件语句的判断条件:
if (a + b = 0.3) { // 要避免这样写
// do somthing
}
3.5.2 取值范围
由于内存限制,Number类型有自己的取值范围,可以分别用 Number.MIN_VALUE
和 Number.MAX_VALUE
得到最小和最大取值。如果数值运算结果超出取值范围,则会得到Infinity
或 -Infinity
表示正无穷和负无穷。
可以用isFinite()
判断数值是否超限。
let result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false, 表示超限
3.5.3 NaN
- Not a Number,表示数值计算不能返回
Number
类型的数据,比如0做除数时; -
NaN
与任何数值运算都会得到NaN
; -
NaN
不等于任何值,包括NaN
; - 可以用
isNaN()
判断是否为NaN
。
console.log(NaN == NaN); // false
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false - 10 is a number
console.log(isNaN("10")); // false - can be converted to number 10
console.log(isNaN("blue")); // true - cannot be converted to a number
console.log(isNaN(true)); // false - can be converted to number 1
3.5.4 数值转换
有三种方法可以将其他类型转为Number类型:Number() parseInt() parseFloat()
;
下面是几个特殊的转换:
Number(null); // return 0
Number(true); // return 1
Number(""); // return 0
Number("Hello"); //return NaN
parseInt("123aabbcc"); // return 123
parseInt(""); // return NaN
parseInt(22.5); // return 22
parseFloat("22.34.5") // return 22.34
parseFloat("1234blue"); // return 1234 - integer
3.6 String
表示0到多个连续的16位Unicode编码的字符。 可以使用单引号、双引号、反引号(``)(ES6)
3.6.1 字符串的不可变性
ECMAScript中的字符串是不可变的,也就是说字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后用另一个包含新值的字符串填充该变量。
不可变的原因是字符串是位于堆内存上的,想要改变则需要销毁原来的对象,重新创建一个新的。
3.6.2 toString() & String()
toString()
和 String()
作用是将其它数据类型转换为 String
类型。几乎每种类型都自带 toString()
方法,但 null
和 undefined
除外。把 null
和 undefined
转换为 String
类型需要使用String()
方法。
String(null) // return "null"
String(undefind) // return "undefined"
还可以通过以下方式将其他类型转换为字符串:
let num = 11;
let numStr = "" + num; // "num"
3.6.3 模板字符串
可以使用反向单引号(``)是字符串自动换行,该方法特别适合写html模板。
let pageHTML = `
<div>
<a href="#">
<span>Jake</span>
</a>
</div>`;
3.6.4 插值
通过反向单引号还可以对字符串进行差插值,语法:${}
。
let value = 5;
let result = 10;
// 不使用插值
let str = value + '乘以2等于' + result;
// 使用插值
let str2 = `${value}乘以2等于${result}`;
插值中还可以调用函数:
function capitalize(word) {
return `${ word[0].toUpperCase() }${ word.slice(1) }`;
}
console.log(`${ capitalize('hello') }, ${ capitalize('world') }!`); // Hello, World!
插值可多次调用
let value = '';function append() {
value = `${value}abc`
console.log(value);
}
append(); // abc
append(); // abcabc
append(); // abcabcabc
更高级的用法(嵌套模板):
const class = `header ${isLargeScreen()? '' : `icon-${item.isCollapsed ? 'expander' : 'collapser'}`}`;
3.6.5 带标签的模板字符串
(没太看懂)
let a = 6;
let b = 9;
function simpleTag(strings, aValExpression, bValExpression, sumExpression) { console.log(strings);
console.log(aValExpression);
console.log(bValExpression);
console.log(sumExpression);
return 'foobar';
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
3.7 Symbol
ES6 中加入了 Symbol 数据类型,表示独一无二的值。该数据类型通常用来作为一个对象属性的键值
Symbol
是惟一的、不可变的。
3.7.1 基本使用
通过 Symbol()
函数进行实例化。
let sym = Symbol();
console.log(typeof sym); // symbol
3.8 Object
Object
是非特定数据和功能的组,通过 new
操作符进行创建。
每个 Object
实例都有如下属性和方法:
-
constructor
— 构造函数,用来创建一个Object
实例 -
hasOwnProperty(propertyName)
— 给定的属性是否存在(不能判断原型链里的属性),例:o.hasOwnProperty("name")
-
isPrototypeof(object)
— 判断该对象是否为另一个对象的原型。 -
propertyIsEnumerable(propertyName)
— 判断给定的属性是否可以用for-in
语句遍历。 -
toLocaleString()
— 返回特定格式的Stringconst date = new Date(); date.toLocaleString('zh'); // 2020/3/25 下午7:07:27 date.toLocaleString('en'); // 3/25/2020, 7:07:27 PM
-
toString()
— 将对象转为字符串 -
valueOf()
— 通常返回一个字符串
4. 运算符
4.1 ++a和a++
前者是先计算再使用
let age = 29;
let anotherAge = --age + 2;
console.log(age); // 28
console.log(anotherAge); // 30
后者是先试用再计算
let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2;
let num4 = num1 + num2;
console.log(num3); // 22
console.log(num4); // 21
4.2 幂运算
在ES7中,可以使用 **
运算符代替 Math.pow()
进行幂运算。
console.log(Math.pow(3, 2); // 9
console.log(3 ** 2); // 9
4.3 == 和 ===
==
会对两边的类型做自动转换,使两边的类型一致;===
不会对两边的类型做自动转换。
推荐使用 ===
let res1 = ("55" == 55); // true
let res2 = ("55" === 55); // false
let res3 = ("55" != 55); // false
let res4 = ("55" !== 55); // true
null == undefined; // true
null === undefined; // false
4.4 条件运算符
let max = (num1 > num2) ? num1 : num2;
5. 语句
5.1 循环
可以这样创建一个死循环:
for (;;) {
// do something
}
标签语句:给语句加一个标签,在 break
或 continue
时可以通过这个标签来引用该语句。标签通常在嵌套循环中使用。
outer:
for (let i = 0; i < 10; i++) {
inner:
for (let j = 0; j < 5; j ++) {
if (i === 2) {
break inner;
}
if ( i=== 4) {
break outer;
}
}
}
5.2 with 语句
with
语句可以给某个对象创建一个特定的使用范围。语法:with (expression) statement;
例如:
let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;
上述代码每一行都用到了 location
对象,可以采用 with
语句这样写:
with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
注意: 在严格模式下不能使用 with
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。