JavaScript语言基础
1.语法
1.1 区分大小写
变量test和变量Test是不同的变量。typeof不能作为函数名(关键字),但Typeof合法
1.2 标识符
变量、函数、属性、函数参数的名称
- 第一个字符必须是字母、下划线(_)、或者美元符号($)
- 其他字符可以是字母、下划线、美元符号或数字
1.3严格模式
ES3不规范的写法会被处理,对于不安全的活动将抛出错误
对整个脚本启用严格模式
\"use strict\";
单独一个函数在严格模式下执行
function doSomething(){\"use strict\";//函数体}
2.变量
js的变量是松散类型的,var、const和let都可以声明变量
2.1 var关键字
var message;
如果声明的变量未初始化变量为undefined
- var 定义的变量为函数作用域
但如果在函数中定义变量时省略了var操作符会创建一个全局变量function test(){message = \"hi\";}test();console.log(message); //\"hi\"
- 可以通知声明多个变量
var message = \"hi\",found = fales,age = 29;
- var 声明提升
所谓的“提升” 就是将声明放到函数作用域顶部console.log(age); //undefinedvar age = 26;//与下方代码等价var age;console.log(age);var age = 26;
2.2 let声明
- let 声明的范围为块作用域
- let 不允许冗余声明
- let 声明的变量不会提升
- 全局声明不会成为window对象属性
注:关于for循环
for循环中var的问题
// 迭代遍历渗透到循环体外部for(var i=0; i<5; i++){//循环体}console.log(i); //5//定时器问题for(var i=0; i<5; i++){setTimeout(()=>console.log(i),0);}//实际输出 5、5、5、5、5
如果使用let声明迭代变量,每次循环会成名一个新的迭代变量
2.3 const声明
const与let基本相同 唯一的区别时声明变量时必须初始化,且修改会报错
注:如果修改对象中的属性则不受限制
for-in 与 for-of使用 const
for(const key in {a:1, b:2}){console.log(key);}// a,b//for-in为迭代对象的键for(const value of [1,2,3,4,5]){console.log(value);}// 1, 2, 3, 4, 5//for-of迭代实现iterator的对象
3.数据类型
ECMAScript有6种简单数据类型(也称为原始类 型):Undefined、Null、Boolean、Number、 String和Symbol。Symbol(符号)是 ECMAScript 6新增的。还有一种复杂数据类型叫 Object(对象)。
3.1 typeof操作符
因为ECMAScript的类型系统是松散的,所以需要一 种手段来确定任意变量的数据类型。
- \”object\”表示值为对象(而不是函数)或 null;
- \”function\”表示值为函数;
console.log(typeof 95); // \"number\"console.log(typeof(\"message\")); // \"string\"
注:typeof是一个操作符而不是函数,但可以使用参数。
3.2 undefined类型
变量声明未赋值,值为undefined
let message;// let message = undefined; 等价console.log(message == undefined); // true
对未声明的变量使用typeof 也为 \”undefined\”
// 确保没有声明过这个变量// let ageconsole.log(typeof age); //\"undefined\"
3.3 Null类型
在定义将来要保存对象值的变量时,建议使用null 来初始化
注:undefined值是由null值派生而来的,因此 ECMA-262将它们定义为表面上相等
console.log(null == undefined); //true
3.4 Boolean类型
布尔值字面量true和false是区分大小写的
任何类型的值都可以调用Boolean()函数转化为Boolean类型
数值类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | false |
String | 非空字符串 | \”\”(空字符串) |
Number | 非零数值(包括无穷值) | 0、NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
注:像if等流控制语句会 自动执行其他类型值到布尔值的转换
3.5 Number类型
- 八进制字面量
第一 个数字必须是零(0),然后是相应的八进制数字 (数值0~7)
严格模式下,前缀0会被视为语法错误,如果要表示八进制值,应该使用前缀0o -
浮点数
let floatNum1 = 0.1;let floatNum2 = .1; // 有效,但不推荐let floatNum3 = 1.; // 小数点后面没有数字,当成整数1处理let floatNum4 = 10.0; // 小数点后面是零,当成整数10处理let floatNum5 = 3.125e7; // 等于31250000let floatNum6 = 0.0000003; // 等于3e-7//ECMAScript会将小数点后至少包含6个零的浮点值转换为科学记数法let floatNum7 = 0.1+0.2; // 等于0.300000000000004 存在舍入错误
- 值的范围
ECMAScript可以表示的最小数值为 Number.MIN_VALUE,可以表示的最大数值为Number.MAX_VALUE
超过范围的值会转化成为Infinity 或 -Infinity ,可以使用 isFinite() 判断一个数是否在范围内 - NaN(Not a Number)
在ECMAScript中,0、+0或-0相除 会返回NaNconsole.log(0/0); // NaNconsole.log(-0/+0); // NaNconsole.log(5/0); // Infinityconsole.log(5/-0); // - Infinity
任何涉及 NaN的操作始终返回NaN
console.log(NaN == NaN); //false// NaN与任何值不相等
ECMAScript提供了isNaN()函数,一个值传给isNaN()后,该函数会尝试把它转换为数值,任何不能转换为数值的值都会导致这个函数返回true。
- Number() 函数
类型 结果 boolean true转为1,false转为0 数值 直接返回 null 0 undefined NaN 字符串 1. 完全符合数字格式返回该数字
2. 空字符串返回0
3. 其他情形返回NaN对象 先调用valueOf()然后按上述方法转换,如果为NaN
则再调用toString() 方法,再按上述方法转换 - parseInt()函数
第一个参数 接收一个字符串let num1 = parseInt(\"1234blue\"); //1234let num2 = parseInt(\"\");// NaNlet num3 = parseInt(\"0xA\");// 10,解释为十六进制整数let num4 = parseInt(22.5);// 22
第二个参数 代表进制
let num1 = parseInt(\"10\", 2);// 2,按二进制解析let num2 = parseInt(\"10\", 8);// 8,按八进制解析let num3 = parseInt(\"10\",10); // 10,按十进制解析let num4 = parseInt(\"AF\",16); // 175let num5 = parseInt(\"AF\");// NaN
- parseFloat() 函数
只能解析十进制let num1 = parseFloat(\"1234blue\"); //1234,按整数解析let num2 = parseFloat(\"0xA\");// 0let num3 = parseFloat(\"22.5\"); //22.5let num4 = parseFloat(\"22.34.5\"); //22.34
3.6 String类型
String(字符串)数据类型表示零或多个16位 Unicode字符序列。字符串可以使用双引号(\”)、 单引号(\’)或反引号(`)标示
- 特殊字面量
\\xnn 以十六进制编码nn表示的字符 (其中n是十六进制数字 0~F),
例如\\x41等于\”A\”\\unnnn 以十六进制编码nnnn表示的 Unicode字符(其中n是十六 进制数字0~F),
例如 \\u03a3等于希腊字符\”Σ\” - toString() 函数
toString()方法可见于数值、布尔值、 对象和字符串值。(字符串值也有 toString()方法,返回自身的一个副本)
null和 undefined值没有toString()方法 (使用会报错)注:在对数值调用这个方法时, toString()可以接收一个底数参数,即 以什么进制来输出数值的字符串表示。
let num = 10;console.log(num.toString());// \"10\"console.log(num.toString(2));// \"1010\"console.log(num.toString(8));// \"12\"console.log(num.toString(10)); // \"10\"console.log(num.toString(16)); // \"a\"
- String() 函数(接收任意类型参数)
值有toSring()方法 调用并返回结果 null 返回 \”null\” undefined \”undefined\” - 模板字面量
① 模板字面量保留换行字符,可以跨行 定义字符串
② 从技术上讲模板字面量不是字符串,而是一种特殊的JavaScript 句法表达式,只不过求值后得到的是字符串
可以通过在${}中使用一个 JavaScript表达式实现字符串插值
let value = 5;let exponent = \'second\';let interpolatedTemplateLiteral =`${ value } to the ${exponent } power is ${ value * value }`;console.log(interpolatedTemplateLiteral); // 5 to the second power is 25
插入的值都会使用toString()强制转型为字符串(实际使用的是String() 函数)
③字面量也支持特有的标签函数 以及String.raw可用于查看原始字面量内容 此处略
3.7 Symbol() 类型
- 符号需要使用Symbol()函数初始化。
let sym = Symbol();console.log(typeof sym); //symbollet fooSymbol = Symbol(\'foo\');let otherFooSymbol = Symbol(\'foo\');console.log(fooSymbol == otherFooSymbol); //false
- Symbol()函数不能用作构造函数,与new关键字一起使用
- Symbol.for() 全局符号注册表
如果程序中需要共享和重用符号实例,那么可以用一个字符串作为键, 在全局符号注册表中创建并重用符号let fooGlobalSymbol = Symbol.for(\'foo\'); //创建新符号let otherFooGlobalSymbol = Symbol.for(\'foo\'); // 重用已有符号let localSymbol = Symbol(\'foo\');console.log(fooGlobalSymbol === otherFooGlobalSymbol); // trueconsole.log(localSymbol === fooGlobalSymbol); // false
- Symbol.keyFor() 可用来查询全局注册表
// 创建全局符号let s = Symbol.for(\'foo\');console.log(Symbol.keyFor(s)); // foo// 创建普通符号let s2 = Symbol(\'bar\');console.log(Symbol.keyFor(s2)); // undefined
let s1 = Symbol(\'foo\'),s2 = Symbol(\'bar\');let o = {[s1]: \'foo val\'};// 这样也可以:o[s1] = \'foo val\';console.log(o);// {Symbol(foo): foo val}Object.defineProperty(o, s2, {value: \'bar val\'});console.log(o);// {Symbol(foo): foo val, Symbol(bar): bar val}Object.defineProperties(o, {[s3]: {value: \'baz val\'},[s4]: {value: \'qux val\'}});
注:几个查询属性的函数
// 以下面对象为例子
let s1 = Symbol(\'foo\'),s2 = Symbol(\'bar\');let o = {[s1]: \'foo val\',[s2]: \'bar val\',baz: \'baz val\',qux: \'qux val\'};
函数名 | 返回值 |
---|---|
Object.getOwnPropertySymbols(o) | [Symbol(foo), Symbol(bar)] |
Object.getOwnPropertyNames(o) | [\”baz\” , \”qux\”] |
Object.getOwnPropertyDescriptors(o) | {baz: {…}, qux: {…}, Symbol(foo): {…}, Symbol(bar): {…}} |
Reflect.ownKeys(o) | [\”baz\” , \”qux\” , Symbol(foo), Symbol(bar)] |
ECMAScript 6也引入了一批常用内置符号 (well-known symbol),用于暴露语言 内部行为,开发者可以直接访问、重写或 模拟这些行为。
- Symbol.asyncIterator
这个符号作为一个 属性表示“一个方法,该方法返回对象默 认的AsyncIterator。由for-await-of语句使用”。
for-await-of循环会利用这个函数执行 异步迭代操作。循环时,它们会调用以 Symbol.asyncIterator为键的函数, 并期望这个函数会返回一个实现迭代器API 的对象。技术上,这个由 Symbol.asyncIterator函数生成的对 象应该通过其next()方法陆续返回 Promise实例
class Emitter {constructor(max) {this.max = max;this.asyncIdx = 0;}async * [Symbol.asyncIterator]() {while(this.asyncIdx < this.max) {yield new Promise((resolve) =>resolve(this.asyncIdx++));}}}// 用于调用异步迭代器的异步函数async function asyncCount() {let emitter = new Emitter(5);for await(const x of emitter) {console.log(x);}}asyncCount();// 0// 1// 2// 3// 4
- Symbol.hasInstance
这个符号作为一个 属性表示“一个方法,该方法决定一个构造器对象(Class)是否认可一个对象是它的实例。
这个属性定义在Function的原型上,因此默认在所有函数和类上都可以调用。function Foo() {}let f = new Foo();console.log(Foo[Symbol.hasInstance](f)); // true//也可使用 instanceofconsole.log(f instanceof Foo); // true
- Symbol.isConcatSpreadable
这个符号作为一个 属性表示“一个布尔值,如果是true,则意味着对象应该用 Array.prototype.concat()打平其数组元素”
(只对类数组对象有效,其他对象设置会导致忽略连接)let initial = [\'foo\'];let array = [\'bar\'];console.log(array[Symbol.isConcatSpreadable]); //undefinedconsole.log(initial.concat(array)); // [\'foo\', \'bar\'] 数组对象本身默认打平array[Symbol.isConcatSpreadable] = false;console.log(initial.concat(array)); // [\'foo\',Array(1)] 改变默认行为let arrayLikeObject = {length: 1, 0: \'baz\' };console.log(arrayLikeObject[Symbol.isConcatSpreadable]);//undefinedconsole.log(initial.concat(arrayLikeObject));// [\'foo\', {...}]arrayLikeObject[Symbol.isConcatSpreadable] = true;console.log(initial.concat(arrayLikeObject));// [\'foo\',\'baz\'] 类数组对象true有效let otherObject = new Set().add(\'qux\');console.log(otherObject[Symbol.isConcatSpreadable]);//undefinedconsole.log(initial.concat(otherObject)); //[\'foo\', Set(1)]otherObject[Symbol.isConcatSpreadable] = true;console.log(initial.concat(otherObject)); //[\'foo\'] 非类数组导致忽略
- Symbol.iterator
这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器。由for-of语句使用”。
for-of循环这样的语言结构会利用这个函数执行迭代操作。循环时,它们会调用以 Symbol.iterator为键的函数,并默认这个函数会返回一个实现迭代器API的对象。
技术上,这个由Symbol.iterator函数 生成的对象应该通过其next()方法陆续返回值。可以通过显式地调用next()方法返回,也可以隐式地通过生成器函数返回class Emitter {constructor(max) {this.max = max;this.idx = 0;}*[Symbol.iterator]() {while(this.idx < this.max) {yield this.idx++;}}}function count() {et emitter = newEmitter(5);for (const x of emitter) {console.log(x);}}count();// 0// 1// 2// 3// 4
- 还有一些内置符号这里先省略 需要再补充
3.8 Object类型
对象其实就是一组数据和功能的集合
let o = new Object();let o = new Object; // 合法,但不推荐
每个Object实例都拥有的属性和方法(方法都在Object的原型上)
属性/方法 | 说明 |
---|---|
constructor | 值为用于创建当前对象的函数。 |
hasOwnProperty(propertyName) | 用于判断当前对象实例(不是原型)上是否存在给定的属性。 要检查的属性名必须是字符串或符号。 |
isPrototypeOf(object) | 用于判断当前对象是否为另一个对象的原型 例:Foo.prototype.isPrototypeOf(foo) //true |
propertyIsEnumerable(propertyName) | 用于判断给定的属性是否可以使用for-in语句枚举 |
toLocaleString() | 返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。 |
toString() | 返回对象的字符串表示。 |
valueOf() | 返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同。 |
注意:浏览器环境中的BOM和DOM对象都是由宿主环境定义和提供的宿主对象。 而宿主对象不受ECMA-262约束,所以它们可能会也可 能不会继承Object。
4.操作符
4.1 一元操作符
- 自增/自减操作符 ++,–
可以作用于任何值,整数、字符串、布尔值、浮点值, 甚至对象都可以。
先对操作的值执行Number() 函数 再进行操作 - 一元加减操作符 (正负号)
可以作用于任何值,先对操作的值执行Number() 函数再进行操作
4.2 位运算
此处略
4.3 布尔操作符
- 逻辑非 ( ! )
始终返回布尔值,可以作用于任何值。
具体操作为先对值执行Boolean() 然后取反 注:也可以使用 (! !) 两个叹号效果和Boolean() 相同 - 逻辑与 ( && ) 短路
可以操作任何类型,如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值。
它会先对第一个操作数执行Boolean() 如果值为false,则返回第一个操作数。如果为true,直接返回第二个 - 逻辑或 ( | | ) 短路
可以操作任何类型,如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值。
它会先对第一个操作数执行Boolean() 如果值为false,则返回第二个操作数。如果是true,直接返回第一个注:可以通过或运算符设置备用值
// backupObject 为备用值// 当preferredObject为undefined或null时使用let myObject = preferredObject || backupObject;
4.4 乘性操作符
- 乘法运算符 ( * )
如果有不是数值的操作数 先应用Number() 转换为数值
下面列举一些特殊行为
特殊情形 返回值 乘积超过范围 Infinity或-Infinity 任意操作数为NaN NaN Infinity乘以0 NaN Infinity乘以非0的有 限数值 Infinity或-Infinity Infinity乘以(正负)Infinity Infinity或-Infinity - 除法运算符 ( / )
如果有不是数值的操作数 先应用Number() 转换为数值
下面列举一些特殊行为
特殊情形 返回值 商超过范围 Infinity或-Infinity 任意操作数为NaN NaN Infinity除以Infinity NaN 0除以0 NaN 非0常数除以0 Infinity或-Infinity Infinity除以任何数值 Infinity或-Infinity - 取模运算符 ( % )
如果有不是数值的操作数 先应用Number() 转换为数值 (小数也可以求余数 例:14.22%3=2.2200000000000006)
下面列举一些特殊行为
特殊情形 返回值 Infinity除以有限值 NaN 有限值除以0 NaN Infinity除以Infinity NaN 有限值除以Infinity 有限值(被除数) 0除以非0 0
4.5 指数操作符 ( ** )
Math.pow()现 在有了自己的操作符**
console.log(Math.pow(3, 2)); //9console.log(3 ** 2); //9
4.6 加性运算符
- 加法操作符
如果有一个是字符串则执行连接字符串操作 其他类型执行String()
如果没有字符串则执行数字相加 其他类型执行Number() - 减法操作符
期待操作数都是数字,除了对象的其他类型会执行Number() 转换为数字。
如果是对象,有valueOf() 就只调用valueOf(),没有就调用toString() 再调用Number()
4.7 关系操作符
包括小于 (<)、大于(>)、小于等于(<=)和大于等于 (>=)
如果都是字符串则比较字符编码
其他情况——期待操作数都是数字,除了对象的其他类型会执行Number() 转换为数字。
如果是对象,有valueOf() 就只调用valueOf(),没有就调用toString() 再调用Number()
4.8 相等操作符
等于、不等于和全等、不全等
- 等于和不等于 ( == )&( != )
布尔值都转换为数字,数字和字符串比较 字符串转换为数字,对象调用valueOf()
null == undefined
null和undefined不能转换为 其他类型的值再进行比较 - 全等不全等
不转换类型(相当于问两个操作数有没有区别)
4.9 条件操作符 (?… : …)
a?b:c
条件(a)为真,表达式值为b,否则为c
4.10 赋值表达式 (=) 略
4.11 逗号表达式 ( , )
用来在一条语句中执行多个操作,值为表达式的最后一个值
5.语句
5.1 标签语句
标签语句用于给语句加标签,可以在后面通过 break或continue语句引用。
let num = 0;outermost:for (let i = 0; i < 10; i++) {for (let j = 0; j < 10; j++) {if (i == 5 && j == 5) {break outermost;}num++;}}console.log(num); // 55
5.2 with语句
with语句的用途是将代码作用域设置为特定的对象
let qs = location.search.substring(1);let hostName = location.hostname;let url = location.href;// 下方等价with(location) {let qs = search.substring(1);let hostName = hostname;let url = href;}
注:由于with语句影响性能且难于调试其中的代 码,通常不推荐在产品代码中使用with语句。
5.3 switch语句
可以 使用 switch(true) 然后使用类似 case num>0: 增加更多逻辑