JavaScript
简介
JavsScript 于 1995 年由 Brendan Eich 用时 10 天写出,用于网景浏览器。最初的名字叫 LiveScript,也被部分员工称为 Mocha。那时 Java 语言很流行,出于商业化的考量,更名为 JavaScript,但两者之间没有关联。
最早的 JS 作为脚本语言给浏览器增加一些诸如鼠标跟随等交互性特效,通过在 HTML 中添加 <script>
元素实现。
1996 年网景和 ECMA 达成了协议,起草 JS 的规范,第一版规范由 ECMA 做出并于 1997 释出。这个规范被称为 ECMA-262 标准。
JS 并不是一个独立的实体,它仅存在于实现中,这个实现称为 JS 引擎。
传统的 JS 引擎环境就是浏览器,确切来说,JS 引擎是浏览器的一个构建块。
此外,JS 引擎本身也可以和浏览器的其他部分交互,这些部分被称为浏览器 API。
所以 JS 代码有两个功能:
- 和浏览器中的 JS 引擎交互
- 和浏览器中的 JS 引擎外的其他部分交互
随后在 2009 年,Ryan Dahl 发布了 Node.js,他把 JS 引擎作为一个独立的实体,使得 JS 可以在浏览器外,作为服务端环境运行。
几种类似 JavaScript 的语言
- JScript
- VBScript
- TypeScript
为什么选择 JavaScript
- 目前最流行的语言之一
- 浏览器唯一编程语言
- 使用者数量多
- 容易使用
- 无处不在
- 纯 JavaScript (Vanilla JS / Plain JS)
- UI 库/框架(React/Vue/Angular/jQuery/Ember/Next)
- 服务端(Node/Deno/Bun)
- 移动端(React Native)
- 桌面端(Electron)
- 社区庞大
- 需求大
注释
被注释的代码块是不会执行的。
JavaScript有两种写注释(comment)的方法。
- 使用
//
注释掉当前行的代码。 这是一个行内注释:
// This is an in-line comment.
- 也可以使用多行注释,使用
/*
开始,*/
结束。 这是一个多行注释:
/* this is a multi-line commment */
最佳实践:应该添加注释来解释代码的作用,适当的注释能让代码更易懂。
分号
分号(semi-colon) ;
在 JavaScript 中用于表示一条语句的结尾。
console.log("hello world");
console.log("hello world")
浏览器有自动插入分号(Automatic Semi-Colon Insertion / ASI)的功能,所以不加分号浏览器也能正确识别语句的结尾。但是最佳实践显示,应该加上分号以提高代码的可读性。
变量 Variable
数据类型 Data Type
JavaScript 提供七种不同的基本类型(Primitive),分别是:
undefined
未定义、null
空、boolean
布尔、string
字符串、symbol
标志、number
数字、bigint
任意大的整数
基本类型不是对象(object),没有方法(method)和属性(property)。
声明 Declare
通过在变量前面使用关键字 var
,声明一个变量,例如:
var myName;//undefined
变量名称可以由数字、字母、美元符号 $
或者下划线 _
组成,但是不能包含空格或者以数字为开头。
如果只声明不赋值,那么值为 undefined
。
字符串变量 String
"your name"
被称为字符串字面量(string literal)。 字符串文字或字符串是用单引号或双引号括起来的一系列零个或多个字符。
与其他一些编程语言不同的是,单引号和双引号的功能在 JavaScript 中是相同的。
字符串在开头和结尾都要有相同的引号,如果在中间使用了相同的引号,字符串会提前中止并抛出错误。
'' // empty string
"" // empty string
var myFirstName = "Pete";
var myLastName = "Mitchell";
const conversation = 'Finn exclaims to Jake, "Algebraic!"';
var str = 'de'f'; // SyntaxError: Unexpected identifier
可迭代
在 JS 中,字符串是可迭代(iterable)类型,指的是这种数据类型可以用循环迭代。
字符串也被称为类数组(array-like)类型。
字符串的长度 Length
通过 .length
来获得字符串的长度。
let lastNameLength = 0;
const lastName = "Lovelace";
lastNameLength = lastName.length;
字符串索引 Index
方括号表示法(Bracket notation)是一种在字符串中的特定 index(索引)处获取字符的方法。
大多数现代编程语言,是从 0 开始计数。 这被称为基于零(Zero-based)的索引。
let firstLetterOfLastName = "";
const lastName = "Lovelace";
firstLetterOfLastName = lastName[0]; // 'L'
字符串的不变性 Immutable
在 JavaScript 中,字符串(String
)的值是不可变的(immutable),这意味着一旦字符串被创建就不能被改变。
例如:
let myStr = "Bob";
myStr[0] = "J";
是不会把变量 myStr
的值改变成 Job
的,因为变量 myStr
是不可变的。 注意,这并不 意味着 myStr
永远不能被改变,只是字符串字面量 string literal 的各个字符不能被改变。 改变 myStr
的唯一方法是重新给它赋一个值,例如:
let myStr = "Bob";
myStr = "Job";
字符串中的字符
为了读取从 0 开始某特定位置的字符,使用 charAt()
方法:
var greet = 'hello';
greet.charAt(0) // h
字符串拼接
字符串拼接方法 concat()
:
"Wo".concat("rl").concat("d") # World
获得某字符的索引
使用 indexOf()
获得某字符在字符串中的索引位置,如果不包含该字符,则返回 -1
,如果包含多个相同字符,则返回第一个字符出现的位置;lastIndexOf()
返回相同字符出现的最后一个位置:
'greet'.indexOf('g') // 0
'greet'.indexOf('e') // 2
'greet'.indexOf('a') // -1
'cace'.lastIndexOf('c'); // 2
Undefined & Null
undefined
数据类型只有一个值:undefined
。
- 所有函数默认返回
undefined
,除非显式返回一个值。 - 访问对象不存在的属性,返回
undefined
- 已声明但未赋值的变量,
undefined
当 JavaScript 中的变量被声明的时候,程序内部会给它一个初始值 undefined
。 当对一个值为 undefined
的变量进行运算操作的时候,算出来的结果将会是 NaN
,它的意思是 “Not a Number”。 当用一个值是 undefined
的变量来做字符串拼接操作的时候,它会转换成字符串(string)undefined
。
null
指代缺少(absence)值,undefined
指代未赋值。
var str = 'abc';
str.match(/d/); // null
var str2; // undefined
变量名区分大小写 Case Sensitivity
最佳实践:使用驼峰命名法(camelCase)来命名一个 Javascript 变量。 在驼峰命名法中,变量名的第一个单词的首字母小写,后面的单词的第一个字母大写。
var someVariable;
var anotherVariable;
var 和 let
使用 var
关键字(keywords)声明变量存在的问题:
- 在声明前就可以被使用
- 相同的变量可以再次声明
- 可以在全局或者函数内部的作用域起作用
// 第一种
console.log(user); // mike
var user = 'mike';
// 第二种
var camper = "James";
var camper = "David";
console.log(camper); // David
ES6 中引入了一个名为 let
的关键字,这是对 JavaScript 的一次重大更新,以解决与 var
关键字有关的潜在问题。
如果将上面代码中的 var
替换为 let
,则会导致错误。使用 let
时,同名的变量只能声明一次。
const
const
具有 let
的所有功能,另外还有一个额外的好处,即使用 const
声明的变量是只读(Read-only)的。 它们是一个常量值(constant),这意味着一旦一个变量被赋值为 const
,它就不能被重新赋值:
const FAV_PET = 'cats';
FAV_PETS = 'dogs'
// this line will cause a error;
命名常量的常见做法是全部使用大写字母,单词之间用下划线分隔。
小数 Decimal
小数有时候也被称作浮点数或者 floats。
提示: 不是所有的实数都可以用浮点数(floating point)来表示。 因为可能产生四舍五入的错误。
var myDecimal = 5.7;
布尔值 Boolean
布尔值只能是两个值中的一个:true
或者 false
。
布尔值是不带引号的。 字符串 "true"
和 "false"
不是布尔值,在 JavaScript 中也没有特殊含义。
function welcomeToBooleans() {
return true;
}
真值 假值
真值(Truthy)和假值(Falsy)指的是在布尔值的上下文中,转换后为 true 和 false 的值。
所有除 false
、0
、-0
、0n
、""
、null
、undefined
和 NaN
以外的皆为真值)。
if (true)
if ({})
if ([])
if (42)
if ("0")
if ("false")
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)
JS 中有 8 个假值:
值 | 说明 |
---|---|
false | false 关键字 |
0 | 数值 zero |
-0 | 数值 负 zero |
0n | 当 BigInt 作为布尔值使用时,遵从其作为数值的规则. 0n 是 falsy 值。 |
“”, ‘’, `` | 这是一个空字符串 (字符串的长度为零). JavaScript 中的字符串可用双引号 "" , 单引号 '' , 或 模板字面量 ```` 定义。 |
null | null - 缺少值 |
undefined | undefined - 原始值 |
NaN | NaN - 非数值 |
BigInt Symbol
这两个数据类型是 ES6 引入的,BigInt 用于表示更大的数字,Symbol 用于表示统一标识符。
数字 Number
数字类型可以表示整数(integer) 或者小数(decimal)。
运算 Operation
赋值 Assignment
使用赋值(assignment)运算符 =
将值存储在变量中。
var myVariable = 5; // 直接赋值
var myCode; // 先声明后赋值
myCode = 100;
在赋值后,可使用赋值运算符将某变量的值赋给另一个变量。
var myVariable = 5;
var b;
b = myVariable;
加法 Add
var sum = 10 + 1; // sum = 11;
拼接 Concatenate
当 +
操作符被用于一个 String
类型的值的时候,它被称作拼接操作符。
const myStr = "This is the start." + " " + "This is the end."; // myStr "This is the start This is the end"
如果一个数字和字符串做拼接,JS 会把数字转化为字符串然后再拼接,最后得到一个字符串:
365 + " days" // "365 days"
1 + "2" // "12"
这种看不见(under-the-hood)的转换称为“强制类型转换(coercion)”。
减法 Subtract
var difference = 10 - 2; // 8
乘法 Multiply
var product = 1 * 2; // 2
除法 Divide
var quotient = 10 / 2; // 5
自增 Increment
var my = 1;
my++; // my 2
自减 Decrement
var my = 2;
my--; // my 1
求余 Reminder
求余运算符(The modulus operator) %
返回两个数相除得到的余数
用法
在数学中,判断一个数是奇数还是偶数,只需要判断这个数除以 2
得到的余数是 0 还是 1。
提示
余数运算符(remainder)有时被错误地称为“模数”运算符。 它与模数非常相似,但不能用于负数的运算。
let remainder = 11 % 3; // 2
求幂 Exponentiation
求幂运算符 **
返回某底数的次幂
let exp = 10 ** 2 // 100
复合赋值 Compound Assignment
+=
,-=
,*=
,`/=``
+=
称为“addition assignment operator。
还可以使用 +=
运算符来拼接字符串到现有字符串变量的结尾。
let myVar = 1;
myVar += 5;
myVar -= 1;
myVar *= 1;
myVar /= 1;
let myStr = "This is the first sentence.";
myStr += " ";
myStr += "This is the second sentence."
相等 Equality Operator
相互比较的操作都返回一个 true
或 false
值。
最基本的运算符是相等运算符:==
。
在 JavaScript 中,为了让两个不同的数据类型(例如 numbers
和 strings
)的值可以作比较,它必须把一种类型转换为另一种类型。 这叫作 “类型强制转换”。 转换之后,可以像下面这样来比较:
1 == 1 //true
1 == 2 //false
1 == '1' //true
"3" == 3 //true
严格相等 Strict Equality Operator
严格相等运算符不会做类型转换。如果比较的值类型不同,那么在严格相等运算符比较下它们是不相等的,会返回 false 。
3 === 3 //true
3 === '3' //false
不等 Inquality Opeartor
不相等运算符(!=
)与相等运算符是相反的。 不相等运算符在比较的时候也会转换值的数据类型。
1 != 2 // true
1 != "1" // false
1 != '1' // false
1 != true // false
0 != false // false
严格不等 Strict Inequality Operator
严格不相等运算符(!==
)与全等运算符是相反的。 严格不相等运算符不会转换值的数据类型。
3 !== 3 // false
3 !== '3' // true;
4 !== 3 // true;
大于 Greater Than Operator
使用大于运算符(>
)来比较两个数字。 如果大于运算符左边的数字大于右边的数字,将会返回 true
。 否则,它返回 false
。
大于运算符在比较的时候,会转换值的数据类型。
5 > 3 // true;
7 > '3' // true;
2 > 3 // false;
'1' > 9 // false;
大于等于 Greater Than Or Equal To Operator
使用大于等于运算符(>=
)来比较两个数字的大小。 如果大于等于运算符左边的数字比右边的数字大或者相等,会返回 true
。 否则,会返回 false
。
大于等于运算符在比较的时候会转换值的数据类型。
6 >= 6 // true
7 >= '3' // true
2 >= 3 // false
'7' >= 9 // false
小于 Less Than Operator
使用小于运算符(<
)来比较两个数字。 如果小于运算符左边的数字比右边的数字小,它会返回 true
。 否则会返回 false
。 小于运算符在做比较的时候会转换值的数据类型。
2 < 5 // true
'3' < 7 // true
5 < 5 // false
3 < 2 // false
'8' < 4 // false
小于等于 Less Than Or Equal To Operator
使用小于等于运算符(<=
)比较两个数字的大小。 如果在小于等于运算符左边的数字小于或者等于右边的数字,它会返回 true
。 如果在小于等于运算符左边的数字大于右边的数字,它会返回 false
。小于或等于运算符会转换数据类型。
4 <= 5 // true
'7' <= 7 // true
5 <= 5 // true
3 <= 2 // false
'8' <= 4 // false
逻辑与 Logical And Opeator
当且仅当运算符的左边和右边都是 true,逻辑与运算符(&&
)才会返回 true
。
该运算符可用于两个比较运算:
if (val <= 50 && val >= 25) {
return "Yes";
}
逻辑或 Logical Or Operator
只要逻辑或运算符(||
)两边的任何一个运算的结果是 true
,则返回 true
。 否则,返回 false
。
if (val <= 10 || val >= 20) {
return "Outside";
}
逻辑非 Logical Not Operator
当结果为 true
,逻辑非(!
)返回 false
;反之返回 true
。
var a = 6
a > 5 # true
!(a > 5) # false
三元运算符 Ternary Operator
条件运算符( conditional operator,)(也称为三元运算符( ternary operator))语法是:a ? b : c
, where a
是条件,当条件返回 true
的时候运行代码 b
,当条件返回 false
的时候运行代码 c
。
function checkEqual(a, b) {
return a === b ? "Equal" : "Not Equal";
}
确定类型 Typeof
使用 typeof
运算符确定变量或值的类型
typeof 3 // number
typeof '3' // string
typeof [] // object
typeof {} // object
优先级和结合律
优先级(precedence)决定了运算的先后次序,结合律(associativity)决定了在优先级相同的情况下,运算的左右顺序。
有两种结合律:
- 从左到右
- 从右到左
转义 Escape
定义一个字符串必须要用单引号或双引号来包裹它。 那么当字符串里面包含引号 "
或者 '
时,在 JavaScript 中,可以通过在引号前面使用反斜杠(\
)来转义引号。
const myStr = "I am a \"double quoted\" string inside \"double quotes\".";
转义字符 Escape Sequence
引号不是字符串中唯一可以被转义(escaped)的字符。 使用转义字符有两个原因:
- 首先是可以使用无法输入的字符,例如退格。
- 其次是可以在一个字符串中表示多个引号,而不会出错。
代码 | 输出 |
---|---|
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\b | 退格 |
\f | 换页符 |
请注意,必须对反斜杠本身进行转义,它才能显示为反斜杠。
const myStr = "FirstLine\n\t\\SecondLine\nThirdLine";
条件 Conditional
条件语句也被称为控制流语句(flow control statement)。
if else
if
语句用于在代码中做出决定。
当条件的计算结果为 true
,程序执行大括号内的语句。 当布尔条件的计算结果为 false
,大括号内的代码将不会执行。使用 else
语句,可以执行当条件为假时相应的代码。
如果有多个条件语句,通过 else if
语句把 if
语句链起来。
function trueOrFalse(wasThatTrue) {
if (wasThatTrue) {
return "Yes, that was true";
}
else {
return "No, that was false";
}
}
switch
switch
评估一个表达式,将表达式的值与 case 子句匹配。 从第一个匹配的 case
值执行语句,直到遇到 break
。
case
值使用严格相等(===
)运算符进行比较。 注意 case
的值,1
匹配数字 1
,‘1’ 匹配字符串 ‘1’。break
告诉 JavaScript 停止执行 switch 语句。 如果遗漏了 break
,下一个语句将会被执行。
在 switch
语句中,可能无法用 case
枚举出所有可能的值。 相反,添加 default
语句,它会在找不到相匹配的 case
语句之后执行。 可以把它看作是 if/else
链中最后的那个 else
语句。
default
语句应该被放到最后。
function switchOfStuff(val) {
let answer = "";
switch(val) {
case 'a': answer = "apple"; break;
case 'b': answer = "bird"; break;
case 'c': answer = "cat"; break;
default : answer = "stuff"; break;
}
return answer;
}
switchOfStuff(1); // stuff
如果想为 switch
中的多个不同的输入设置相同的结果,可以这样写:
function sequentialSizes(val) {
let answer = "";
switch(val) {
case 1:
case 2:
case 3: answer = "Low"; break;
case 4:
case 5:
case 6: answer = "Mid";
break;
case 7:
case 8:
case 9: answer = "High";break;
}
return answer;
}
sequentialSizes(1); // Low
循环 Loop
while
当 while 指定的条件为真,循环才会执行,反之不执行。
const myArray = [];
let i = 5;
while (i <= 5 && i >= 0) {
myArray.push(i);
i--;
}
上面的 i 称为计数器(counter)。
for
最常见的循环就是 for
,它可以循环指定次数。
for 循环中的可选三个表达式用分号隔开:
for (a; b; c)
,其中 a
为初始化语句,b
为条件语句,c
是最终的表达式。
初始化语句只会在执行循环开始之前执行一次。 它通常用于定义和设置循环变量。
循环条件语句会在每一轮循环的开始前执行,只要条件判断为 true
就会继续执行循环。 当条件为 false
的时候,循环将停止执行。 这意味着,如果条件在一开始就为 false,这个循环将不会执行。
终止循环表达式在每次循环迭代结束, 在下一个条件检查之前时执行,通常用来递增或递减循环计数。
const myArray = [];
for (let i = 1; i <= 5; i++) {
myArray.push(i);
}
for-of
for-of
循环可用在可迭代对象中:
const colors = ['red', 'blue', 'yellow'];
for (var color of colors) {
console.log(color);
}
do-while
它被称为 do...while
循环,是因为不论什么情况,它都会首先 do
(运行)循环里的第一部分代码,然后 while
(当)规定的条件被评估为 true
(真)的时候,它会继续运行循环。
const myArray = [];
let i = 10;
do {
myArray.push(i);
i++;
} while (i < 11);
嵌套循环
如果有一个二维数组,可以使用相同的逻辑,先遍历外面的数组,再遍历里面的子数组。
let myArray = [[1, 2], [3, 4]];
for (let i = 0; i < myArray.length; i++) {
for (let j = 0; j < myArray[i].length; j++) {
console.log(myArray[i][j]);
}
}
函数 Function
把代码的重复部分抽取出来,放到一个函数 (function)中。
通过函数名加上后面的小括号来调用(invoke / call)这个函数,就像这样: functionName();
每次调用函数时,大括号之间的所有代码都将被执行。
function reusableFunction() {
console.log("Hi World");
}
reusableFunction(); // Hi World
传参 Passing value with Argument
函数的参数 (parameters)在函数调用中充当传入函数的输入占位符(也叫形参)。 函数调用时,参数可以为一个或多个。 调用函数时输入(或传递 “passed”)的实际值被称为参数(arguments)。
function functionWithArgs(a,b) {
console.log(a + b);
}
functionWithArgs(3, 4); // 7
返回值 Return Value
使用 return
语句把数据从一个函数中传出来。
function timesFive(a) {
return a * 5;
}
timesFive(1); // 5
在函数没有 return
语句的情况下,当调用它时,该函数会执行内部代码,返回的值是 undefined
。
let sum = 0;
function addThree() {
sum = sum + 3;
}
function addFive() {
sum += 5;
}
addThree();
addFive();
作用域和函数 Scope
全局作用域 Gobal Scope
在 JavaScript 中,作用域涉及到变量的作用范围。 在函数外定义的变量具有 全局 作用域。 具有全局作用域的变量可以在代码的任何地方被调用。
未使用 let
或 const
关键字声明的变量会在 global
范围内自动创建。 当在代码其他地方无意间定义了一个变量,刚好变量名与全局变量相同,这时会产生意想不到的后果。 应该总是用 let
或 const
声明变量。
局部作用域 Local Scope
在一个函数内声明的变量,以及该函数的参数都具有局部(local)作用域。 这意味着它们只在该函数内可见。
在 ES5 中,只有函数能创建局部作用域;ES6 中,使用 let
和 const
可以创建块级作用域(block scope),块级作用域是被声明变量所属的块,如果在函数外就是全局,如果函数内就是块级或者局部。
function test(a) {
let a = 3;
console.log(a);
}
// 3;
console.log(a);
// Reference Error;
优先级
一个程序中有可能具有相同名称的局部变量和全局变量。 在这种情况下,局部变量将会优先于全局变量。
const outerWear = "T-Shirt";
function myOutfit() {
const outerWear = "sweater";
return outerWear;
}
myOutfit(); // sweater
数组 Array
在 JS 中,array 也是 object,有内置方法和属性。
使用数组(array
)存储多个数据。以左方括号开始定义一个数组,以右方括号结束,里面每个元素之间用逗号隔开。
const myArray = [0, 1, "code"];
多维数组 Multi-Dimensional Array
在其他数组中嵌套数组,这也叫做多维数组(multi-dimensional array)
const myArray = [["html","css"],"javascript"];
索引访问 Index Access
使用索引(indexes)来访问数组中的数据。数组索引与字符串一样使用方括号来表示,不同的是,它们不是指定字符,而是指定数组中的一个条目。 数组索引与字符串索引一样是从 0 开始(zero-based)的。**注意:**数组名与方括号之间不应该有任何空格,比如array [0]
。
const myArray = [50, 60, 70];
var myData = myArray[0]; // 50
访问嵌套数组
与访问嵌套对象类似,数组的方括号可以用来对嵌套数组进行链式访问。
const myPlants = [
{
type: "flowers",
list: [
"rose",
"tulip",
"dandelion"
]
},
{
type: "trees",
list: [
"fir",
"pine",
"birch"
]
}
];
const secondTree = myPlants[1].list[1]; // pine
索引修改 Index Modify
与字符串不同,数组的条目是 可变的 并且可以自由更改,即使数组是用 const
声明的。
const myArray = [18, 64, 99];
myArray[0] = 45; // [45,64,99]
访问多维数组
使用方括号表示法访问数组时,第一个方括号访问的是数组的最外层(第一层),第二个方括号访问的是数组的第二层,以此类推。
const myArray = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[[10, 11, 12], 13, 14],
];
const myData = myArray[2][1]; // 8;
push()
将数据添加到数组末尾的简单方法是 push()
函数。
.push()
接受一个或多个参数(parameters),并把它压入到数组的末尾。
const myArray = [["John", 23], ["cat", 2]];
myArray.push(["dog", 3]); // [["John", 23], ["cat", 2],["dog", 3]]
pop()
.pop()
函数移除数组末尾的元素并返回这个元素。
const myArray = [["John", 23], ["cat", 2]];
let removedFromMyArray = myArray.pop(); // ["cat", 2]
shift()
shift()
移除的是第一个元素。
const myArray = [["John", 23], ["dog", 3]];
const removedFromMyArray = myArray.shift(); // ["John", 23]
unshift()
unshift
(移入)一个元素到数组的头部。
const myArray = [["John", 23], ["dog", 3]];
myArray.unshift(["Paul", 35]); // ["Paul", 35]],[["John", 23],["dog", 3]
splice()
splice()
可以从数组中的任意位置连续删除任意数量的元素。
splice()
最多可以接受 3 个参数。
splice()
接收的前两个参数是整数,表示正在调用的splice()
数组中的元素的索引或位置。 splice()
的第一个参数代表从数组中的哪个索引开始移除元素,而第二个参数表示要从数组中的这个位置开始删除多少个元素。
splice()
不仅会修改调用该方法的数组,还会返回一个包含被移除元素的数组:
const arr = [2, 4, 5, 1, 7, 5, 2, 1];
arr.splice(1, 4);
console.log(arr);
第三个参数可以是一个或多个元素,这些元素会被添加到数组中。 这样,能够便捷地将数组中的一个或多个连续元素换成其他的元素。
function htmlColorNames(arr) {
arr.splice(0, 2, "DarkSalmon", "BlanchedAlmond");
return arr;
}
console.log(htmlColorNames(['DarkGoldenRod', 'WhiteSmoke', 'LavenderBlush', 'PaleTurquoise', 'FireBrick']));
slice()
slice()
不会修改数组,而是会复制,或者说*提取(extract)*给定数量的元素到一个新数组。 slice()
只接收 2 个输入参数:第一个是开始提取元素的位置(索引),第二个是提取元素的结束位置(索引)。 提取的元素中不包括第二个参数所对应的元素。
function forecast(arr) {
return arr.slice(2, 4);
}
console.log(forecast(['cold', 'rainy', 'warm', 'sunny', 'cool', 'thunderstorms']));
展开运算符…
展开运算符复制数组
let thisArray = [true, true, undefined, false, null];
let thatArray = [...thisArray];
展开运算符合并数组
展开语法(spread)的另一个重要用途是合并数组,或者将某个数组的所有元素插入到另一个数组的任意位置。
let thisArray = ['sage', 'rosemary', 'parsley', 'thyme'];
let thatArray = ['basil', 'cilantro', ...thisArray, 'coriander'];
forEach
forEach
方法接收一个函数,该函数会在每一个数组项中执行。该函数的第一个参数是当前数组元素本身,第二个参数是索引(可选参数)。
const fruits = ['kiwi','mango','apple','pear'];
function appendIndex(fruit,index) {
console.log(`${index}.${fruit}`)
}
fruits.forEach(appendIndex);
通常会把 forEach
接收的函数体直接写入函数调用中:
const fruits = ['kiwi','mango','apple','pear'];
fruits.forEach(function(fruit, index) {
console.log(`${index}.${fruit}`)
})
filter()
类似 forEach
,该方法用于每一个数组项,它会根据传入函数返回一个过滤后的数组:
const nums = [100,200,30,40,10,0];
nums.filter(function (item) {
return item > 100;
})
// [200]
map()
该方法根据传入的函数,把某数组的值按照函数规则映射(map)到另一数组:
[0,10,20,30].map(function (num) {
return num ** 2;
}) // [0, 100, 400, 900]
对象 Object
对象(object)本质上是键值对(key-value pair)的集合。 或者说,一系列被映射到唯一标识符的数据就是对象;习惯上,唯一标识符叫做属性(property)或者键(key);数据叫做值(value)。
JS 中常用对象字面量语法 {}
来创建一个对象。
var user = {};
也可以把属性和值以键值对的形式直接声明来创建对象:
const myDog = {
"name": "Good",
"legs": 4,
"tails": 1,
"friends": ["code", "fads"]
};
对象和arrays
类似,区别在于数组使用索引来访问和修改数据,而对象中数据是通过属性
访问的。
对象非常适合用来存储结构化数据。属性都存储为字符串,也可以使用数字作为属性。甚至可以省略单字字符串属性中的引号。
然而,如果对象有非字符串属性的话,JavaScript 会自动将它们转为字符串。
in
JavaScript 提供了两种不同的方式来检查对象是否有某个属性: 一个是通过 hasOwnProperty()
方法,另一个是使用 in
关键字。
users.hasOwnProperty('Alan'); // true
'Alan' in users; // true
for … in 遍历对象
对象是不可迭代的,所以不能直接使用 for
和 for-of
循环。
const car = {
speed: 100,
color: 'blue'
}
for (prop of car) {
console.log(prop);
}
// Uncaught TypeError: car is not iterable
遍历对象中的所有属性, 只需要使用 JavaScript 中的 for…in 语句即可。
for-in
对象遍历对象和其原型的属性;for-of
仅遍历对象自身的属性。
let car = {
speed: 100,
engine: 'true'
}
let car2 = Object.create(car);
for (prop of car2) { console.log(prop); } // Uncaught TypeError: car2 is not iterable
for (prop in car2) {
console.log(prop);
} // color speed engine 顺序不固定
for (key of Object.keys(car2)) {
console.log(key);
} // color
**注意:**对象中的键是无序的,这与数组不同。 因此,一个对象中某个属性的位置,或者说它出现的相对顺序,在引用或访问该属性时是不确定的。
for (let user in users) {
console.log(user);
}
Object.keys()
给 Object.keys()
方法传入一个对象作为参数,来生成包含对象所有键的数组。 这会返回一个由对象中所有属性(字符串)组成的数组。 需要注意的是,数组中元素的顺序是不确定的。
let users = {
Alan: {
age: 27,
online: false
},
Jeff: {
age: 32,
online: true
},
Sarah: {
age: 48,
online: false
},
Ryan: {
age: 19,
online: true
}
};
function getArrayOfUsers(obj) {
return Object.keys(obj);
}
console.log(getArrayOfUsers(users));
// [ 'Alan', 'Jeff', 'Sarah', 'Ryan' ]
遍历对象中的属性和值:
var clothingItem = {
price: 50,
color: 'beige',
material: 'cotton',
season: 'autumn'
}
for (key of Object.keys(clothingItem)) {
console.log(keys, ":", clothingItem[key]);
}
Object.values()
给 Object.values()
方法传入一个对象作为参数,来生成包含对象所有值的数组。 这会返回一个由对象中所有属性的值组成的数组。
const car3 = {
speed: 300,
color: 'yellow'
}
console.log(Object.values(car3)); // [300,'yellow']
Object.entries()
给 Object.entries()
方法传入一个对象作为参数,来生成包含对象所有键值对的数组。
const car4 = {
speed: 400,
color: 'magenta'
}
console.log(Object.entries(car4)); // [ ['speed', 400], ['color', 'magenta'] ]
访问属性 Access Property
访问对象属性有两种方式:点号表示法(.
)和方括号表示法([]
)
点号访问 Dot Notation
如果提前知道要访问的属性名,使用点号表示法是最方便的。
const testObj = {
"hat": "ballcap",
"shirt": "jersey",
"shoes": "cleats"
};
const hatValue = testObj.hat; // ballcap
const shirtValue = testObj.shirt; // jersey
方括号访问 Bracket Notation
访问对象属性的第二种方式是方括号表示法([]
)。这种方法需要把属性作为字符串写入方括号内。如果要访问的属性名中包含空格,就必须使用方括号表示法来获取它的属性值。
const testObj = {
"an entree": "hamburger",
"my side": "veggies",
"the drink": "water"
};
const entreeValue = testObj["an entree"];
const drinkValue = testObj["the drink"];
最后,方括号访问比点号访问的优势是:可以计算表达式。
var arrOfKeys = ['speed', 'altitude', 'color'];
var drone = {
speed: 100,
altitude: 200,
color: 'red'
}
for (var i = 0; i < arrOfKeys.length; i++) {
console.log(drone[arrOfkeys[i]]);
}
/*
100
200
red
*/
变量访问
还可以访问对象上作为变量值存储的属性。 当需要遍历对象的所有属性,或者根据一个变量的值查找对应的属性值时,这种写法尤其适用。
const dogs = {
Fido: "Mutt",
Hunter: "Doberman",
Snoopie: "Beagle"
};
const myDog = "Hunter";
const myBreed = dogs[myDog];
console.log(myBreed); // Doberman
使用这一概念的另一种情况是:属性的名字是在程序运行期间动态收集得到的。
const someObj = {
propName: "John"
};
function propPrefix(str) {
const s = "prop";
return s + str;
}
const someProp = propPrefix("Name");
console.log(someObj[someProp]);
注意,当使用变量名访问属性时,没有 使用引号包裹它,因为使用的是变量的值,而不是变量的名字。
const testObj = {
12: "Namath",
16: "Montana",
19: "Unitas"
};
const playerNumber = 16;
const player = testObj[playerNumber]; // Montana
嵌套访问
通过连续使用点号表示法和方括号表示法来访问对象的嵌套属性。
const myStorage = {
"car": {
"inside": {
"glove box": "maps",
"passenger seat": "crumbs"
},
"outside": {
"trunk": "jack"
}
}
};
const gloveBoxContents = myStorage.car.inside["glove box"]; // maps
更改属性 Update
创建了 JavaScript 对象后,可以随时更新它的属性。 使用点或中括号操作符来更新。
const myDog = {
"name": "Coder",
"legs": 4,
"tails": 1,
"friends": ["freeCodeCamp Campers"]
};
myDog["name"] = "Happy Coder";
添加属性 Add
使用点号来更新,给 JavaScript 对象添加属性。
可以给对象添加多种数据类型的属性,包括函数;如果添加了函数属性,就称为“方法”。
const myDog = {
"name": "Happy Coder",
"legs": 4,
"tails": 1,
"friends": ["freeCodeCamp Campers"]
};
myDog.bark = "woof";
myDog.call = function() {
console.log('hello');
}
删除属性 Delete
可以使用 delete
删除对象的属性
const myDog = {
"name": "Happy Coder",
"legs": 4,
"tails": 1,
"friends": ["freeCodeCamp Campers"],
"bark": "woof"
};
delete myDog.tails;
检查对象属性 Test
用对象的 .hasOwnProperty(propname)
方法来检查对象是否有指定的属性。 .hasOwnProperty()
找到该属性时返回 true
,找不到该属性时返回 false
。
obj.hasOwnProperty(check);
复杂对象 Complex Object
**提示:**数组中有多个 JSON 对象的时候,对象与对象之间要用逗号隔开。
const myMusic = [
{
"artist": "Billy Joel",
"title": "Piano Man",
"release_year": 1973,
"formats": [
"CD",
"8T",
"LP"
],
"gold": true
},
{
"artist": "Mitchell",
"title": "Cocomo",
"release_year": 1900,
"formats": [
"fds",
"Fsdf"
]
}
];
递归 Recursion
递归是函数调用自身的操作。
function sum(arr, n) {
if (n <= 0) return 0;
else return sum(arr, n - 1) + arr[n-1];
}
Math 库
常用的常量
- PI 值:
Math.PI
- 欧拉数 Euler 常量:
Math.E
- 2 的自然对数:
Math.LN2
舍入方法
Math.ceil()
无论小数部分如何,向上舍入到最接近的整数Math.floor()
无论小数部分如何,向下舍去到最接近的整数Math.round()
如果小数部分大于.5
,向上舍入到最接近的整数 ,否则向下舍去最接近整数Math.trunc()
去掉小数部分,仅留下整数部分
console.log(Math.ceil(2.3)); // 3
console.log(Math.ceil(2.7)); // 3
console.log(Math.floor(2.3)); // 2
console.log(Math.floor(2.7)); // 2
console.log(Math.round(2.3)); // 2
console.log(Math.round(2.7)); // 3
console.log(Math.trunc(2.3)); // 2
console.log(Math.trunc(2.7)); // 2
随机分数 Random
随机数非常适合用来创建随机行为。
在 JavaScript 中,可以用 Math.random()
生成一个在0
(包括 0)到 1
(不包括 1)之间的随机小数。
function randomFraction() {
return Math.random();
}
随机整数
- 用
Math.random()
生成一个随机小数。 - 把这个随机小数乘以
20
。 - 用
Math.floor()
向下取整,获得它最近的整数。
记住 Math.random()
永远不会返回 1
。同时因为是在向下取整,所以最终获得的结果不可能有 20
。 这确保了我们获得了一个在 0
到 19
之间的整数。
function randomWholeNum() {
return Math.floor(Math.random() * 10);
}
生成某个范围内的数字
需要定义一个最小值 min
和一个最大值 max
。
function randomRange(myMin, myMax) {
return Math.floor(Math.random() * (myMax - myMin + 1)) + myMin;
}
parseInt
parseInt()
函数解析一个字符串返回一个整数。
如果字符串中的第一个字符不能转换为数字,则返回 NaN
。
function convertToInteger(str) {
return parseInt(str);
}
convertToInteger("56");
它还可以传入第二个参数,指定了字符串中数字的基数。 基数可以是 2 到 36 之间的整数。
parseInt(string, radix);
function convertToInteger(str) {
return parseInt(str, 2);
}
convertToInteger("10011");
数学计算方法 Arithmetic and calculus
- 幂运算:
Math.pow()
- 开算术平方根:
Math.sqrt()
- 开立方根:
Math.cbrt()
- 取绝对值:
Math.abs()
- 取对数:
Math.log()
Math.log2()
Math.log10()
- 取最值:
Math.min()
Math.max()
- 三角(Trigonometric)计算:
Math.sin()
Math.cos()
Math.tan()
Math.pow(2,3)
8
Math.sqrt(4)
2
Math.cbrt(8)
2
Math.abs(-2)
2
Math.log(4)
1.3862943611198906
Math.log2(4)
2
Math.log10(100)
2
Math.log(2.7)
0.9932517730102834
Math.sin(4/5)
0.7173560908995228
Math.cos(4/5)
0.6967067093471654
Math.tan(4/5)
1.0296385570503641