JavaScript语言基础
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); //undefined var 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 age console.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; // 等于31250000 let 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相除 会返回NaN
console.log(0/0); // NaN console.log(-0/+0); // NaN console.log(5/0); // Infinity console.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"); //1234 let num2 = parseInt("");// NaN let 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); // 175 let num5 = parseInt("AF");// NaN
- parseFloat() 函数
只能解析十进制
let num1 = parseFloat("1234blue"); //1234,按整数解析 let num2 = parseFloat("0xA");// 0 let num3 = parseFloat("22.5"); //22.5 let 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); //symbol let 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); // true console.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 //也可使用 instanceof console.log(f instanceof Foo); // true
- Symbol.isConcatSpreadable
这个符号作为一个 属性表示“一个布尔值,如果是true,则意味着对象应该用 Array.prototype.concat()打平其数组元素”
(只对类数组对象有效,其他对象设置会导致忽略连接)
let initial = ['foo']; let array = ['bar']; console.log(array[Symbol.isConcatSpreadable]); //undefined console.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]);//undefined console.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]);//undefined console.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)); //9 console.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: 增加更多逻辑