javaScript学习

一、基础

1.1 简洁

  • JavaScript:是一种运行在浏览器中的解释型的编程语言

1.2 数据类型

  • 分类
    • 基本数据类型:Number/String/Boolean/undefined/null/Symbol/BigInt
      • Symbol:唯一标识符(解决命名冲突)
      • BigInt:大整数(仅在整数超过 2^53-1 时使用)
    • 引用数据类型:Array/Object/Function/内置对象(Date,RegExp,Map,Set,Promise等)
  • 判断方法
    • 基本数据类型:
      • typeof:判断基本数据类型(null/function特殊)
    • 引用数据类型:
      • instanceof:判断是否在构造函数的原型链上
      • constructor:判断构造函数本身(constructor是原型对象的属性,修改原型,constructor会变)
    • 万能方法:
      • Object.prototype.toString.call()
    • 其他:
      • 判断数组:Array.isArray([]) // true
      • 判断NaN:isNaN(NaN) // true
  • 包装类
    • 概念:基本数据类型通过包装类可以调用属性和方法(执行完后会立即销毁)
    • 三种包装类:Number(),String(),Boolean()
    let num = 42; // 基本数据类型:数字
    console.log(num.toString()); // 自动将数字转为字符串
    

1.3 字符串

  • 定义:用’’或””包裹的字符
  • 基础
// 转义字符
'i\'am ok' // i'am ok
// ASCII字符
'\x41';  // 'A'
// Unicode字符
'\u4e2d\u6587'; // '中文'
// 多行字符串(``)
`你好
 世界
`
// 模板字符串
`my name is ${name}`
  • 相关操作
// 获取长度
let s = 'hello world';
s.length; // 11
s[0]; // 'h' 可以获取,但修改无效
// 大小写转换
let s = 'Hello';
s.toUpperCase(); // 'HELLO'
s.toLowerCase(); // 'hello'
// indexOf;搜索字符出现的位置
let s = 'hello';
s.indexOf('el'); // 1
s.indexOf('s'); // -1 找不到返回-1
// subString;字符串截取(前闭后开)
let s = 'hello world';
s.subString(0, 5); // hello
s.subString(6);    // world

1.4 数组

  • 常用方法
    • 修改原数组:push/pop,shift/unshift,splice,reverse/sort,fill,copyWithin
    • 不修改原数组:find,includes,slice,reduce等
  • 相关示例
// (1)fill - 数组填充 - fill(value, start, end)
let arr = new Array(5);
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0]
arr.fill(2, 0, 2); // [2, 2, 0, 0, 0]
// (2)copyWithin - 复制并覆盖 - copyWithin(target, start, end)
let arr = ['A', 'B', 'C', 'D', 'E'];
arr.copyWithin(0, 3, 5);
console.log(arr); // ['D', 'E', 'C', 'D', 'E']
// (3)splice - 删除并截取 第一个参数:开始索引,第二个参数:删除个数,后面参数:添加的内容
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 2, 6) // [2, 3]
console.log(arr) // [1, 6, 4, 5] 
// (4)slice - 数组截取,两个参数均为索引(前闭后开)
let arr = [1, 2, 3, 4, 5];
arr.slice(1, 2) // [2]
arr.slice(1) // [2, 3, 4, 5]
let arr1 = [1, 2, 3, 4, 5, 6];
let copeArr = arr1.slice(); // [1, 2, 3, 4, 5, 6]
// (5)join/split
let arr = [1, 2, 3];
arr.join('@'); // '1@2@3'
let str = '1-34';
str.split('-'); // ['1', '34']
// (6)flat
let arr = [[1, 2], [3, 4]];
console.log(arr.flat(Infinity)) // [1, 2, 3, 4];
console.log(arr.toString().split(',').map(Number)) // [1, 2, 3, 4];
// (7)reduce/new Set
let arr = [1, 2, 3, 4, 2, 2, 1];
let newArr = arr.reduce((acc, cur) => {
    if(!acc.includes(cur)) {
        acc.push(cur)
    }
    return acc;
}, []);
console.log(newArr); // [1, 2, 3, 4];
console.log(Array.from(new Set(arr))); // [1, 2, 3, 4];

