1. JavaScript变量
JavaScript的变量是松散型变量,和之前在python中的 动态机 很类似,这让我想起了之前的一篇讲python变量的文章,松散型指的是变量可以存任何类型数据,所谓变量只是仅仅是存值的占位符而已。如下操作完全没问题,数据类型动态改变。
var temp = 123;
temp = "west";
注:使用var
定义的变量将成为定义该变量的的作用域中的局部变量
如果不用var代表变量是一个全局变量,这里又涉及到了暗示全局变量。
也就是说 在函数中定义的变量在函数退出后即会销毁 ,这里之前的作用域链 提到过。
注:这里再次强调var temp = 123;
的步骤,并不是直接赋值,涉及到了 预编译 ,只有调用该变量时才会给他赋值,之后立即被销毁。
【例】猜它会怎么报错?
function dc() {
var temp = 123;
temp = "west";
}
dc();
alert(temp);
【Error】:Uncaught ReferenceError: temp is not defined
2. 数据类型
ECMAScript 有五种基本数据类型,如下
undefined
null
boolean
number
string
bigint
symbol
最新版的ECMAScript 的基本数据类型新增了:
还有一种复杂数据类型 Object
,当然类似于Function、Number、Error、RegExp也是复杂数据类型。
ECMAScript不支持任何创建自定义类型的机制,就好比C语言的自定义结构体是不支持的。
reference:MDN——最新的 ECMAScript 标准定义了8种数据类型
2.1. undefined和null
实际上 undefined
值派生自 null
值,所以undefined == null //true
,undefined类型只有一个值,即undefined,引入它是为了区分空对象指针和未经初始化的变量。
null表示一空对象指针,如果定义的变量将来用于保存对象,那么最好将该变量初始化为null,这样只要检查null值就可以知道相应的变量是否已经保存了一个对象的引用。
还有,没初始化的和没声明的变量使用typeof 会返回undefined,但是建议都对数据进行初始化,这样返回undefined时就会知道变量时还没声明而不是没初始化。
【例】
var car = null;
if (car != null) {
//操作
}
注:尽管null和undefined又有特殊关系,但他们完全不同,任何情况都没有必要把一个变量值显式地设置为undefined,但对null并不适用,只要意在保存对象的变量还没有真正保存对象,就应该明确保存变量为null值。这样不仅体现null作为空对象指针的惯例,也有助于进一步区分null和undefined。
2.2. Boolean
注意true、false
区分大小写
存在Boolean()
函数将一个值转换为对应的Boolean值。
2.3. Number
- 八进制在严格模式下无效,会抛出
Uncaught SyntaxError: Octal literals are not allowed in strict mode
,在进行算术运算时所有八进制和十六进制表示的数值都会转换为十进制。 - 对于小数点后面没有数字的浮点数,ECMAScript会把它转换为整数保存。
- 对于浮点数值的精度计算远不如整数值,比如
0.1+0.2==0.30000000000000004
,因此永远不要测试某个特定得浮点数值。 - Infinity是不能参加计算的数值,如果数值超出范围会返回
Infinity
表无穷,可使用isFinite()
查看数值是否在范围内,如果在最大与最小数值之间会返回true
。 NaN
,即非数值,任何涉及NaN的操作都会返回NaN,它用来表示本要返回数值的操作数未返回数值的情况(这样就不会报错了)。其次NaN与任何数值都不相等,包括自己。有isNaN()
函数可接受一个任意类型的参数,它会尝试把值转换为数值,比如字符串“10”
和Boolean值,而不能转换为数值的值会返回true
。- JS中number就是number,并没有声明浮点数、整型数的关键字,浮点数最高精度是17位小数
2.4. 数值转换
Number()
可用于任何数据类型parseFloat()
(只解析十进制的值,故只有一个参数) , 用于把字符串转换为数值parseInt()
(有两个参数,后者为进制参数)用于把字符串转换为数值
注意(parseInt(string [, radix])
)是把string转换成数字,让后把它当作radix进制的数字转换成十进制。例如parseInt('321',2)
这样的操作会返回NaN,因为321不可能是2进制的。
与parseInt
类似,parseFloat
从第一个字符开始(位置0)解析字符串,直到末尾或者解析到遇见一个无效的浮点数字字符为止。还要记住字符串中的第一个小数点有效而第二个是无效的。
由于Number()
函数在转换字符时比较复杂并且不够合理,因此在处理整数时更常用的时parseInt()
,还有ES3和ES5对于八进制字面量的字符串进行解析时存在分歧,在ES3中console.log(parseInt("070"));
会输出56,但是ES5中会输出70,原因是ES5中的parseInt已经不具备解析八进制的能力。这个时候加上parseInt的第二个参数就可以改变这情况,实际上带上了第二个参数之后都不再需要在字符串中使用进制字面量,比如console.log(parseInt("70", 8));
不再需要写070了。
parseFloat
始终会忽略前导的0。十六进制始终会被转换为0。相比于parseInt多解析了一个小数点,而且它没有第二个参数,只解析十进制。
console.log(Number("Hello,Word!")); //NaN
console.log(Number("")); //0
console.log(Number("000011")); //11
console.log(Number(true)); //1
console.log(parseInt("1234blue")); //1234
console.log(parseInt("")); //NaN
console.log(parseInt("0xA")); //10
console.log(parseInt("22.5")); //22
console.log(parseInt("070")); //70
console.log(parseInt("70")); //70
console.log(parseInt("0xf")); //15
console.log(parseInt("0xAF", 16)); //175
console.log(parseInt("AF")); //NaN
console.log(parseInt("10", 2)); //2
console.log(parseInt("10", 8)); //8
console.log(parseInt("10", 10)); //10
console.log(parseInt("10", 16)); //16
console.log(parseFloat("1234blue")); //1234
console.log(parseFloat("0xA")); //0
console.log(parseFloat("22.5")); //22.5
console.log(parseFloat("22.34.5")); //22.34
console.log(parseFloat("0908.5")); //908.5
console.log(parseFloat("3.125e7")); //31250000
2.5. String
注:
- PHP中双引号与单引号会影响对字符串解释方式的不同
- 字符串的值不可改变,要改变变量存的字符串只能销毁原来的字符串,再用包含新值的字符串填充该变量。
var test = "Java";
test = test + "Script";
alert(test);
看似是拼接字符串,实际上不是,系统在后台创建了一个新的可容纳JavaScript的变量,再把JavaScript填充进去,,最后再销毁原来的两个字符串。
转换为字符串:toString()
、String()
但需注意二者的不同以及对于undefined、null
二者的特殊性(因为它俩没有toString方法)
console.log(null.toString());
//Uncaught TypeError: Cannot read property 'toString' of null
在不知道要转换的值是否是null、undefined的情况下,为防止报错可使用String对其进行转换。
| 字面量(也叫转义序列) | 含义 |
| :--------------------: | :----------------------------------------------------------------------------: |
| n
| 换行 |
| t
| 制表 |
| b
| 空格 |
| r
| 回车 |
| f
| 进纸(走纸换页) |
| `` | 斜杠 |
| '
| 单引号 |
| "
| 双引号 |
| xnn
| 以十六进制代码nn表示的一个字符(其中n为0~F)
例:x41表示“A” |
| unnnn
| 以十六进制代码nnnn表示的一个Unicode字符(其中n为0~F)
例:u03a3表希腊字符Σ |
2.6. Object
一组数据和功能的集合
创建:var test = Object(); //在不给构造函数传递参数的情况下‘()’可省略
重要思想
在ECMAScript中(就像Java中的java.lang.Object对象一样)Object类型是所有它的实例和基础.换句话说,Object类型所具有的所有属性和方法也同样存在于更具体的对象中,和Java创建对象类似。
Object的每个实例都具有下列属性和方法:
- Construor
- hasOwnProperty(propertyName)
- isPrototypeof(object)
- propertyIsEnumerable(propertyName)
- toLocaleString()
- toString()
- valueOf()
附:JS中toString()、toLocaleString()、valueOf()的区别
在数里讲的很详细了。
2.7. 附加: typeof
参考:typeof返回值
使用typeof操作符可返回下列某个 字符串
"undefined"
——值未定义"boolean"
——值为布尔值"string"
——值为字符串"number"
——值为数值"object"
——值为对象或null,或者其他引用类型数据"function"
——值为函数
由于最新的ES又新增了一些内容,所以现在它还可以返回
"bigint"
"symbol"
从技术角度来讲,function在JS中是一个对象,而不是一种数据类型,但是函数确实有一些特殊的属性,因此通过typeof来区分它们是有必要的。
注:强调typeof是一个操作符而不是一个函数,括号不是必须的,null之所以会返回Object
是因为null最初是作为空对象的占位符的使用的,被认为是空对象的引用。在对正则表达式调用typeof时,Safari5及之前的版本、Chrome7及之前的版本会返回"function"
,其他浏览器会返回"object"
因为typeof只能返回这几种字符串,所以它是不完善的,比如在面对object和array时,都只会返回 "object"
,对这一缺陷可以使用以下方法弥补
// typeof的完善
function type(target) {
var ret = typeof (target);
var template = {
"[object Array]": "array",
"[object Object]": "object",
"[object Number]": "number - object",
"[object Boolean]": "boolean - object",
"[object String]": "string - object",
}
if (target === null) {
return "null";
}
if (ret == "object") {
var str = Object.prototype.toString.call(target);
return template[str];
} else {
return ret;
}
}
3. JavaScript变量的问题
JavaScript变量松散型的本质,决定了他只是在特定时间用于保存特定值的一个名字而已。不存在定义某个变量必须要保存某种数据类型值的规则,变量的值及数据类型可以在脚本的生命周期内改变。
ECMAScript不认为字符串是引用类型的,这点与其他语言不同。
ECMAScript变量可能包含两种不同数据类型的值:
4. 以下为来自MDN的摘抄
其实内容和上面的很多事重复的,主要是想看英文的,所以摘抄重要的点。
参考:Grammar and types
4.1. Basics
- case-sensitive(区分大小写)
- uses the Unicode character set(集合)
- instructions are called statements and are separated by semicolons (;).
对于语句末尾到底要不要加分号?MDN_ASI 给出了回答
4.2. Declarations
JavaScript has three kinds of variable declarations.
- var:Declares a variable, optionally initializing it to a value.
- let:Declares a block-scoped(块作用域), local variable(局部变量), optionally initializing it to a value.
- const:Declares a block-scoped, read-only named constant(只读命名常量).
With the keywordvar
. For example, var x = 42. This syntax can be used to declare both local and global variables, depending on the execution context.With the keyword
const
orlet
. For example, let y = 13. This syntax can be used to declare a block-scope local variable. (See Variable scope below.)
4.3. Evaluating variables
- The undefined value behaves as false when used in a boolean context.
- The undefined value converts to NaN when used in numeric context.
- When you evaluate a null variable, the null value behaves as 0 in numeric contexts and as false in boolean contexts.
4.4. Variable scope
- When you declare a variable outside of any function, it is called a global variable, it is available to any other code in the current document.
- When you declare a variable within a function, it is called a local variable, because it is available only within that function.
- JavaScript before ECMAScript 2015 does not have block statement scope. Rather, a variable declared within a block is local to the function (or global scope) that the block resides within.(在块中声明的变量是该块所驻留的函数(或全局范围)的局部变量)
- The scope of x is not limited to the immediate
if{}
statement block.
注:This behavior changes when using the let declaration (introduced in ECMAScript 2015).
if (true) {
let y = 5;
}
console.log(y); // ReferenceError: y is not defined
4.5. Variable hoisting
Variable hoisting involves pre-compilation(预编译),you can refer to a variable declared later, without getting an exception.
Because of hoisting, all var statements in a function should be placed as near to the top of the function as possible. This best practice increases the clarity of the code(代码清晰度).
In ECMAScript 2015, let and const are hoisted but not initialized. Referencing the variable in the block before the variable declaration results in a ReferenceError, because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
4.6. Function hoisting
In the case of functions, only function declarations are hoisted—but not the function expressions.
(只有函数声明会被提升,函数表达式不会被提升)
var b = function () {} //函数表达式
function myFun() {} //函数声明
在Javscript中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问——预编译)。至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
4.7. Global variables
关于 GO 和 window 的关系在上一篇文章中提过: window对象
Global variables are in fact properties of the global object.
In web pages, the global object is window, so you can set and access global variables using the window.variable
syntax.
因此,你可以通过指定 window 或 frame 的名字,在当前 window 或 frame 访问另一个 window 或 frame 中声明的变量。例如,在文档里声明一个叫 phoneNumber 的变量,那么你就可以在子框架里使用 parent.phoneNumber 的方式来引用它。
这里所说的子框架指的是iframe
HTML内联框架元素 (<iframe>
) 表示嵌套的browsing context。它能够将另一个HTML页面嵌入到当前页面中。每个嵌入的浏览上下文(embedded browsing context)都有自己的会话历史记录(session history)和DOM树。包含嵌入内容的浏览上下文称为父级浏览上下文。顶级浏览上下文(没有父级)通常是由 Window 对象表示的浏览器窗口。
4.8. Constants
You can create a read-only, named constant with the const keyword.
A constant cannot change value through assignment(分配) or be re-declared while the script is running. It must be initialized to a value.
尝试更改常量的值将会导致:Uncaught TypeError: Assignment to constant variable.
在同一个范围内也不能声明常量和函数同名。
However, the properties of objects assigned to constants are not protected, so the following statement is executed without problems.
(分配给常量的对象的属性不受保护,因此可以重新赋值),同样的如果分配给常量的是一个数组,数组中的值也不受保护,也可以像对象的属性一样修改。
const a = {
tt: "Tian"
};
a = 2;
//Uncaught TypeError: Assignment to constant variable.
a.tt = 23 //23
// 数组同理
4.9. Data structures and types
Seven data types that are primitives(原始):
Boolean
. true and false.null
. A special keyword denoting a null value. (Because JavaScript is case-sensitive,null is not the same as Null, NULL, or any other variant.)undefined
. A top-level property whose value is not defined.Number
. An integer or floating point number. For example: 42 or 3.14159.BigInt
. An integer with arbitrary(任意) precision(精度). For example:9007199254740992n
.String
. A sequence of characters that represent a text value. For example: "Howdy"Symbol
(new in ECMAScript 2015). A data type whose instances are unique and immutable(不可变).
- and
Object
4.10. Data type conversion
JavaScript is a dynamically(动态地) typed language,所以你声明变量是不必指明数据类型,并且数据类型还可以自动地转换。
4.11. Numbers and the '+' operator
用 +
算子连接数字和字符串,数字将会变成字符。 但有些时候JS不会将数字转换成字符。比如'3' - 2 //1
可使用 parseInt()
、parseFloat()
将用字符串存储的数字转换为数字。
另一种另类的处理方法是使用 +
(一元运算符) 从字符串中提取数字
'1.1' + '1.1' // '1.11.1'
(+'1.1') + (+'1.1') // 2.2
// Note: the parentheses(括号) are added for clarity(清晰度), not required.
5. Literals
Literals 应该翻译成什么?字面量?字符?文字?我理解成能直接看出来是什么类型的数据 “的”这种声明的形式。
它代表了JS中的值,是在脚本中自己提供的固定的值,而不是变量。翻译总有偏差,所以它只叫 Literals。
5.1. Array literals
是0个或者多个表达式组成的列表,每个表达式代表一个数组元素,括在方括号中([]
),你可以用 Array literals 创建一个数组,它的元素被初始化成指定值。
Note : An array literal is a type of object initializer
5.2. Boolean literals
The Boolean type has two literal values: true and false.
var bool = new Boolean()
// bool = Boolean {false}
typeof bool // "object"
bool == false // true
bool === false //false
5.3. Floating-point literals
3.1415926
-.123456789
-3.1E+12
.1e-23
5.4. Numeric literals
Number and BigInt types can be expressed in decimal (十进制), hexadecimal (16进制), octal (8进制) and binary (2进制).
5.5. Object literals
An object literal is a list of zero or more pairs of property names and associated values of an object, enclosed in curly braces ({}
).
5.6. RegExp literals
A regex literal is a pattern enclosed between slashes. The following is an example of a regex literal.
var re = /ab+c/;
5.7. String literals
A string literal is zero or more characters enclosed in double (") or single (') quotation marks.
You can call any of the String object's methods on a string literal value. JavaScript automatically converts the string literal to a temporary(临时) String object, then discards the temporary String object.
5.8. 解构赋值
参考:https://zh.javascript.info/de...
5.9. 数组解构
解构赋值是一种特殊且简洁的语法,让我们可以拆分复杂数据类型中的需要的内容到一系列变量中。解构操作对那些具有很多参数和默认值等的函数也很奏效。
let arr = ["Ilya", "Kantor"]
// 解构赋值
let [firstName, surname] = arr;
console.log(firstName); // Ilya
console.log(surname); // Kantor
// 结合split
let [firstName1, surname1] = "Ilya Kantor".split(' ');
console.log(firstName1); // Ilya
console.log(surname1); // Kantor
- 解构并没有破坏原有的数组。
- 对于数组中不想要的元素可以通过多加逗号滤除,
// 不需要第二个元素
let [firstName, , title] = ["Julius", "Caesar", "Consul", " Republic"];
alert(title); // Consuls
- 等号右侧可以是任何可迭代对象
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
- 赋值给等号左侧的任何内容
let user = {};
[user.name, user.surname] = "Ilya Kantor".split(' ');
alert(user.name); // Ilya
- 与 .entries() 方法进行循环操作
这写法非常简洁。
let user = {
name: "John",
age: 30
};
// 循环遍历键—值对
for (let [key, value] of Object.entries(user)) {
alert(`${key}:${value}`); // name:John, then age:30
}
与map结合
let user = new Map();
user.set("name", "John");
user.set("age", "30");
for (let [key, value] of user) {
alert(`${key}:${value}`); // name:John, then age:30
}
- 交换变量技巧
let guest = "Jane";
let admin = "Pete";
let marry = "HuiD";
[guest, marry, admin] = [admin, guest, marry];
console.log(guest, admin, marry); // Pete HuiD Jane
- 剩余的 ‘…’
let arr = ["Jane", "HuiD", "Pete", "Caoke"];
let [one, ...two] = ["Jane", "HuiD", "Pete", "Caoke"];
console.log(one); //Jane
console.log(two); //["HuiD", "Pete", "Caoke"]
- 默认值
如果赋值语句中,变量的数量多于数组中实际元素的数量,赋值不会报错。未赋值的变量被认为是 undefined
let [firstName, surname] = [];
console.log(firstName); // undefined
console.log(surname); // undefined
// 默认值
let [name = "Guest", surname1 = "Anonymous"] = ["Julius"];
console.log(name); // Julius(来自数组的值)
console.log(surname1); // Anonymous(默认值被使用了)
默认值可以是更加复杂的表达式甚至可以是函数调用,这些表达式或函数只会在这个变量未被赋值的时候才会被计算。
// 只会提示输入姓氏
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"]; //Juliu对应到name
console.log(name); // Julius(来自数组)
console.log(surname); // 你输入的值
5.10. 对象解构
对象结构和数组结构的区别在于哪里?
let options = {
title: "Menu",
width: 100,
height: 200
};
// 注意这里变量名和对象中的键相同 ,等号左侧包含了对象相应属性的一个“模式(pattern)”,变量的顺序并不重要。
let {
title,
width,
height
} = options;
console.log(title); // Menu
console.log(width); // 100
console.log(height); // 200
如果我们想把一个属性赋值给另一个名字的变量,比如把 options.width 属性赋值给变量 w,那么我们可以使用冒号来指定:
let options = {
title: "Menu",
width: 100,
height: 200
};
// { sourceProperty: targetVariable }
let {
width: w,
height: h,
title
} = options;
// width -> w
// height -> h
// title -> title
console.log(title); // Menu
console.log(w); // 100
console.log(h); // 200
对于可能缺失的属性,我们可以使用 "=" 设置默认值。就像数组或函数参数一样,默认值可以是任意表达式甚至可以是函数调用。它们只会在未提供对应的值时才会被计算/调用。
我们还可以将冒号和等号结合起来:
let options = {
title: "Menu"
};
let {
width: w = 100,
height: h = 200,
title = "Defult"
} = options;
console.log(title); // Menu
console.log(w); // 100
console.log(h); // 200
同数组解构类似,我们可以只取其中的某一些属性,然后把“剩余的”赋值到其他地方。
我们可以使用剩余模式(pattern),就像我们对数组那样。一些较旧的浏览器不支持此功能(例如,使用 Babel 对其进行填充),但可以在现代浏览器中使用。
let obj = {
name: "Hui",
age: 22,
heigh: 168
};
let {
name,
...per
} = obj;
console.log(name); //Hui
console.log(per); //{age: 22, heigh: 168}
注意:JavaScript把主代码流的{...}当作一个代码块,所以
let title, width, height;
// 这一行发生了错误,js会把{}当作代码块,但实际上它是对象解构
{title, width, height} = {title: "Menu", width: 200, height: 100};
// 通过把整个赋值表达式用括号 (...) 包起来解决
({title, width, height} = {title: "Menu", width: 200, height: 100});
5.11. 嵌套解构
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
// 为了清晰起见,解构赋值语句被写成多行的形式
let {
size: { // 把 size 赋值到这里
width,
height
},
items: [item1, item2], // 把 items 赋值到这里
title = "Menu" // 在对象中不存在(使用默认值)
} = options;
console.log(title); // Menu
console.log(width); // 100
console.log(height); // 200
console.log(item1); // Cake
console.log(item2); // Donut
let arr = [{
name: "Hui",
age: 22,
info: [178, 18]
}, "DT", ["Jane", "Pete"]];
let [{
name,
age,
info: [heigh, length]
}, str, [J, P]] = arr;
console.log(name); //Hui
console.log(age); //22
console.log(heigh); //178
console.log(length); //18
console.log(str); //DT
console.log(J); //Jane
console.log(P); //Pete
5.12. 智能函数参数
参考:https://zh.javascript.info/de...
主要针对函数传参进行代码清洁
我自己写解构时出现的几次错误都是因为搞混了数组与对象使用的括号,还有冒号、等号以及对象属性需要同名等,就这几点需要记住。
6. JSON
JSON和语言无关,是纯数据的规范,因此一些JavaScript特定的属性会被JSON.stringify跳过,比如:
- 函数属性(方法)。
- Symbol 类型的属性。
- 存储 undefined 的属性。
这在借助JSON做deepClone的时候需要注意。
let obj = {
name: "DT",
age: 23,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.age;
},
wife: undefined,
say: function () {
console.log("Hui");
},
}
console.log((JSON.stringify(obj))); //{"name":"DT","age":23}
参考:Symbol.toPrimitive
- 支持嵌套解构
重要的限制:不得有循环引用。
let meetup = {
title: "Conference",
room: {
number: 23,
participants: ["john", "ann"]
}
};
console.log(JSON.stringify(meetup));
/* 整个解构都被字符串化了
{
"title": "Conference",
"room": {
"number": 23,
"participants": ["john", "ann"]
}
}
*/
let room = {
number: 23
};
meetup.place = room; // meetup 引用了 room
room.occupiedBy = meetup; // room 引用了 meetup
// 在这里,转换失败了,因为循环引用:room.occupiedBy 引用了 meetup,meetup.place 引用了 room
console.log(JSON.stringify(meetup));; // Uncaught TypeError: Converting circular structure to JSON
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。