typeof null === 'object'; // true
// 判断是否为null的合理方法
let a = null;
(!a && typeof a === 'object'); // true
变量在未持有值的时候为
undefined
,此时tyoeof
返回字符串格式的"undefined"
类型 | 描述 |
---|---|
undefined | 已在作用域中声明但还没有赋值的变量,是 undefined 的 |
undeclared | 还没有在作用域中声明过的变量,是 undeclared 的、是 is not defined 的 |
// 例如:
var a;
console.log(a); // undefined
console.log(b); // ReferenceError: b is not defined
// 例如:
var a;
console.log(typeof a); // "undefined"
console.log(typeof b); // "undefined"
undefined
并不等于 b is not defined
b is not found
或者 “b is not declared
才更加准确a``b
变量使用typeof
均返回"undefined"
,这是因为typeof
有一个特殊的安全防范机制,此时typeof
如果能返回 undeclared
(而非 undefined
)的话,情况会好很多typeof
的安全防范机制可用于检查变量是否被定义了if(typeof DEBUG !== "undefined") console.log('Hello');
ReferenceError
错误,另一种判断就可以是:if(window.DEBUG) console.log('Hello');
delete
运算符可以将单元从数组中删除,删除后数组的length
并不会改变var list = [1, 2, 3];
delete list[1];
console.log(list); // (3) [0: 1, 2: 3, length: 3]
list[1]
等于undefined
,但与list[1] = undefined
这种赋值的方式还是存在区别的
永远不要创建和使用空单元数组
// 以下数组存在空单元(不带有索引1和值)
var list = [1, 2, 3];
delete list[1];
console.log(list); // (3) [0: 1, 2: 3, length: 3]
// 以下数组不存在空单元(带有索引1和值)
var list = [1, 2, 3];
list[1] = undefined;
console.log(list); // (3) [0: 1, 1: undefined, 2: 3, length: 3]
字符串类型的key
,但这些并不计算在数组长度内(不建议,存放键值建议使用对象)var list = [1, 2, 3];
list.name = '123';
console.log(list); // (3) [1, 2, 3, name: '123']
console.log(list.length); // 3
key能被强制转换成十进制数字
时,会被当做数字索引值
处理var list = [1, 2, 3];
list['5'] = 5;
console.log(list); // (6) [0: 1, 1: 2, 2: 3, 5: 5, length: 6]
console.log(list.length); // 6
function foo(){
// 类数组
console.log(arguments); // Arguments(2) ['foo', 'bar', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 类数组转换为数组,方式一:
console.log(Array.prototype.slice.call(arguments)); // ['foo', 'bar']
// 类数组转换为数组,方式二:
console.log(Array.from(arguments)); // ['foo', 'bar']
}
foo('foo', 'bar');
字符串
不是字符数组
,字符串无法借用数组的一些方法
var a = 0.12;
var a = .12;
var b = 0.10;
var b = 0.1;
var c = 1.0;
var c = 1.;
// 指数显示法
var d = 5E8;
d; // 500000000
d.toExponential(); // "5e+8"
// toPrecision 用来指定有效数位的显示位数
var e = 42.59;
e.toPrecision(1); // "4e+1"
e.toPrecision(2); // "43"
e.toPrecision(3); // "42.6"
e.toPrecision(4); // "42.59"
e.toPrecision(5); // "42.590"
e.toPrecision(6); // "42.5900"
// tofixed 方法可指定小数部分的显示位数
42.toFixed(2); // SyntaxError: Invalid or unexpected token
(42).toFixed(2); // "42.00"
42 .toFixed(2); // "42.00"
42..toFixed(2); // "42.00"
判断两个数相等
0.1 + 0.2 === 0.3; // false
// 兼容写法
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
// 绝对值相减是否小于精度引发的偏差值
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
numbersCloseEnoughToEqual(0.1 + 0.2, 0.3); // true
numbersCloseEnoughToEqual(0.1E-10, 0.2E-10); // false
numbersCloseEnoughToEqual(0.1E-1000, 0.2E-1000); // true 当数字相差过小还是会出现精度问题
数字的呈现方式决定了“整数”的安全值范围远远小于
Number.MAX_VALUE
Number.MAX_VALUE; // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MIN_SAFE_INTEGER; // -9007199254740991
Number.isInteger(42); // true
Number.isInteger(42.000); // true
Number.isInteger(42.3); // false
// 兼容写法
if (!Number.isInteger) {
Number.isInteger = function(num) {
return typeof num == "number" && num % 1 == 0;
};
}
Number.isSafeInteger(Number.MAX_SAFE_INTEGER); // true
Number.isSafeInteger(Math.pow(2, 53)); // false
Number.isSafeInteger(Math.pow(2, 53) - 1); // true
// 兼容写法
if (!Number.isSafeInteger) {
Number.isSafeInteger = function(num) {
return Number.isInteger(num) && Math.abs(num) <= Number.MAX_SAFE_INTEGER;
};
}
undefined 和 null 常被用来表示“空的”值或“不是值”的值。二者之间有一些细微的差别
值 | 描述 |
---|---|
undefined | undefined 指没有值(missing value); undefined 指从未赋值; |
null | null 指空值(empty value); null 指曾赋过值,但是目前没有值; |
void 1; // undefined
void undefined; // undefined
NaN
NaN === NaN; // false
typeof NaN; // 'number'
isNaN(NaN); // true
无穷数
1 / 0; // Infinity
-1 / 0; // -Infinity
1 / 0 === Infinity; // true
-1 / 0 === -Infinity; // true
Number.MAX_VALUE + 1E1000; // Infinity
零值
0 / -3; // -0
0 * -3; // -0
-0 === 0; // true
-0
字符串后变为"0"
var a = 0 / -3;
a; // -0
a.toString(); // '0'
a + ''; // '0'
JSON.stringify(a); // '0'
"-0"
将字符串转换为数字后变为-0
+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0
-0
(比如用于判断动画方向需要使用0的符号位
)function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
isNegZero( 0 / -3 ); // true
isNegZero( 0 ); // false
判断两个值是否绝对相等
Object.is(null, undefined); // false
Object.is(-0, 0); // false
Object.is(-0, -0); // true
Object.is(NaN, NaN); // true
Object.is(NaN, 0 / 'str'); // true
new String('abc')
和 'abc'
和 String('abc')
// new String('abc')
let str1 = new String('abc');
// String {0: "a", 1: "b", 2: "c", length: 3, [[Prototype]]: String, [[PrimitiveValue]]: "abc"}
str1;
str1.toString(); // 'abc'
str1.length; // 3
typeof str1; // 'object'
str1 instanceof String; // true
Object.prototype.toString.call(str1); // [object String]
// 'abc'
let str2 = 'abc';
str2; // abc
str2.toString(); // 'abc'
str2.length; // 3
typeof str2; // 'string'
str2 instanceof String; // false
Object.prototype.toString.call(str2); // '[object String]'
// String('abc')
let str3 = String('abc');
str3; // abc
str3.toString(); // 'abc'
str3.length; // 3
typeof str3; // 'string'
str3 instanceof String; // false
Object.prototype.toString.call(str3); // '[object String]'
str1 === str2; // false
str1 == str2; // true
str1 === str3; // false
str1 == str3; // true
str2 === str3; // true
str2 == str3; // true
new String("abc")
创建的是字符串 "abc"
的封装对象,而非基本类型值 "abc"
'abc'
和 String('abc')
一致[[CLass]]
认为每个对象的内部都包含一个内部属性
[[CLass]]
,这个属性无法直接进行访问需要通过Object.prototype.toString.call(..)
这种方式来查看
// 示例:
let arr = new Array(1, 2, 3);
Object.prototype.toString.call(arr); // '[object Array]'
由于基本类型值没有
.length
和.toString()
这样的属性和方法,需要通过封装对象才能访问,此时 JavaScript 会自动为基本类型值包装(box 或者 wrap)一个封装对象
一般情况下,我们不需要直接使用封装对象。最好的办法是让 JavaScript 引擎自己决定什么时候应该使用封装对象
// 示例:
let str = 'abc';
str.length; // 3
Boolean
let a = new Boolean(false);
if(!a) console.log('abc');
else console.log('Lee');
Lee
,并不返回 abc
, 因为对象返回真
let a = new Boolean(false);
if(!a.valueOf()) console.log('abc');
else console.log('Lee');
Object(a)
let a = Object('abc');
a; // String {'abc'}
a.valueOf(); // abc
let b = Object(12345);
b; // Number {12345}
b.valueOf(); // 12345
let c = Object(false);
c; // Boolean {false}
c.valueOf(); // false
let d = Object([1, 2]);
d; // (2) [1, 2]
d.valueOf(); // (2) [1, 2]
关于
数组(array)
、对象(object)
、函数(function)
和正则表达式
,我们通常喜欢以常量的形式来创建它们。实际上,使用常量和使用构造函数的效果是一样的(创建的值都是通过封装对象来包装)
Array(..)
var a = new Array( 1, 2, 3 );
a; // [1, 2, 3]
var b = [1, 2, 3];
b; // [1, 2, 3]
永远不要创建和使用空单元数组
Object(..)
、Function(..)
和 RegExp(..)
除非万不得已,否则尽量不要使用 Object(..)/Function(..)/RegExp(..)
不要把 Function(..) 当作 eval(..) 的替代品,你基本上不会通过这种方式来定义函数
Date(..)
和 Error(..)
Date(..)
和Error(..)
没有对应的常量形式来作为他们的替代
错误对象通常与 throw 一起使用
function foo(x) {
if (!x) {
let err = new Error( "x wasn’t provided" );
console.log({err});
throw err;
}
}
foo();
除
Error(..)
之外,还有一些针对特定错误类型的原生构造函数,如EvalError(..)
、RangeError(..)
、ReferenceError(..)
、SyntaxError(..)
、TypeError(..)
和URIError(..)
。这些构造函数很少被直接使用,它们在程序发生异常(比如试图使用未声明的变量产生 ReferenceError 错误)时会被自动调用。
Symbol(..)
符号是具有唯一性的特殊值(并非绝对),用它来命名对象属性不容易导致重名
符号可以用作属性名,但无论是在代码还是开发控制台中都无法查看和访问它的值,只会显示为诸如
Symbol(Symbol.create)
这样的值
符号并非对象,而是一种简单标量基本类型
var obj = {}
obj[Symbol.iterator] = function (a){return a+1;}
obj[Symbol(Symbol.iterator)]; // TypeError: Cannot convert a Symbol value to a string
obj['Symbol(Symbol.iterator)']; // undefined
Object.getOwnPropertySymbols(obj); // [Symbol(Symbol.iterator)]
obj[Object.getOwnPropertySymbols(obj)[0]]; // ƒ (a){return a+1;}
通过
Object.getOwnPropertySymbols(..)
便可以公开获得对象中的所有符号
很多开发人员喜欢用它来替代有下划线(_)前缀的属性,而下划线前缀通常用于命名私有或特殊属性
原生原型
// Function.prototype 是一个空函数
Function.prototype; // ƒ () { [native code] }
let a;
let fun = a || Function.prototype;
// RegExp.prototype 是一个“空”的正则表达式(无任何匹配)
RegExp.prototype; // /(?:)/
let a;
let reg = a || RegExp.prototype;
// Array.prototype 是一个空数组
Array.prototype; // []
let a;
let arr = a || Array.prototype;
显式类型转换(显式强制类型转换)
:将值从一种类型显示转换为另一种类型;
强制类型转换(隐式强制类型转换)
: 将值从一种类型隐式转换为另一种类型;
let a = 123;
let b = a + ''; // 隐式强制类型转换
let c = String(a); // 显式强制类型转换
注意
: 理解上,显式
和隐式
并没有明确的分界线,理解其含义那么它就是显式,不理解其意它也可以是隐式
toString() 可以被显式调用,或者在需要字符串化时自动调用
toString
方法在转换时会将数字转换为指数形式let num = 0.123 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
num.toString(); // 1.23e+23
toString()
(Object.prototype.toString()
)返回内部属性 [[Class]]
的值,如 "[object Object]"
Object.prototype.toString(); // '[object Object]'
var a = [1,2,3];
a.toString(); // "1,2,3"
所有安全的 JSON 值(JSON-safe)都可以使用 JSON.stringify(..) 字符串化
安全的JSON 值是指能够呈现为有效 JSON 格式的值
undefined
、function
、symbol
、包含循环引用(对象之间相互引用,形成一个无限循环)的对象
JSON.stringify(undefined); // undefined
JSON.stringify(function(){}); // undefined
JSON.stringify(Symbol); // undefined
JSON.stringify(Symbol.iterator); // undefined
JSON.stringify([1, undefined, function(){}, 4]); // '[1,null,null,4]'
JSON.stringify({a: 2, b: function(){}}); // '{"a":2}'
// 对包含循环引用的对象执行 JSON.stringify(..) 会出错
let a = {};
let b = {a};
a.b = b;
JSON.stringify(b); // TypeError: Converting circular structure to JSON
toJSON()
方法,JSON 字符串化时会首先调用该方法,然后用它的返回值来进行序列化
toJSON()
应该“返回一个能够被字符串化的安全的 JSON 值”
,而不是“返回一个 JSON 字符串”
let obj = {
name: 'Lee',
toJSON(){
return {name1: this.name};
}
}
JSON.stringify(obj); // '{"name1":"Lee"}'
深拷贝
和 JSON.parse(JSON.stringify(...))
本质上不一样JSON.stringify()
可以接收3个参数JSON.stringify(
// 需要序列化的数据
value: any,
/**
* 用来指定对象序列化过程中哪些属性应该被处理,哪些应该被排除,和 toJSON() 很像
* 数组:必须是一个字符串数组,其中包含序列化要处理的对象的属性名称,除此之外其他的属性则被忽略
* 函数:它会对对象本身调用一次,然后对对象中的每个属性各调用一次,每次传递两个参数,键和值。如果要忽略某个键就返回 undefined,否则返回指定的值
*/
replacer?: (key: string, value: any) => any,
// 缩进
space?: string | number
): string
replacer数组示例:
var obj = {
a: 123,
b: "123",
c: [1, 2, 3],
d: {
a: 123,
b: "123",
c: [1, 2, 3],
}
};
let json1 = JSON.stringify(obj, ['a', 'b', 'd'], 1);
/**
* {
* "a": 123,
* "b": "123",
* "d": {
* "a": 123,
* "b": "123"
* }
* }
*/
console.log(json1);
replacer函数示例:
var obj = {
a: 123,
b: "123",
c: [1, 2, 3],
d: {
a: 123,
b: "123",
c: [1, 2, 3],
}
};
let json1 = JSON.stringify(obj, (k, v)=>{
console.log('【key】', k, '【value】', v);
return v;
}, null);
// {"a":123,"b":"123","c":[1,2,3],"d":{"a":123,"b":"123","c":[1,2,3]}}
console.log(json1);
space缩进示例:
var obj = {
b: 123,
c: "123",
d: [1, 2, 3]
};
let json1 = JSON.stringify(obj, null, 4);
/**
* {
* "b": 123,
* "c": "123",
* "d": [
* 1,
* 2,
* 3
* ]
* }
*/
console.log(json1);
let json2 = JSON.stringify(obj, null, '--');
/**
* {
* --"b": 123,
* --"c": "123",
* --"d": [
* ----1,
* ----2,
* ----3
* --]
* }
*/
console.log(json2);
Number(true); // 1
Number(false); // 0
Number(undefined); // NaN
Number(null); // 0
Number(''); // 0
Number([]); // 0
Number(['a', 1]); // NaN
Number([1, 2, 3]); // NaN
Number({}); // NaN
Number(0x10); // 16 解析成十进制
Number(010); // 8 解析成十进制
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive 会
首先(通过内部操作 DefaultValue)检查该值是否有 valueOf() 方法。
如果有并且返回基本类型值,就使用该值进行强制类型转换。
如果没有就使用 toString()的返回值(如果存在)来进行强制类型转换。
如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。
Number({ valueOf(){ return '123' } }); // 123
Number({ valueOf(){ return 'abc' } }); // NaN
Number({ valueOf(){ return '' } }); // 0
let a = [1, 2, 3];
a.valueOf = () => '111';
a.toString = () => '456';
Number(a); // 111
let a = [1, 2, 3];
a.toString = () => '456';
Number(a); // 456
undefined
null
false
+0
-0
NaN
''
""
let timestamp = new Date();
+timestamp; // 时间戳
~
运算符(字位运算符非
)(不推荐使用,难以理解)
~x
大致等同于 -(x+1)
(大致等同于
不等于 等于
)let arr = [1, 2, 3];
// arr.indexOf(2); // 1
// ~arr.indexOf(2); // -2
// arr.indexOf(4); // -1
// ~arr.indexOf(4); // 0
function isOfData1(x){
if(arr.indexOf(x) !== -1)
console.log(x, '存在');
else
console.log(x, '不存在');
}
function isOfData2(x){
if(~arr.indexOf(x))
console.log(x, '存在');
else
console.log(x, '不存在');
}
isOfData1(2); // 2 '存在'
isOfData2(2); // 2 '存在'
isOfData1(4); // 4 '不存在'
isOfData2(4); // 4 '不存在'
~~12.123; // 12
parseInt
只支持字符串和数字类型的参数,尽量不要传入非这两种类型的参数Number({ valueOf: _ => 123 }); // 123
parseInt('123'); // 123
parseInt({ valueOf: _ => 123 }); // NaN
parseInt({}); // NaN
parseInt
的 坑
parseInt('0x10'); // 16
parseInt('0x10', 10); // 0
parseInt(1/0, 19); // 18
parseInt(Infinity, 19); // 18
parseInt
一些奇怪的返回值parseInt(0.000008); // 0 ("0" 来自于 "0.000008")
parseInt(0.0000008); // 8 ("8" 来自于 "8e-7")
parseInt(false, 16); // 250 ("fa" 来自于 "false")
parseInt(parseInt, 16); // 15 ("f" 来自于 "function..")
parseInt("0x10"); // 16
parseInt("103", 2); // 2
parseInt
隐式强制类型转换的作用是减少冗余,让代码更简洁
let a = [1, 2];
let b = [3, 4];
a + b; // "1,23,4"
var a = {
valueOf: function() { return 42; },
toString: function() { return 4; }
}
a + ''; // '42'
String(a); // '4'
var a = '123';
a + 0; // '1230'
0 + a; // '0123'
a - 0; // 123
0 - a; // -123
{} + []; // 0
[] + {}; // '[object Object]'
相对布尔值,数字和字符串操作中的隐式强制类型转换还算比较明显
if (..)
语句中的条件判断表达式for ( .. ; .. ; .. )
语句中的条件判断表达式(第二个)while (..)
和 do..while(..)
循环中的条件判断表达式? :
中的条件判断表达式||(逻辑或)
和 &&(逻辑与)
左边的操作数(作为条件判断表达式)短路语句
&&
: 返回第一个为假的表达式,否则返回最后一个表达式||
: 返回第一个为真的表达式,否则返回最后一个表达式从符号到字符串的显式强制类型转换
从符号到字符串的隐式强制类型转换会产生错误
var s1 = Symbol( "cool" );
String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" );
s2 + ""; // TypeError: Cannot convert a Symbol value to a string
==
) 和 严格相等(===
)推荐
===
,原因:严格
有人觉得
==
会比===
慢,实际上虽然强制类型转换确实要多花点时间,但仅仅是微秒级(百万分之一秒)的差别而已。
如果进行比较的两个值类型相同,则
==
和===
使用相同的算法,所以除了 JavaScript 引擎实现上的细微差别之外,它们之间并没有什么不同。如果两个值的类型不同,我们就需要考虑有没有强制类型转换的必要,有就用
==
,没有就用===
,不用在乎性能
Type(x)
是数字,Type(y)
是字符串,则返回 x == ToNumber(y)
的结果;Type(x)
是字符串,Type(y)
是数字,则返回 ToNumber(x) == y
的结果;Type(x)
是布尔类型,则返回 ToNumber(x) == y
的结果;Type(y)
是布尔类型,则返回 x == ToNumber(y)
的结果;null
和 undefined
之间的相等比较
x
为 null
,y
为 undefined
,则结果为 true;x
为 undefined
,y
为 null
,则结果为 true;ToPrimitive
抽象操作特性(如 toString()
、valueOf()
))
Type(x)
是字符串或数字,Type(y)
是对象,则返回 x == ToPrimitive(y)
的结果;Type(x)
是对象,Type(y)
是字符串或数字,则返回 ToPrimitive(x) == y
的结果;Number.prototype.valueOf = function() {
return 3;
};
new Number(2) == 3; // true
"0" == null; // false
"0" == undefined; // false
"0" == false; // true -- 晕!
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true -- 晕!
false == ""; // true -- 晕!
false == []; // true -- 晕!
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true -- 晕!
"" == []; // true -- 晕!
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true -- 晕!
0 == {}; // false
[] == []; // false
[] == ![]; // true
2 == [2]; // true
"" == [null]; // true
0 == "\n"; // true
42 == "43"; // false
"foo" == 42; // false
"true" == true; // false
42 == "42"; // true
"foo" == [ "foo" ]; // true
true
或者 false
,千万不要使用 ==
[]
、""
或者 0
,尽量不要使用 ==
var a = [ 42 ];
var b = [ "43" ];
a < b; // true
b < a; // false
var a = [ "42" ];
var b = [ "043" ];
a < b; // false
<=
不解释为小于等于
,而是解释为不大于
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
语句不是表达式
语句相当于“句子”,而表达式相当于“短语”。“句子”由多个“短语”组成。
let a; // 声明语句
a = 3 * 2; // 赋值表达式
a; // 表达式语句
语句都有一个结果值,
undefined
也算
var a, b;
a = if (true) {b = 4 + 38;}; // SyntaxError: Unexpected token 'if'
// eval 不推荐
var a, b;
a = eval( "if (true) { b = 4 + 38; }" );
a; // 42
var a = 42;
var b = a++;
a; // 43
b; // 42
a++
和 ++a
var a = 42;
a++; // 42
a; // 43
++a; // 44
a; // 44
// obj对象
var obj = {
foo: () => 123
}
var obj =
去掉后,并不会报错,而是变成了一个标签语句
,如下:// 代码块
{
/**
* 标签语句
* 结果:
* 0 0
* 1 0
* 2 0
*/
foo: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
console.log(i, j);
continue foo;
}
}
}
(()=>{
foo: {
console.log('Hello');
break foo;
console.log(234);
}
console.log('Lee');
})()
// Hello
// Lee
[] + {};
和 {} + [];
[] + {}; // '[object Object]' 原因:[]被处理成了'',而{}在表达式中被处理成了空对象,所以输出'[object Object]'
{} + []; // 0 原因:{}被看作了代码块,而[]被处理成了'',故+[]为0
var {a, b} = {a: 123, b: 456};
var {a: a, b: b} = {a: 123, b: 456};
var {a: c, b: d} = {a: 123, b: 456};
var [a, b] = [123, 456];
function foo(a = 1, b = a + 1) {
return a + b;
}
foo(); // 3
foo(2); // 5
foo(2, 2); // 4
foo(undefined, 1); // 2
foo(void 123, 1); // 2
foo(null, 1); // 1
try..finally
try {
foo();
} catch (error) {
console.log(error); // ReferenceError: foo is not defined
} finally {
console.log('Hello Lee!'); // Hello Lee!
}
finally
中存在 return
会覆盖掉 try
和 catch
中的 return
返回值function foo(){
try {
// return bar();
return 'abc';
} catch (error) {
console.log(error);
return 'error';
} finally {
return 123;
}
}
console.log(foo()); // 123
finally
和带标签
的 break
混合使用function foo(){
bar: {
try {
return 'abc';
} catch (error) {
console.log(error);
return 'error';
} finally {
break bar;
}
}
console.log('Hello');
return 'World';
}
console.log(foo()); // Hello World
switch (a)
执行的是 ===
判断switch (true)
可以自定义执行对变量的比较操作var a = '10';
// a 为 10 or '10'
switch (true) {
case a == 20:
console.log(`a 为 20 or '20'`);
break;
case a == 10:
console.log(`a 为 10 or '10'`);
break;
default:
console.log('没有分析出a的值');
break;
}
// 没有分析出a的值
switch (a) {
case 20:
console.log(`a 为 20 or '20'`);
break;
case 10:
console.log(`a 为 10 or '10'`);
break;
default:
console.log('没有分析出a的值');
break;
}
由于浏览器演进的历史遗留问题,在创建带有
id
属性的DOM元素
时也会创建同名的全局变量
<div id="foo"></div>
<script>
console.log(foo); // HTML元素
</script>
<script>
多个
<script>
标签下的代码可以互相调用,但前提是需要保证前后顺序
<script>foo(); // ReferenceError: foo is not defined</script>
<script>function foo(){};</script>
<script>console.log(foo()); // undefined</script>
console.log(foo()); // undefined
function foo(){};
保留字
不能用作变量名(包括四类:关键字
预留关键字
null常量
true/false布尔常量
)