1.5 对象

  • 基础使用
let person = {
    name: '张三',
    age: 20,
    score: 90
}
// 获取
console.log(person.name)
console.log(person[name])
// 新增
person.hobby = '睡觉';
// 修改
person.score = 99;
// 删除
delete person.age;
  • in操作符 - 判断对象是否拥有某个属性(属性也可能是继承获取的)
let person = {
    name: '张三',
    age: 22
}
'name' in person // true
'toString' in person // true - person继承Object

person.hasOwnProperty('toString') // false - 判断是否对象独有

1.6 条件判断和循环

  • 条件判断
if(){...}else{...}
if()(...)else if(){...}else{...}
  • 循环
// (1)for循环(用于遍历数组/类数组)
for (let i=0; i<length; i++){}
// (2)for in (用于遍历数组/对象(会遍历对象本身及原型链上**可枚举的属性**))
let parent = { hobby: '睡觉' };
let child = Object.create(parent);
child.name = '张三';
for(let key in child) {
    console.log(key) // name hobby
}
// (3)只遍历自身属性 - hasOwenProperty
for (let key in child) {
  if (child.hasOwnProperty(key)) {
    console.log(key); // 只输出自身属性
  }
}
Object.keys(child).forEach(key => {
  console.log(key);
});
Object.getOwnPropertyNames(child).forEach(key => {
  console.log(key);
});

1.7 Map/Set及iteable(遍历可迭代对象的值 - 按顺序访问的元素的对象)

  • Map:键值对的集合,具有极快的查找速度(和对象区别)
    • 特点
      • 属性名可以是任意类型
      • 有顺序,保持键值对插入顺序
      • 更易读的API:set()、get()、has()、delete()
    • 示例
    let map = new Map();
    map.set('name', '张三');
    map.set('age', 25);
    console.log(map.get('name')); // '张三'
    console.log(map.has('name')); // true
    // 二维数组写法
    let m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
    m.get('Michael'); // 95
    
  • Set:拥有唯一值的集合(操作高效,复杂度低)
    • 特点
      • 自动去重
      • 查找高效(复杂度是 O(1))
      • 插入和删除高效(复杂度是 O(1))
    • 示例
    // 去重
    let set = new Set([1, 2, 2, 3, 4, 4]);
    console.log([...set]); // [1, 2, 3, 4]
    // 查找
    let set = new Set([1, 2, 3, 4]);
    console.log(set.has(3)); // 输出: true
    console.log(set.has(5)); // 输出: false
    
  • iterable(可迭代对象能够被统一遍历)
    • for…of 可以遍历 Iterable 类型的数据
      • 可遍历:数组/字符串/Set/Map/arguments
      • 不可遍历:普通对象/null/undefined/Boolean/Number
    • 相关示例
    // 遍历数组
    let arr = [1, 2, 3]
    for(let item of arr) { 
        console.log(item) // 1 2 3
    }
    // 遍历Set
    let set = new Set([1, 2, 3, 4]);
    for(let item of set) {
        console.log(item) // 1 2 3 4
    }
    // 遍历Map
    let map = new Map([['a', 1], ['b', 2]]);
    for (let [key, value] of map) {
        console.log(key, value); // 输出 'a' 1, 'b' 2
    }
    
  • for of 和 for in区别
    • for of 遍历可迭代对象的值(数组/字符串/Set/Map/arguments)
    • for in 遍历不可迭代对象的属性名(以及继承的属性名)

二、函数

2.1 函数的重要概念

  • 定义函数的方式
    • 函数声明:function add(x,y) { return x + y }
    • 匿名函数:let add = function(x,y){ return x + y }
  • arguments:(传入的参数列表-类数组)
  • rest参数:剩余参数function foo(x, y, …rest) {}
  • return语句:终止函数执行,并返回一个值

