-
var
-
声明作用域
function test() { var message = 'hi' // 局部变量 console.log(message, 222); // 'hi' 222 } // console.log(message); // 出错 test(); console.log(message); // 出错
在函数内部用var定义的变量,在函数调用后随即被销毁。因此函数调用前后都会报错。
function test() { message = 'hi' // 局部变量 console.log(message, 222); // 'hi' 222 } // console.log(message); // 出错 test(); console.log(message); // 'hi'
去掉var操作符,在函数执行后,message就变成了全局变量,并且可以在外部访问。
-
var声明提升
使用var可以将关键字声明的变量会自动提升到函数作用域顶部,所以第一个打印不会报错。
function foo() { console.log(age, 111); var age = 26 var age = 36 console.log(age, 222); } foo() // undefined 111 // 36 222
-
-
Let
let和war最明显的区别:let声明的范围是块级作用域,而var声明的范围是函数作用域。
-
暂时性死区
let与var的另一个重要的区别:let不会在作用域中被提升。
// name 会被提升 console.log(name); // 'Matt' var name = 'Matt'; // age 不会被提升 console.log(age); // Reference:age没有定义 let age = 26;
在let声明之前的执行瞬间被称为暂时性死区(temporal dead zone),在此阶段引用后面才声明的变量都会抛出ReferenceError。
-
全局声明
与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性(var声明的变量则会)。
var name = 'Matt'; console.log(window.name); // 'Matt' let age = 26; console.log(window.age); // undefined
-
条件声明
不能使用let进行条件式声明。
-
for循环中的let声明
for (var i = 0; i < 5; ++i) { // 循环逻辑 } console.log(i); // 5 for (let j = 0; j < 5; ++j) { // 循环逻辑 } console.log(j); //ReferenceError:没有定义
var 会把for循环中迭代的变量渗透到循环外部,let的出现解决了这个问题。
for (var i = 0; i < 5; ++i) { setTimeout(() => { console.log(i) }, 0) } // 5 // 5 // 5 // 5 // 5 for (let j = 0; j < 5; ++j) { setTimeout(() => { console.log(j) }, 0) } // 0 // 1 // 2 // 3 // 4
使用let变量的时候,JS引擎会在后台为每个迭代循环声明一个新的迭代变量。var的话,迭代变量保存的是导致循环退出的值。因此最后输出的都是5。
-
-
const
const的行为与let基本相同,唯一一个重要的区别是用它声明变量是必须同时初始化变量,且const声明的变量不能更改(更改会导致运行时错误)。
-
const给常量赋值
const age = 26; age = 16; //TypeError:
-
const也不允许重复声明
const name = 'jack'; const name = 'lucy'; //SyntaxError
-
const声明的作用域也是块
const name = "matt" if (true) { const name = "jack" console.log(name); // 'jack' } console.log(name); //'matt'
-
const声明对象,这个对象里的属性也可以修改
const person = {} person.name = 'matt' person.age = '26' person.name = 'jack' person.age = '16' console.log(person.age, person.name); // 'jack 16'
-
js引擎会为for循环中的let声明分别创建独立的变量实例,虽然const变量和let变量相似,但不能用const来声明迭代变量。
-
const在for-in和for-of循环使用
for (const key in { a: 1, b: 2 }) { console.log(key); // a // b } for (const value of [1, 2, 3, 4, 5]) { console.log(value); // 1 // 2 // 3 // 4 // 5 }
-
es有七种简单数据类型(原始类型):Undefined、Null、Boolean、Number、String、Symbol和BigInt。
-
typeof操作符
-
typeof null返回的是'object'
console.log(typeof null); // 'object' console.log(typeof (null)); // 'object'
-
typeof可以用来区分函数和其他对象
function fn() {} console.log(typeof fn); // 'function' console.log(typeof new Date()); // 'object' console.log(typeof new RegExp()); // 'object' console.log(typeof {}); // 'object'
-
-
Undefined类型
未初始化的变量会被自动赋予undefined的值,未声明的变量typeof时也是undefined。因此,建议声明变量的时候同时进行初始化。
let name; console.log(name); // 未初始化,值为undefined console.log(typeof name); // 初始化了的变量,typeof类型为undefined console.log(typeof age); // 未声明的变量,typeof类型为undefined console.log(age); //报错
-
Null类型
逻辑上讲,null值表示一个空对象指针,因此typeof null会返回'object'。
undefined的值是由null值派生而来的,因此他们表面上相等。
console.log(undefined == null); // true console.log(undefined === null); // false
-
Boolean类型
有两个字面值:true和false。
不同类型和Boolean的转换。
// Boolean console.log(Boolean(true)); // true console.log(Boolean(false)); // false // String console.log(Boolean('12')); // 非空字符串 true console.log(Boolean('')); // false // Number console.log(Boolean(12)); // 非零数值包括无穷大 true console.log(Boolean(-Infinity)); // true console.log(Boolean(Infinity)); // true console.log(Boolean(NaN)); // false console.log(Boolean(0)); // false // Object console.log(Boolean({})); // true console.log(Boolean({ a: 1 })); // true console.log(Boolean(null)); // false // Undefined console.log(Boolean(undefined)); // false
-
Number类型
有进制,也有+0和-0;
-
浮点值
必须要有小数点,且小数点后必须至少有一个数字。
浮点值的精确度最高可达17位小数。永远不要测试某个特定的浮点值。
console.log(0.1 + 0.2); // 0.30000000000000004
-
值的范围
最小的数值可以保存在Number.MIN_VALUE,最大的数值可以保存在Number.MAX_VALUE;任何无法表示的负数以-Infinity表示,任何无法表示的正数以Infinity表示。
使用Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY也可以获取正、负Infinity
-
NaN
意思是“不是数值”,用于表示本来要返回数值的操作失败了(而不是抛出错误)。
0,+0,-0相除会返回NaN
console.log(0 / 0); // NaN console.log(-0 / +0); // NaN
如果分子是非0值,分母是有符号的0或者无符号的0,会返回Infinity和-Infinity。
console.log(5 / 0); // Infinity console.log(6 / -0); // -Infinity
任何涉及NaN的操作始终返回NaN
console.log(NaN + 10); // NaN console.log(NaN + NaN); // NaN console.log(NaN / 10); // NaN console.log(NaN / 0); // NaN
NaN不等于包括NaN在内的任何值
console.log(NaN == NaN); // false console.log(NaN == 0); // false console.log(NaN == 1); // false
isNaN函数,判断这个参数是否“不是数值”,可以转换为数值的返回false,否则true。
console.log(isNaN(NaN)); // true console.log(isNaN(10)); // false console.log(isNaN('10')); // false console.log(isNaN('BLUE')); // true console.log(isNaN(true)); // false console.log(isNaN(false)); // false
-
数值转换
有三个函数可以将非数值转换为数值:number()、parseInt()和parseFloat()。
// Boolean => 0/1 console.log(Number(true)); // 1 console.log(Number(false)); // 0 // Number => 直接返回 console.log(Number(12)); // 12 // Null => 0 console.log(Number(null)); // 0 // Undefined => NaN console.log(Number(undefined)); // NaN // 数值型String console.log(Number('123')); // 123 console.log(Number('011')); // 11 console.log(Number('e11')); // NaN console.log(Number('abc11')); // NaN // 浮点值格式的String console.log(Number('1.1')); // 1.1 console.log(Number('0.1')); // 0.1 // 十六进制的String console.log(Number('0xf')); // 15 // 空字符串 console.log(Number('')); // 0 // 其他字符串 => NaN console.log(Number('abc')); //NaN
考虑到Number转字符串有点反常规,通常在需要得到整数时,可以优先使用parseInt()函数。
console.log(parseInt(' 123blue ')); // 123 console.log(parseInt(' 123blue 44')); // 123 console.log(parseInt('')); // NaN console.log(parseInt('0xA')); // 10 console.log(parseInt('22.5')); // 22 console.log(parseInt('70')); // 70 console.log(parseInt('0xf')); // 15
parseInt可以传两个参数,第二个参数可以用于指定底数(进制数)。
console.log(parseInt('10', 2)); // 2 console.log(parseInt('10', 8)); // 8 console.log(parseInt('10', 10)); // 10 console.log(parseInt('10', 16)); // 16
parseFloat也是解析到字符串末尾,或者解析到一个无效浮点数值字符为止。
console.log(parseFloat(' 1234blue ')); // 1234 console.log(parseFloat('0xA')); // 0 十六进制始终返回0 console.log(parseFloat('22.5')); // 22.5 只解析十进制不能指定指数 console.log(parseFloat('22.34.5')); // 22.34 console.log(parseFloat('0908.5')); // 908.5 会忽略字符串开头的0 console.log(parseFloat('3.125e7')); // 31250000 console.log(parseFloat('3.0')); // 3 如果小数点后没有小数点或者只有零返回整数 console.log(parseFloat('3.00')); // 3
-
-
String类型
-
转换为字符串
几乎所有的值都有toString()方法;null和undefined值没有toString()方法;
const age = 11 console.log(age.toString()); // '11' const found = true console.log(found.toString()); // 'true'
toString()接收进制基数。
const age = 10 console.log(age.toString()); // '10' console.log(age.toString(2)); // '1010' console.log(age.toString(8)); // '12' console.log(age.toString(10)); // '10' console.log(age.toString(16)); // 'a'
String()会把始终返回表示相应类型值的字符串
let value1 = 10 let value2 = true let value3 = null let value4 = undefined console.log(String(value1)); // '10' console.log(String(value2)); // 'true' console.log(String(value3)); // 'null' console.log(String(value4)); // 'undefined'
-
模板字面量
可以跨行定义字符串
let str = `123 234`
可以插值
let age = 25 let str = `age:${age}`
-
-
Symbol类型
符号是原始值,且符号实例是唯一、不可变的 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而用做非字符串形式的对象属性。
-
基本用法
let sym = Symbol() console.log(typeof sym); // symbol
传入的字符参数与符号定义或标识完全无关。
let sym = Symbol() let sym1 = Symbol() console.log(sym == sym1); // false let symFoo = Symbol('foo') let symFoo1 = Symbol('foo') console.log(symFoo == symFoo1); // false
Symbol()函数不能与new关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象。
-
使用全局符号注册表
Symbol.for()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,他会检查全局运行时注册表,发现不存在对应对应的符号,于是就会生成一个新的符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串相应的符号,然后就会返回该符号实例。
let oneSym = Symbol.for('foo') let oneSymSame = Symbol.for('foo') console.log(oneSym === oneSymSame); // true
在全局注册表中定义的符号跟使用Symbol()定义的符号并不等同。
let oneSym = Symbol('foo') let oneSymSame = Symbol.for('foo') console.log(oneSym == oneSymSame); // false
创建全局符号
let s = Symbol.for('foo'); console.log(Symbol.keyFor(s)); // 'foo'
创建普通符号
let s = Symbol('bar'); console.log(Symbol.keyFor(s)); // undefined
Symbol.keyFor传空会报错。
-
使用符号作为属性
凡是可以使用字符串或数值作为属性的地方,都可以使用符号。
let s1 = Symbol('foo'), s2 = Symbol('bar'), s3 = Symbol('baz'), s4 = Symbol('qux'); 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' } }) console.log( o); //{Symbol(foo): 'foo val', Symbol(bar): 'bar val', Symbol(baz): 'baz val', Symbol(qux): 'qux val'}
Object.getOwnPropertyNames()返回对象实例的常规属性数组;
Object.getOwnPropertySymbols()返回对象实例的符号属性数组;
Object.getOwnPropertyDescriptors返回同时包含常规和符号属性描述的对象;
Reflect.ownKeys()会返回两种类型的键。
let s1 = Symbol('foo'), s2 = Symbol('bar'); let o = { [s1]: 'foo val', [s2]: 'bar val', baz: 'baz val', qux: 'qux val' } console.log(Object.getOwnPropertySymbols(o)); // [Symbol(foo), Symbol(bar)] console.log(Object.getOwnPropertyNames(o)); // ['baz', 'qux'] console.log(Object.getOwnPropertyDescriptors(o)); // {baz: {…}, qux: {…}, Symbol(foo): {…}, Symbol(bar): {…}} console.log(Reflect.ownKeys(o)); // ['baz', 'qux', Symbol(foo), Symbol(bar)]
-
-
Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。对象通过new操作符后跟对象的类型的名称来创建。
let o = new Object()
每个Object实例都有如下属性和方法:
- constructor:用于创建当前对象的函数。这个属性的值就是Object()函数。
- hasOwnProperty(propertyName):用于判断当前对象是否存在给定的属性。也能检查符号(Symbol)。
- isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
- propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。
- toLocaleString():返回对象的字符串表示。
- toString():返回对象的字符串表示。
- valueOf():返回对应字符串、数值、布尔值表示。通常与toString()的返回值相同。