【1】语法
(1)声明变量(let-var-const)
- 变量提升:
- 是JavaScript引擎在代码执行前将变量的声明部分提升到作用域顶部的行为。
- 尽管变量的声明被提升了,变量的赋值(即初始化)仍然保留在原来的位置。因此,如果在初始化之前使用变量,其值将为
undefined
。
- 暂时性死区(Temporal Dead Zone, TDZ)
- 在
let
或const
声明的变量被初始化之前,如果尝试访问它们,会抛出一个ReferenceError
。 - 这个区域被称为暂时性死区。
- var存在变量提上不会报错,但是应该少用
- 在
- var
ES5
中引入的变量声明方式,它声明的变量具有函数作用域或全局作用域,并且存在变量提升现象。- 这意味着
var
声明的变量,无论实际声明在何处,都会被提升到其所在作用域的最顶部。此外,var
声明的变量可以被重新赋值。
var x = 10;
function testVar() {
console.log(x); // undefined,因为变量提升,但此时x还没有被赋值
var x = 20;
console.log(x); // 20
}
testVar();
console.log(x) //10
- let
ES6
中引入的变量声明方式,它声明的变量具有块级作用域(即大括号{}
内),并且不会存在变量提升。let
声明的变量在声明之前使用会抛出错误。同样,let
声明的变量可以被重新赋值。let
是 ES6 中引入的一个关键字,用于声明变量。- 与
var
相比,let
声明的变量具有块级作用域,变量只在它被声明的代码块{}
内有效。
let y = 10;
if (true) {
// console.log(y) // 不存在跨域提升,直接报错
let y = 20; // 不会与外部的y冲突,因为具有块级作用域
console.log(y); // 20
}
console.log(y); // 10
- const
ES6
中引入的,它用于声明一个只读的常量。一旦一个变量被const
声明,它的值就不能被重新赋值- 但如果该值是一个对象或数组,那么其内部属性或元素仍然可以被修改。
const
也具有块级作用域。
const z = 10;
// z = 20; // 报错,不能被重新赋值
const obj = {key: 'value'};
obj.key = 'new value'; // 内部元素可以修改
// obj = {anotherKey: 'value'}; // 报错,整体不能被直接修改
- 小结:
let
和const
提供了块级作用域。let
声明的变量可以被重新赋值。const
声明的常量(或称为不可重新赋值的变量)一旦被赋值后,就不能再被重新赋值(但可以修改对象或数组的内部状态)。- 在声明之前访问
let
或const
声明的变量会导致ReferenceError
,这是由暂时性死区导致的。
(2)箭头函数
-
语法
-
箭头函数是ES6中引入的一种新的函数表达式。它的语法比传统的函数表达式更简洁,且有一些特殊的行为。
-
(parameters) => { functionBody }
-
parameters
:箭头函数的参数列表。 -
=>
:箭头,表示这是一个箭头函数。 -
functionBody
:箭头函数的主体。
-
-
-
示例讲解
-
箭头函数和传统的函数表达式
-
相比于传统的函数表达式,箭头函数没有
function
关键字,这使得代码更简洁。 -
当函数只有一个参数传递时,方法的括号也可省略
-
当函数只用一个return值时,return关键字和大括号也可以省略
-
// 传统函数表达式 const testFunc = function(x){return x * x} //箭头函数表达式 const tetsFunc = (x) => {return x * x} const testFunc = x => x * x
-
-
箭头函数没有自己的this,即不绑定this
-
箭头函数不会创建自己的
this
值,而是捕获其所在上下文的this
值。 -
这使得在回调函数、事件处理器和定时器中使用箭头函数更加安全和方便。
-
const obj = { value: 10, getValue: function() { // 等待一秒输出log内容 setTimeout(() => { console.log(this.value); // 输出 10,因为箭头函数捕获了外层函数的this值 }, 1000); } }; obj.getValue();
-
-
箭头函数没有arguments对象
-
箭头函数没有
arguments
对象,但可以使用剩余参数(rest parameters)来达到类似的效果。 -
const sum = (...args) => args.reduce((a, b) => a + b, 100); let num = sum(1, 2, 3, 4); console.log(num)
-
-
箭头函数不能作为构造函数
-
箭头函数没有
prototype
属性,因此不能用作构造函数。 -
const Arrow = () => {}; const arrowInstance = new Arrow(); // 抛出错误 // Uncaught TypeError: Arrow is not a constructor
-
-
(3)箭头函数和this
- 箭头函数内部的
this
- 箭头函数不会创建自己的
this
上下文,因此它捕获其所在(即定义它的位置)的外部函数的this
值,作为自己的this
值。
- 箭头函数不会创建自己的
function Outer() {
this.value = 'bruce';
this.innerFunc = () => {
console.log(this.value); // bruce
};
}
const outer = new Outer();
outer.innerFunc();
-
this 指向问题分析
-
在全局上下文中使用(this是window对象)
-
console.log(this); // window 对象
-
-
普通函数内调用(this是window对象)
-
function normalFunction() { console.log(this); // window 对象 } normalFunction();
-
-
作为对象方法调用(this是当前的对象)
-
const obj = { value: 'hello', method: function () { console.log(this); } }; obj.method();
-
-
在node中直接调用(this是全局对象global)
-
console.log(this)
-
-
(4)模板字符串
-
在ES6中,模板字符串是一种新的字符串语法,用反引号(``)包围字符串内容。
-
模板字符串可以包含变量,这些变量会被解析并替换为实际的值。
-
let name = 'bruce' let info = `My name is:${name}`
-
举例来说,假设你有一个变量
name
,其值为"bruce"
,那么当你使用模板字符串let info = My name is:${name}
;时,变量info
的值将会是"My name is:bruce"
。
-
-
特点:
- 可以在字符串中直接插入变量,使代码更加简洁易读。
- 可以跨行书写字符串,不再需要使用字符串连接符(+)。
- 支持在字符串中使用表达式,比如
${2 + 2}
会被计算为4
。
(5)解构赋值
-
解构赋值对象
-
解构赋值允许您从对象中提取属性,并将它们赋值给不同的变量。
-
这避免了多次使用点操作符(
.
)或方括号([]
)来获取对象属性的值。 -
let user = {name: 'bruce', age: 1}; // let name = user.name; // let age = user.age; let {name, age, hobby, a = 10} = user; // 没有bobby所以是undefined // 没有a但是又默认是所以a是10 console.log(name, age, hobby, a); // bruce 1 undefined 10
-
-
解构赋值数组
-
对于数组,解构赋值允许直接从数组中提取元素
-
按照索引位置直接赋值
-
如果解构的变量数量超过数组的长度,多余的变量会被赋值为
undefined
。 -
let listInfo = [11, 22]; let [a, b, c] = listInfo; console.log(a, b, c); // 11 22 undefined
-
-
交换变量
-
当使用解构赋值时,还可以很方便地交换两个变量的值
-
let a = 10; let b = 20; [a, b] = [b, a]; console.log(a, b); // 20 10
-
-
解构函数返回值
-
当函数返回一个对象时,可以使用解构赋值来直接访问该对象的属性。
-
function handleInfo() { return {name: 'bruce', age: 19}; } let {name = '1', age} = handleInfo(); // 如果返回的对象中没有name属性,则name的值为默认值'1' console.log(name, age); // bruce 19
-
(6)展开运算
-
展开运算符
...
- 将可迭代对象的元素或属性展开为多个参数或元素。
- 可以简单理解为python的*args
-
对象展开
-
在对象中使用展开运算符,可以将源对象的所有可枚举属性复制到目标对象中。
-
let userDetail = {age: 19, hobby: ['swimming', 'running']}; let userInfo = {name: 'bruce', ...userDetail}; console.log(userInfo); // {name: 'bruce', age: 19, hobby: Array(2)}
-
-
数组展开
-
在数组中使用展开运算符,可以将一个数组的元素插入到另一个数组的指定位置。
-
类似于python中的两个列表相加
-
let list1 = [1, 2, 3]; let list2 = [...list1, 4, 5, 6]; console.log(list2); // [1, 2, 3, 4, 5, 6]
-
-
函数参数展开
-
在函数调用时,可以使用展开运算符将一个数组的元素作为单独的参数传递给函数。
-
function addFunc(a, ...b) { let c = b.reduce((acc, item) => acc + item, a); console.log(c); } addFunc(1, 2, 3, 4);
-
-
小结:
- 展开运算符为我们在处理对象和数组时提供了极大的灵活性。
- 无论是复制对象属性、合并数组,还是将数组元素作为参数传递给函数,展开运算符都能简化代码并提高效率。
(7)模块化
-
默认导出和导入
-
默认导出:每个模块中只能有一个默认导出。
-
默认导出的成员可以是任何类型的值(变量、函数、对象、类等)。
-
在导入时,可以使用自定义的名称来接收这个导出的成员,script标签需要指定类型type为module。
-
未导出的任何类型变量都不能被导入
-
// 导出 // utils.js let name = 'bruce' let age = 18 function add(a, b) { return a + b } export default { name, add }
-
// 导入 <script type="module"> import utils from "./test/utils.js"; console.log(utils.name) // bruce console.log(utils.age) // undefined console.log(utils.add(1, 1)) // 2 </script>
-
-
-
命名导出和导入
-
命名导出:每个模块中可以有多个命名导出。
-
每个导出的成员都需要一个名字,这个名字在导入时也需要被使用,基本都需要
{}
。 -
// 导出 // utils.js export let name = 'bruce' export function add(a, b) { return a + b } export const age = 10
-
// 导出方式一: <script type="module"> import {add, age} from './test/utils.js' console.log(add(1, 1)) // 2 console.log(age) // 10 </script>
-
// 导出方式二:全部导入起别名 <script type="module"> import * as info from './test/utils.js' console.log(info.add(1, 1)) //2 console.log(info.age) //10 </script>
-
// 导出方式二:起别名 <script type="module"> import {add as myAdd} from './test/utils.js' console.log(myAdd(1, 1)) // 2 </script>
-
-
index.js
文件-
如果文件夹下的js文件叫做
index.js
,那么导入就可以省略/index.js
,只需要导入到文件夹这层就可以 -
无论是命名导入还是默认导入
-
# 导入 <script type="module"> import * as info from './test' console.log(info.add(1, 1)) //2 console.log(info.age) //10 </script>
-
【2】JS中循环遍历
(1)遍历数组
- for循环:基于索引遍历数组。
const array = ['a', 'b', 'c'];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
- for…of循环:ES6中引入的,基于迭代取值
const array = ['a', 'b', 'c'];
for (let value of array) {
console.log(value);
}
- forEach方法:数组内置方法,遍历每个元素
const array = ['a', 'b', 'c'];
array.forEach(function(value) {
console.log(value);
});
- map方法:数组内置方法,根据指定方法,创建一个新数组
const array = [1, 3, 5];
const newArray = array.map(value => value * value);
console.log(newArray); // [1, 9, 25]
(2)遍历对象
- for…in循环:遍历对象的可枚举属性(包括原型链上的属性)。
const obj = {name: 'bruce', age: 18};
for (let key in obj) {
console.log(key, obj[key]);
}
// name bruce
// age 18
- Object.keys方法:对象方法,返回对象的所有键值,和for…in一样
const obj = {name: 'bruce', age: 18};
Object.keys(obj).forEach(key => {
console.log(key, obj[key]);
});
- Object.values方法:对象方法,只返回对象的所有值,没有键
const obj = {name: 'bruce', age: 18};
Object.values(obj).forEach(function (value) {
console.log(value)
})
// bruce
// 18