2.2 函数的类型

  • 分类
    • 1.普通函数 function Test(){ return arguments }
    • 2.箭头函数 let num = (a, b, …rest) => { return … }
    • 3.立即执行函数 let num = (function () { return 8 + 9}())
    • 4.异步函数 async function test() {}
    • 5.生成器函数 function* gen() { yield } – 可暂停执行(yield)
      • 特点:能暂停执行的函数,调用next执行,遇到下一个yield或结束
      • 作用:分步加载大数据
      • 示例
       // 1. 定义生成器
      function* lottery() {
          yield "🎉 一等奖";
          yield "🎁 二等奖";
          yield "🤝 谢谢参与";
      }
      // 2. 创建抽奖实例
      const draw = lottery();
      // 3. 抽奖按钮
      document.getElementById('btn').onclick = () => {
          const result = draw.next();
          alert(result.done ? "抽奖结束" : result.value);
      };
      

2.3 闭包

  • 定义:函数里面嵌套函数,并把内部函数返回出来,内部函数可以访问外部变量
  • 作用:
    • 私有变量:防止外部污染变量
    • 做状态缓存:节流/防抖
  • 示例:
// 定时器介绍
// setTimeout(callback, interval, ...args) // args可选参数传递给回调函数
// setInterval(callback, interval, ...args)// args可选参数传递给回调函数
// 防抖 - 单位时间内多次操作只执行最后一次
 function debounce(func, delay = 300) {
    let timer = null;
    return function(...args) { // 返回普通函数
        clearTimeout(timer);
        timer = setTimeout(() => { // 箭头函数使this指向继承定义时的this
            func.apply(this, args)
        })
    }
}

let obj = {
    objName: 'zhangsan',
    handler: debounce(function(){
        console.log(this.objName)
    }, 300)
}
obj.handler();

// 节流 = 单位时间内多次执行间隔执行
function throller(func, interval = 300){
    let lastTime = 0;
    return function(...args) { // 返回普通函数
        const now = Date.now();
        if(now - lastTime > interval){
            func.apply(this, args);
            lastTime = now;
        }  
    }   
}

2.4 this指向及改变this指向的方法

  • 分类
    • (1)普通函数:谁调用指向谁,正常指向undefined/window
    • (2)箭头函数:继承定义时的this
    • (3)定时器:默认指向全局对象,除非使用箭头函数或显示绑定
    • (4)dom事件this:触发事件的DOM元素
  • 代码示例
// 1.普通函数this指向(谁调用指向谁)
function fn() {
    console.log(this);
}
fn(); // window/undefined
fn.apply({ name: 'zhangsan' }); // {name: 'zhangsan'}
// 2.箭头函数
const person = {
  personName: 'zhangsan',
  arrow: () => console.log(this.personName),
  regular: function() {
    return console.log(this.personName)
  }
};
person.arrow() // undefined (对象字面量不创建作用域,箭头函数仍在全局定义)
person.regular() // 'zhangsan'(继承regular的this)
let regularFn = person.regular;
regularFn(); // undefined (隐式丢失,默认绑定)
// 3.定时器
setTimeout(function() {
  console.log(this); // window/undefined
}, 100);
// 箭头函数
const obj = {
  name: 'obj',
  start() {
    setTimeout(() => {
      console.log(this.name); // 'obj' // 继承于定义时的外层作用域this
    }, 100);
  }
};
obj.start();
// 显示绑定
const obj = { name: 'obj' };
setTimeout(function() {
  console.log(this.name); // 'obj'
}.bind(obj), 100);
// 4.dom事件中的this
document.getElementById('btn').addEventListener('click', function() {
    console.log(this); // dom元素 btn按钮
});
  • 改变this指向的方法
    • call:参数一个一个传,返回一个函数,需手动出发
    • apply:参数以数组形式传,立即执行
    • bind:参数一个一个传,立即执行
    function greet(name, age) { 
        console.log(`hello ${name}, i am ${this.name}` )
    }
    const person = { name: 'zhangsan' };
    greet.apply(person, ['lisi']);
    

