function A(){ console.log('called A'); return false; }
function B(){ console.log('called B'); return true; }
console.log( A() && B() );
// logs "called A" due to the function call,
// then logs false (which is the resulting value of the operator)
console.log( B() || A() );
// logs "called B" due to the function call,
// then logs true (which is the resulting value of the operator)
5. 注意
与运算 && 的优先级比或运算 || 要高。
所以代码 a && b || c && d 完全跟 && 表达式加了括号一样:(a && b) || (c && d)。
十二、循环:while 和 for
1. while 循环
详细可以阅读《MDN while》 。
while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。
如:
var n = 0;
var x = 0;
while (n < 3) {
n++;
x += n;
}
所谓 case 分组,就是与多个 case 分支共享同一段代码,如下面例子中 case 1 和 case 2:
let a = 2;
switch (a) {
case 1: // (*) 下面这两个 case 被分在一组
case 2:
console.log('case is 1 or 2!');
break;
case 3:
console.log('case is 3!');
break;
default:
console.log('The result is default.');
}
// 'case is 1 or 2!'
3. 注意点
expression 表达式的值与 case 值的比较是严格相等:
function f(n){
let a ;
switch(n){
case 1:
a = 'number';
break;
case '1':
a = 'string';
break;
default:
a = 'default';
break;
}
console.log(a)
}
f(1); // number
f('1'); // string
如果没有 break,程序将不经过任何检查就会继续执行下一个 **case** :
let a = 2 + 2;
switch (a) {
case 3:
console.log( 'Too small' );
case 4:
console.log( 'Exactly!' );
case 5:
console.log( 'Too big' );
default:
console.log( "I don't know such values" );
}
// Exactly!
// Too big
// I don't know such values
**default**放在 **case**之上不影响匹配:
function f(n){
switch (n) {
case 2:
console.log(2);
break;
default:
console.log('default')
break;
case 1:
console.log('1');
break;
}
}
f(1); // 1
f(2); // 2
f(3); // default
switch 语句中存在 let / const重复声明问题:
// 以下定义会报错
function f(n){
switch(n){
case 1:
let msg = 'hello';
console.log(1);
break;
case 2:
let msg = 'leo';
break;
default:
console.log('default');
break;
}
}
// Error: Uncaught SyntaxError: Identifier 'msg' has already been declared
这是由于两个 let 处于同一个块级作用域,所以它们被认为是同一变量名的重复声明。
解决方式,只需要将 case 语句包装在括号内即可解决:
function f(n){
switch(n){
case 1:{ // added brackets
let msg = 'hello';
console.log(msg);
break;
}
case 2: {
let msg = 'leo';
console.log(msg);
break;
}
default:
console.log('default');
break;
}
}
let fun = function(){
console.log(arguments);
console.log(arguments.length);
}
fun('leo');
// Arguments ["leo", callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 1
以一个实际示例介绍,实现将任意数量参数连接成一个字符串,并输出的函数:
let argumentConcat = function(separator){
let result = '', i;
for(i = 1; i < arguments.length; i ++){
result += arguments[i] + separator;
}
return result;
}
argumentConcat(',', 'leo', 'pingan'); //"leo,pingan,"
5. 函数返回值
在函数任意位置,指定 return 指令来停止函数的执行,并返回函数指定的返回值。
let sum = function(a, b){
return a + b;
};
let res = sum(1, 2);
console.log(res); // 3
// 函数表达式
let fun = function(){};
// 函数声明
function fun(){}
2. 创建时机差异
函数表达式会在代码执行到达时被创建,并且仅从那一刻可用。
而函数声明被定义之前,它就可以被调用。
// 函数表达式
fun(); // Uncaught ReferenceError: fun is not defined
let fun = function(){console.log('leo')};
// 函数声明
fun(); // "leo"
function fun(){console.log('leo')};
// 有1个参数
let f = v => v;
// 等同于
let f = function (v){return v};
// 有多个参数
let f = (v, i) => {return v + i};
// 等同于
let f = function (v, i){return v + i};
// 没参数
let f = () => 1;
// 等同于
let f = function (){return 1};
let arr = [1,2,3,4];
arr.map(ele => ele + 1); // [2, 3, 4, 5]
2. 注意点
箭头函数不存在this;
箭头函数不能当做构造函数,即不能用new实例化;
箭头函数不存在arguments对象,即不能使用,可以使用rest参数代替;
箭头函数不能使用yield命令,即不能用作Generator函数。
一个简单的例子:
function Person(){
this.age = 0;
setInterval(() => {
this.age++;
}, 1000);
}
var p = new Person(); // 定时器一直在执行 p的值一直变化
最近原创文章🔥:
前言
最近与部门老大一起面试了许多前端求职者,其中想换个学习氛围较好的人占多数,但良好的学习氛围也是需要一点点营造出来的🌺。
为此我们组建了我们团队内部的“现代 JavaScript 突击队”,第一期学习内容为《现代 JavaScript 教程》系列,帮助小组成员系统地进行学习巩固,并让大家养成系统性学习和输出学习总结的学习方式。
本文作为我输出的第一部分学习总结,希望作为一份自测清单,帮助大家巩固知识,温故知新。
这里也下面分享我们学习小组的“押金制度”和“押金记录表”🍀
接下来开始分享自测清单的内容。
一、Hello World!
1. 脚本引入方式
JavaScript 脚本引入方式有两种:
<script>
标签插入脚本;<script>
标签src
设置脚本地址。2. script 标签属性
<script>
标签有以下常用属性:2.1 src
src
:指定外部脚本的URI, 如果设置了src
特性,script 标签内容将会被忽略;2.2 type
type
:指定引用脚本的语言,属性值为 MIME 类型,包括text/javascript
,text/ecmascript
,application/javascript
, 和application/ecmascript
。如果没有定义这个属性,脚本会被视作JavaScript。ES6 新增了属性值
module
,代码会被当做 JavaScript 模块。2.3 async
async
规定一旦脚本可用,则会异步执行。 注意:async 属性仅适用于外部脚本(只有在使用 src 属性时)。 有多种执行外部脚本的方法: 如果async="async"
:脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行); 如果不使用async
且defer="defer"
:脚本将在页面完成解析时执行; 如果既不使用async
也不使用defer
:在浏览器继续解析页面之前,立即读取并执行脚本;2.4 defer
defer
属性规定是否对脚本执行进行延迟,直到页面加载为止。如果您的脚本不会改变文档的内容,可将 defer 属性加入到
<script>
标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。详细介绍可以阅读《MDN
<script>
章节 》。二、代码结构
1. 语句
语句是执行行为(action)的语法结构和命令。如:
alert('Hello, world!')
这样可以用来显示消息的语句。2. 分号
存在分行符时,多数情况下可以省略分号。但不全是,比如:
建议新人最好不要省略分号。
3. 注释
单行注释以两个正斜杠字符
//
开始。多行注释以一个正斜杠和星号开始
“/*”
并以一个星号和正斜杆结束“*/”
。三、现代模式,"use strict"
1. 作用
JavaScript 的严格模式是使用受限制的 JavaScript 的一种方式,从而隐式地退出“草率模式”。
"use strict"
指令将浏览器引擎转换为“现代”模式,改变一些内建特性的行为。2. 使用
通过在脚本文件/函数开头添加
"use strict";
声明,即可启用严格模式。 全局开启严格模式:函数内开启严格模式:
3. 注意点
"use strict"
需要定义在脚本最顶部(函数内除外),否则严格模式可能无法启用。4. 体验
启用
"use strict"
后,为未定义元素赋值将抛出异常:启用
"use strict"
后,试图删除不可删除的属性时会抛出异常:详细介绍可以阅读《MDN 严格模式章节 》。
四、变量
1. 介绍
变量是数据的“命名存储”。
2. 使用
目前定义变量可以使用三种关键字:var / let / const。三者区别可以阅读《let 和 const 命令》 。
3. 命名建议
变量命名有 2 个限制:
$
和_
。const PI = 3.141592
;userName
或者shoppingCart
。4. 注意点
leo
与Leo
是不同的;let 平安 = "leo"
;a
、b
、c
这种缩写。五、数据类型
JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据:
详细介绍可以阅读《MDN JavaScript 数据类型和数据结构 》。
1. 八大数据类型
前七种为基本数据类型,也称为原始类型(值本身无法被改变),而
object
为复杂数据类型。 八大数据类型分别是:number
用于任何类型的数字:整数或浮点数,在 ±2 范围内的整数。bigint
用于任意长度的整数。string
用于字符串:一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。boolean
用于true
和false
。null
用于未知的值 —— 只有一个null
值的独立类型。undefined
用于未定义的值 —— 只有一个undefined
值的独立类型。symbol
用于唯一的标识符。object
用于更复杂的数据结构。 每个类型后面会详细介绍。2. 检测数据类型
通过
typeof
运算符检查:typeof x
或者typeof(x)
。"string"
。typeof null
会返回"object"
—— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个object
。六、类型转换
JavaScript 变量可以转换为新变量或其他数据类型:
1. 字符串转换
通过全局方法
String()
将其他类型数据(任何类型的数字,字母,布尔值,对象)转换为 String 类型:2. 数值转换
通过以下几种方式能将其他类型数据转换为 Number 类型:
+
Number
方法3. 布尔值转换
转换规则如下:
0
、空字符串、null
、undefined
和NaN
)将变为false
。true
。4. 小结
七、运算符
1、运算符概念
常见运算符如加法
+
、减法-
、乘法*
和除法/
,举一个例子,来介绍一些概念:其中:
1 + 2
中,1
和2
为 2 个运算元,左运算元1
和右运算元2
,即运算元就是运算符作用的对象。1 + 2
运算式中包含 2 个运算元,因此也称该运算式中的加号+
为 二元运算符。+18
中的加号+
对应只有一个运算元,则它是 一元运算符 。2、+ 号运算符
3、运算符优先级
圆括号
( … )
成员访问
… . …
需计算的成员访问
… [ … ]
new
(带参数列表)new … ( … )
… ( … )
?.
new …
… ++
… --
! …
~ …
+ …
- …
++ …
-- …
typeof …
void …
delete …
await …
… ** …
… * …
… / …
… % …
… + …
… - …
… << …
… >> …
… >>> …
… < …
… <= …
… > …
… >= …
… in …
… instanceof …
… == …
… != …
… === …
… !== …
… & …
… ^ …
… | …
… && …
… || …
… ? … : …
… = …
… += …
… -= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
yield …
yield* …
...
…… , …
八、值的比较
1. 常见比较
在 JS 中的值的比较与数学很类型:
a>b
/a<b
/a>=b
/a<=b
;(图片来自:《MDN JavaScript 中的相等性判断》)
!=
/!==
。2. 相等性判断(Object.is())
另外 ES6 新增 Object.is 方法判断两个值是否相同,语法如下:
以下任意项成立则两个值相同:
undefined
null
true
或者都是false
+0
-0
NaN
NaN
外的其它同一个数字 使用示例:兼容性 Polyfill 处理:
3. null 与 undefined 比较
对于相等性判断比较简单:
对于其他比较,它们会先转换位数字:
null
转换为0
,undefied
转换为NaN
。需要注意:
null == 0; // false
这里是因为:undefined
和null
在相等性检查==
中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。第 1、2 行都返回
false
是因为undefined
在比较中被转换为了NaN
,而NaN
是一个特殊的数值型值,它与任何值进行比较都会返回false
。 第 3 行返回false
是因为这是一个相等性检查,而undefined
只与null
相等,不会与其他值相等。九、alert / prompt / confirm
1. alert
显示一个警告对话框,上面显示有指定的文本内容以及一个“确定”按钮。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。
message
是要显示在对话框中的文本字符串,如果传入其他类型的值,会转换成字符串。2. prompt
显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。 当点击确定返回文本,点击取消或按下 Esc 键返回
null
。 语法如下:result
用来存储用户输入文字的字符串,或者是 null。text
用来提示用户输入文字的字符串,如果没有任何提示内容,该参数可以省略不写。value
文本输入框中的默认值,该参数也可以省略不写。不过在 Internet Explorer 7 和 8 中,省略该参数会导致输入框中显示默认值"undefined"。3. confirm
Window.confirm()
方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框。 注意:弹出模态框,并暂停脚本,直到用户点击“确定”按钮。 语法如下:十、条件运算符:if 和 '?'
1. if 语句
当 if 语句当条件表达式,会将表达式转换为布尔值,当为 truthy 时执行里面代码。 转换规则如:
0
、空字符串""
、null
、undefined
和NaN
都会被转换成false
。因为他们被称为 “falsy” 值。true
,所以它们被称为 “truthy”。2. 三元运算符
条件(三元)运算符是 JavaScript 仅有的使用三个操作数的运算符。一个条件后面会跟一个问号(?),如果条件为 truthy ,则问号后面的表达式A将会执行;表达式A后面跟着一个冒号(:),如果条件为 falsy ,则冒号后面的表达式B将会执行。本运算符经常作为
[if](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)
语句的简捷形式来使用。 语法:十一、逻辑运算符
详细可以阅读《MDN 逻辑运算符》 。
1. 运算符介绍
_expr_
可能是任何一种类型, 不一定是布尔值):&&
)_expr1_ && _expr2_
expr**1**
可转换为true
,则返回expr**2**
;否则,返回expr**1**
。||
)_expr1_ || _expr2_
expr**1**
可转换为true
,则返回expr**1**
;否则,返回expr**2**
。!
)!_expr_
expr
可转换为true
,则返回false
;否则,返回true
。如果一个值可以被转换为
true
,那么这个值就是所谓的 truthy,如果可以被转换为false
,那么这个值就是所谓的 falsy。 会被转换为false
的表达式有:null
;NaN
;0
;""
or''
or ````);undefined
。 尽管&&
和||
运算符能够使用非布尔值的操作数, 但它们依然可以被看作是布尔操作符,因为它们的返回值总是能够被转换为布尔值。如果要显式地将它们的返回值(或者表达式)转换为布尔值,请使用双重非运算符(即!!
)或者Boolean构造函数。 JavaScript 里有三个逻辑运算符:||
(或),&&
(与),!
(非)。2. 运算符示例
3. 布尔值转换规则
4. 短路取值
由于逻辑表达式的运算顺序是从左到右,也可以用以下规则进行"短路"计算:
(some falsy expression) && (_expr)_
短路计算的结果为假。(some truthy expression) || _(expr)_
短路计算的结果为真。 短路意味着上述表达式中的expr部分不会被执行,因此expr的任何副作用都不会生效(举个例子,如果expr是一次函数调用,这次调用就不会发生)。造成这种现象的原因是,整个表达式的值在第一个操作数被计算后已经确定了。看一个例子:5. 注意
与运算
&&
的优先级比或运算||
要高。 所以代码a && b || c && d
完全跟&&
表达式加了括号一样:(a && b) || (c && d)
。十二、循环:while 和 for
1. while 循环
详细可以阅读《MDN while》 。 while 语句可以在某个条件表达式为真的前提下,循环执行指定的一段代码,直到那个表达式不为真时结束循环。 如:
当循环体为单行时,可以不写大括号:
2. do...while 循环
详细可以阅读《MDN do...while》 。
do...while
语句创建一个执行指定语句的循环,直到condition
值为 false。在执行statement
后检测condition
,所以指定的statement
至少执行一次。 如:3. for 循环
详细可以阅读《MDN for》 。
for
语句用于创建一个循环,它包含了三个可选的表达式,这三个表达式被包围在圆括号之中,使用分号分隔,后跟一个用于在循环中执行的语句(通常是一个块语句)。 语法如:示例:
i = 0
i < 3
alert(i)
i++
4. 可选的 for 表达式
for
语句头部圆括号中的所有三个表达式都是可选的。5. break 语句
详细可以阅读《MDN break》 。 break 语句中止当前循环,
switch
语句或label
语句,并把程序控制流转到紧接着被中止语句后面的语句。 在 while 语句中:另外,也可以为代码块做标记,并在 break 中指定要跳过的代码块语句的 label:
需要注意的是:break 语句需要内嵌在它所应用的标签或代码块中,否则报错:
6. continue 语句
continue 声明终止当前循环或标记循环的当前迭代中的语句执行,并在下一次迭代时继续执行循环。 与
break
语句的区别在于, continue 并不会终止循环的迭代,而是:while
循环中,控制流跳转回条件判断;for
循环中,控制流跳转到更新语句。 注意:continue 也必须在对应循环内部,否则报错。带 label:
7. 注意
禁止
break/continue
在 ‘?’ 的右边:这样会提示语法错误。 请注意非表达式的语法结构不能与三元运算符
?
一起使用。特别是break/continue
这样的指令是不允许这样使用的。8. 总结
三种循环:
while
—— 每次迭代之前都要检查条件。do..while
—— 每次迭代后都要检查条件。for (;;)
—— 每次迭代之前都要检查条件,可以使用其他设置。 通常使用while(true)
来构造“无限”循环。这样的循环和其他循环一样,都可以通过break
指令来终止。 如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么可以使用continue
指令。break/continue
支持循环前的标签。标签是break/continue
跳出嵌套循环以转到外部的唯一方法。十三、"switch" 语句
switch
语句用来将表达式的值与 case 语句匹配,并执行与情况对应的语句。switch
语句可以替代多个 if 判断,为多个分支选择的情况提供一个更具描述性的方式。1. 语法
switch
语句至少包含一个case
代码块和一个可选的default
代码块:当
expression
表达式的值与value1
匹配时,则执行其中代码块。 如果没有case
子句匹配,则会选择default
子句执行,若连default
子句都没有,则直接执行到switch
结束。2. 使用 case 分组
所谓 case 分组,就是与多个 case 分支共享同一段代码,如下面例子中
case 1
和case 2
:3. 注意点
expression
表达式的值与case
值的比较是严格相等:break
,程序将不经过任何检查就会继续执行下一个**case**
:**default**
放在**case**
之上不影响匹配:switch
语句中存在let
/const
重复声明问题:这是由于两个
let
处于同一个块级作用域,所以它们被认为是同一变量名的重复声明。 解决方式,只需要将case
语句包装在括号内即可解决:十四、函数
函数可以让一段代码被多次调用,避免重复代码。 如之前学习到的一些内置函数:
alert(msg)
/prompt(msg, default)
/confirm(quesyion)
等。1. 函数定义
定义函数有两种方式:函数声明和函数表达式。
1.1 函数声明
如定义一个简单
getUser
函数:通过函数声明来定义函数时,需要由以下几部分组成:
getUser
;name
;return 'hello ' + name
。1.2 函数表达式
类似声明变量,还是以
getUser
为例:另外,函数表达式也可以提供函数名,并用于函数内部指代函数本身:
2. 函数调用
当定义一个函数后,它并不会自动执行,而是需要使用函数名称进行调用,如上面例子:
只要注意: 使用 函数表达式 定义函数时,调用函数的方法必须写在定义之后,否则报错:
而使用 函数声明 则不会出现该问题:
原因就是:函数提升仅适用于函数声明,而不适用于函数表达式。
3. 函数中的变量
在函数中,可以使用局部变量和外部变量。
3.1 局部变量
函数中声明的变量只能在该函数内可见。
3.2 全局变量
函数内可以使用外部变量,并且可以修改外部变量的值。
当函数内也有与外部变量名称相同的变量,会忽略外部变量:
4. 函数参数
从ECMAScript 6开始,有两个新的类型的参数:默认参数,剩余参数。
4.1 默认参数
若函数没有传入参数,则参数默认值为
undefined
,通常设置参数默认值是这样做的:4.2 剩余参数
可以将参数中不确定数量的参数表示成数组,如下:
既然讲到参数,那就不能少了 arguments 对象。
4.3 arguments 对象
函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,我们可以使用 arguments 对象获取函数的所有参数:
以一个实际示例介绍,实现将任意数量参数连接成一个字符串,并输出的函数:
5. 函数返回值
在函数任意位置,指定
return
指令来停止函数的执行,并返回函数指定的返回值。默认空值的
return
或没有return
的函数返回值为undefined
。十五、函数表达式
函数表达式是一种函数定义方式,在前面章节中已经介绍到了,这个部分将着重介绍 函数表达式 和 函数声明 的区别:
1. 语法差异
2. 创建时机差异
函数表达式会在代码执行到达时被创建,并且仅从那一刻可用。 而函数声明被定义之前,它就可以被调用。
3. 使用建议
建议优先考虑函数声明语法,它能够为组织代码提供更多灵活性,因为我们可以在声明函数前调用该函数。
十六、箭头函数
本章节简单介绍箭头函数基础知识,后面章节会完整介绍。 函数箭头表达式是ES6新增的函数表达式的语法,也叫胖箭头函数,变化:更简洁的函数和
this
。1. 代码更简洁
2. 注意点
this
;new
实例化;arguments
对象,即不能使用,可以使用rest
参数代替;yield
命令,即不能用作Generator函数。 一个简单的例子:总结
本文作为《初中级前端 JavaScript 自测清单》第一部分,介绍的内容以常用基础知识为主,并在学习资料中,将知识点结合实际开发中遇到的场景进行展开介绍。希望能帮助大家自测自己的 JavaScript 水平并查缺补漏,温故知新。