【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 }
-
// 导入 import utils from "./test/utils.js"; console.log(utils.name) // bruce console.log(utils.age) // undefined console.log(utils.add(1, 1)) // 2
-
命名导出和导入
-
命名导出:每个模块中可以有多个命名导出。
-
每个导出的成员都需要一个名字,这个名字在导入时也需要被使用,基本都需要{}。
-
// 导出 // utils.js export let name = 'bruce' export function add(a, b) { return a + b } export const age = 10
-
// 导出方式一: import {add, age} from './test/utils.js' console.log(add(1, 1)) // 2 console.log(age) // 10
-
// 导出方式二:全部导入起别名 import * as info from './test/utils.js' console.log(info.add(1, 1)) //2 console.log(info.age) //10
-
// 导出方式二:起别名 import {add as myAdd} from './test/utils.js' console.log(myAdd(1, 1)) // 2
-
index.js文件
-
如果文件夹下的js文件叫做index.js,那么导入就可以省略/index.js,只需要导入到文件夹这层就可以
-
无论是命名导入还是默认导入
-
-
# 导入 import * as info from './test' console.log(info.add(1, 1)) //2 console.log(info.age) //10
【2】JS中循环遍历
(1)遍历数组
- for循环:基于索引遍历数组。
const array = ['a', 'b', 'c']; for (let i = 0; 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
- Object.values方法:对象方法,只返回对象的所有值,没有键
- Object.keys方法:对象方法,返回对象的所有键值,和for…in一样
- for…in循环:遍历对象的可枚举属性(包括原型链上的属性)。
- map方法:数组内置方法,根据指定方法,创建一个新数组
- forEach方法:数组内置方法,遍历每个元素
- for…of循环:ES6中引入的,基于迭代取值
- for循环:基于索引遍历数组。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 箭头函数不会创建自己的 this 上下文,因此它捕获其所在(即定义它的位置)的外部函数的 this 值,作为自己的 this 值。
- 箭头函数内部的 this
-
-
-
-
-
-
-
-
- 小结:
- const
- let