2.5 递归函数(函数自我调用)

  • 必备条件
    • 1.终止条件
    • 2.函数自我调用
    • 3.问题缩小
  • 示例
// 阶乘
function factorial(n) {
    if(n <= 1) return 1;       // 终止条件
    return n * factorial(n-1); // 自我调用+问题缩小
}

三、标准对象

3.1 Date

   let now = new Date();
   console.log(now);
   console.log(now.getFullYear());
   console.log(now.getMonth()); // 月份从0开始的所以需要加1
   console.log(now.getDate());
   console.log(now.getHours());
   console.log(now.getMinutes());
   console.log(now.getSeconds());

3.2 RegExp

  • 正则表达式:记录文本规则的代码

3.3 JSON

  • 介绍:一种轻量级的数据交换格式,用于不同系统间传输
  • 核心方法(不支持函数,日期)
    • JSON.stringify:对象 -> JSON字符串
    • JSON.parse:JSON字符串-> 对象

四、面向对象编程

4.1 创建对象方式

  • 创建对象
    • (1)字面量:let obj = {}
    • (2)new Object:let obj = new Object()等价于字面量
    • (3)构造函数:let person = new Person()
    • (4)Object.create:创建一个新对象,并把它连接到指定原型
  • 合并对象:Object.assign
    var test = Object.assign(target,...sources) // test == target
  • 示例
function Person(name) {
    this.name = name;
    this.greet = function() { console.log(this.name) }
}
var obj = new Person('张三');
// Object.create
var obj = { name:'张三', age: 20 };
var obj1 = Object.create(obj);
obj1.age = 18;
console.log(obj1.age) // 18
console.log(obj.age) // 20

4.2 原型和原型链

  • 概念解读
    • 构造函数:创建对象的函数
    • 原型:构造函数的一个属性,存放着实例共享的属性/方法
    • 原型链:对象继承属性的链条
  • 示例
// 注意:原型对象的constructor默认指向构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}
let p = new Person('张三', 22);
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person.prototype.constructor); // true
console.log(p.constructor === Person); // true
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true

4.3 继承方式 - 代码复用和逻辑分层

  • 作用:
    • 复用性:子类可以复用父类的属性和方法
    • 扩展性:子类可以在父类基础上扩展
    • 多态性:子类可重写父类的方法
    • 层次清晰
  • 方式
    • 1.原型链继承 - 适合方法继承(修改引用属性影响所有实例)
    • 2.构造函数继承 - 适合属性继承(无法复用方法)
    • 3.寄生组合式继承 - (构造函数提供属性+原型链提供方法+Object.create做桥梁)
    • 4.class继承 - 底层基于寄生组合式继承
  • 示例
// 1.原型链继承 - 引用数据类型共享
function Animal() { this.food = ["肉","菜"] }
function Dog() {}
Dog.prototype = new Animal(); // 核心:把父类实例作为子类原型
const dog1 = new Dog();
dog1.food.push("狗粮"); // 会污染所有实例
// 2.构造函数继承
function Animal(){
    this.name = '动物';
    this.say = function() {
        return this.name;
    }
}
function Dog(){
    Animal.call(this) // 核心:在子类中调用父类构造函数
}
let dog1 = new Dog();
let dog2 = new Dog();
console.log(dog1.name === dog2.name); // true
console.log(dog1.say === dog2.say); // false - 方法不共享
// 3.寄生组合式继承
function Animal(name){
    this.name = name;
    this.color = ['black']
}
Animal.prototype.say = function(){
    return this.name;
}
function Dog(name, age){
    Animal.call(this, name); // 继承实例属性
    this.age = age;
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复构造函数
let dog1 = new Dog('小狗', 18);
let dog2 = new Dog('大狗', 20);
console.log(dog1);
console.log(dog2);

// 圣杯模式
let inherit = (function(){
    function F(){}; // 私有化变量,立即执行函数,执行后即销毁
    return function(Target, Origin){
        F.prototype = Origin.prototype;
        Target.prototype = new F();
        Target.prototype.constructor = Target;
        Target.prototype.uber = Origin.prototype;
    }
}());
// 使用Object.create简化
let inherit = (function() {
    return function(Target, Origin) {
        Target.prototype = Object.create(Origin.prototype); // 使用 Object.create 建立原型链
        Target.prototype.constructor = Target; // 修复构造函数指向
        Target.prototype.uber = Origin.prototype; // 保留对父类原型的引用
    }
}());
// 4.class继承
class Animal {
    // 构造函数
    constructor(name){
        this.name = name;
    }
    // 原型方法
    say(){
        return this.name
    }
}
class Dog extends Animal {
    constructor(name, age) {
        super(name); // 相当于调用Animal.call(this, name)
        this.age = age;
    }
    // 独有
    bark() {
        console.log("汪汪汪!");
    } 
}

let dog = new Dog('小狗', 4);
dog.bark(); // 汪汪汪!
let animal = new Animal('大狗');
animal.bark(); // 报错

五、浏览器

5.1 浏览器对象

  • window:浏览器窗口 - innerWidth/innerHeight/outWidth/outHeight
  • navigator:浏览器信息 - appName/appVersion/language/platform/userAgent
  • screen:屏幕信息 - widht/height/colorDepth
  • location:当前页面url信息 - protocol/host/port/pathname/search/hash
  • document:当前页面 - cookie/title
  • history:浏览器历史记录 - back/forward/pushState

5.2 Dom操作

  • 查询:querySelector/querySelectorAll
    • 获取单个:document.querySelector(‘#submit-btn’)
    • 获取多个:document.querySelectorAll(‘.list-item’);
  • 更新:innerHTML/innerText/textContent
  • 插入:appendChild/insertBefore
  • 删除:removeChild
  • 创建:createElement
  • 属性操作:setAttribute/getAttribute
  • 类操作:classList.add()/.remove()
  • 事件处理:addEventListener
  • 样式操作:style

六、异步操作

6.0 异步编程的解决方案

  • 回调函数:基础但容易嵌套
  • Promise:链式调用解决嵌套问题
  • async/await:同步写法写异步代码,基于 Promise
  • 其他方案
    • 事件监听:button.addEventListener(‘click’, callback)
    • 发布订阅:eventBus.on(‘event’, handler)

6.1 Promise

  • 定义:异步编程的解决方案
  • 三种状态:pending/fulfilled/rejected
  • 静态方法:
    • Promise.all:所有成功才成功
    • Promise.race:第一个结果
    • Promise.allSettled:返回所有结果
    • Promise.resolve/.reject:直接返回成功失败的结果
      • 对原始值:
      Promise.resolve(42) // 等同于
      new Promise(resolve => resolve(42))
      
      • 对Promise值的作用(直接返回原对象):
      let p1 = Promise.resolve(666)
      console.log(Promise.resolve(p1) === p1)
      
  • 中断promise链的方式
    • 返回未解决的Promise:return new Promise(() => {})
    • 使用状态变量 + return 跳出函数
    • AbortController
  • 基础使用
// 1.简单示例
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(666)
    }, 2000)
})
p.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
}).finally(() => {
    console.log('结束')
})
// 2.链式调用(必须返回Promise才能保持顺序,否则后续操作会立即执行)
// 示例:用户登录 → 获取用户信息 → 获取用户订单
login(email, password)
  .then(loginData => {
    // 处理登录结果
    return getUserInfo(loginData.token);
  })
  .then(userInfo => {
    // 处理用户信息
    return getUserOrders(userInfo.id);
  })
  .then(orders => {
    // 处理订单数据
    console.log('完整数据获取成功!');
  })
  .catch(error => {
    // 统一错误处理
    console.error('流程出错:', error);
  });
// 3.手写Promise.all
Promise.myAll = function(promises) {
    return new Promise((resolve, reject) => {
        let results = [];
        let completed = 0;
        promises.forEach((p, index) => {
            p.then(res => {
                results[index] = res;
                completed++;
                if(completed === promises.length) {
                    resolve(results)
                }
            }).catch(reject)
        });
    })
}

6.2 async/await

  • 定义:基于Promise的语法糖,同步写法解决异步问题
  • 注意:
    • async:标记函数为异步函数,使其返回promis对象
    • await:暂停异步函数执行,等待Promise解决
    • 错误处理:使用try/catch替代Promise的.catch()
  • 示例
// 1.基本介绍
async function getData() {
    return 666 // 等同于Promise.resolve(666)
}
getData().then(res=> {
    console.log(res)
})
// 2.把异步操作写成同步执行的形式
async function fetchUserData() {
  try {
    // 用户登录
    const loginData = await login(email, password);
    // 获取用户信息
    const userInfo = await getUserInfo(loginData.token);
    // 获取用户订单
    const orders = await getUserOrders(userInfo.id);
    // 处理订单数据
    console.log('完整数据获取成功!', orders);
    return orders;
  } catch (error) {
    // 统一错误处理
    console.error('流程出错:', error);
    throw error; // 可以选择继续抛出或处理
  }
}

七、AJAX及其他请求方式

7.1 AJAX介绍

  • 简介:让网页与服务器异步通信、无需刷新页面的技术
  • 示例:
// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest();
// 2. 配置请求(GET 请求,异步模式)
xhr.open('GET', 'https://api.example.com/data', true);
// 3. 定义响应处理函数
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) { // 请求完成
    if (xhr.status === 200) { // 成功响应
      const data = JSON.parse(xhr.responseText);
      document.getElementById("result").innerHTML = data.message;
    } else {
      console.error('请求失败:', xhr.status);
    }
  }
};
// 4. 发送请求
xhr.send();

7.2 常见的请求方式

  • 原生AJAX
  • Fetch API
  • axios
    • 支持PromiseAPI
    • 拦截请求和响应
    • 取消请求
    • 超时处理
  • jQuery.ajax()

八、重点总结

8.1 深浅拷贝

  • 浅拷贝:只拷贝一层,嵌套引用类型仍然共享
    • 常见方法
      • 对象:Object.assign/…
      • 数组:slice/concat/…
  • 深拷贝:拷贝所有层级,避免引用数据共享
    • 常见方式
      • JSON.stringify/.parse:无法处理函数/RegExp/Date/Set/Map等特殊对象
      • Lodash库的_.cloneDeep():提供更为完备的深拷贝功能
      • 递归拷贝
        • 1.基本数据类型判断 - 终止条件
        • 2.处理特殊对象
        • 3.递归拷贝(创建对应的数组/对象)
        • 4.返回结果
    • 手写深拷贝
    // 1.初始版本
    function deepClone(origin, target) {
        // 1.处理基本数据类型 - 终止条件
        if (origin == null || typeof origin != 'object') return origin;
       
        // 2.处理特殊对象(正则,日期,函数,Set,Map等)
        if (origin instanceof RegExp) { // 正则
            return new RegExp(origin.source, origin.flags);
        }
        if (origin instanceof Date) { // 日期
            return new Date(origin.getTime());
        }
        if (typeof origin === 'function') { // 函数
            return origin;
        }
    
        // 3.判断是数组还是对象,并创建对应的数组或对象
        if(!target) {
            target = Array.isArray(origin) ? [] : {};
        }
    
        // 4.递归遍历拷贝所有属性
        for (let key in origin) {
            if(origin.hasOwnProperty(key)) {
                target[key] = deepClone(origin[key], target[key])
            }
        }
        return target;
    }
    // Map形式
    
×

喜欢就点赞,疼爱就打赏