Java从作用域到对象高级应用​

简介: 本内容详细讲解了JavaScript中的作用域类型(函数作用域、块作用域、全局作用域)、作用域链、垃圾回收机制、闭包、变量提升、函数参数、数组方法、内置构造函数、对象高级知识、原型链、对象赋值、深浅拷贝、递归、异常处理及this指向等内容,全面覆盖JS核心概念与编程技巧。

01: 作用域

作用域类型

  • 局部作用域:
  • 函数作用域: 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。函数执行完毕后,函数内部的变量实际被清空。
  • 块作用域: 在JavaScript中使用{}包裹的代码称为代码块。
  • 注意:let声明的变量会产生块作用域,const常量也会产生块作用域,var不会产生块作用域。
  • 全局作用域: 在<script>标签和.js文件的【最外层】就是所谓的全局作用域。

作用域链

本质上是底层的变量查找机制。在函数被执行时,会优先查找当前函数作用域中的变量,如果没有,则依次逐级查找父级作用域直到全局作用域。

垃圾回收机制

简称GC,JavaScript内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。

  • 内存泄漏: 不再用到的内存,没有及时释放。
  • 垃圾回收算法:
  • 引用计数法(老版本IE浏览器): 看一个对象是否有指向它的引用,如果被引用一次,那么记录次数1,反之减少引用则减1,如果引用次数是0,则释放内存。
  • 缺点: 如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏。
  • 标记清除法(目前大部分浏览器): 标记清除算法将"不再使用的对象"定义为"无法达到的对象",就是从根部出发定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的,反之回收。

闭包

通俗来说就是内层函数访问到其外层函数的作用域,如闭包 = 内层函数 + 外层函数的变量。

  • 作用: 封闭数据,提供操作,外部也可以访问函数内部的变量。
  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

function out(){
  let i = 1
  function fn(){
    console.log(i)
  }
  return fn
}
const fun = out()
fun()

变量提升与函数提升

  • 变量提升: 在JavaScript中允许在变量声明之前即被访问(仅存在var声明变量)。
  • 注意:
  1. 变量在var声明之前被访问,变量的值为undefined
  2. let/const声明的变量不存在变量提升
  • 函数提升: 函数提升与变量提升比较类似,是指函数在声明之前即被调用。

函数参数

  • 动态参数:arguments是函数内置的伪数组变量,它包含了调用函数时传入的所有实参(伪数组只存在函数中)。
  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

function sum(){
  let s = 0
  for(let i = 0; i < arguments.length; i++){
    s += arguments[i]
  }
  console.log(s)
}
sum(1,2)
sum(1,2,3)
  • 剩余参数:
  • 语法: ...是语法符号,置于最末函数形参之前,用于获取多余的实参(...获取的剩余实参,是个真数组)。
  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

function config(a,...other){
  console.log(a)
  console.log(other)
}
config(1,2,3)
  • 展开运算符:
  • 语法: ...将一个数组进行展开。
  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

const arr = [1,2,3,4,5]
console.log(...arr)
console.log(Math.max(...arr))
  • 注意: 剩余参数主要是在函数实参使用,得到真数组;展开运算符主要是数组使用,数组展开。

箭头函数

  • 目的: 引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。
  • 语法: () => {}
  • 注意:
  • 只有一个参数可以省略小括号
  • 如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值
  • 如果最后返回的是一个对象,加括号的函数体返回对象字面量表达式
  • 箭头函数没有arguments动态参数,但是有剩余参数...args
  • 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
  • 由于上一点的原因,DOM事件回调函数为了简便,还是不太推荐使用箭头函数

解构赋值

  • 目的: 使用解构简洁语法为变量快速赋值。

数组解构

  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

// 基本语法
const [a,b,c] = [1,2,3]
// 交换两个变量
let a = 1
let b = 3;
[b,a] = [a,b]
  • 注意事项:
  • JavaScript在两种情况下需要加分号: 立即执行函数、数组解构
  • 变量多,单元值少时,多余的变量将被赋值为undefined
  • 变量少,单元值多时,依旧按顺序赋值
  • 可以利用剩余参数解决变量少,单元值多的情况,剩余参数返回的是一个数组
  • 防止有undefined传递单元值,可以设置默认值:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

const [a = '手机', b = '华为'] = ['小米']
  • 数组解构可以按需导入,忽略某些返回值:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

const [a,,c,d] = [111,222,333,444]

对象解构

  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

// 基础语法
const user = {
  name: '小明',
  age: 18
}
const {name, age} = user
// 数组对象解构
const pig = [
  {
    name: '佩奇',
    age: 6
  }
]
const [{name, age}] = pig
// 多级对象解构
const pig = {
  name: '佩奇',
  family: {
    mother: '猪妈妈',
    father: '猪爸爸',
    sister: '乔治'
  },
  age: 6
}
const {name, family: {mother, father, sister}, age} = pig

数组方法

forEach()方法

  • 语法:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

arr.forEach(function(item, index, self){
  // item代表数组中每一个元素
  // index代表每一个索引
  // self代表数组本身
})

filter()方法

  • 使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组。
  • 语法:aly.sos-tofl.com55

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

arr.filter(function(item, index){
  // item代表数组中每一个元素
  // index代表每一个索引
  // 例如筛选大于30的元素
  return item > 30
})

02: 内置构造函数

创建对象的三种方式

  1. 利用对象字面量创建对象

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

const o = {
  name: '佩奇'
}
  1. 利用new Object创建对象

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

const o = new Object({name: '佩奇'})
  1. 利用构造函数创建对象

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

function Pig(name, age, gender){
  this.name = name
  this.age = age
  this.gender = gender
}
const Peppa = new Pig('佩奇', 6, '女')
  • 注意事项:
  • 对象的命名以大写字母开头
  • 使用new关键字调用函数的行为称为实例化
  • 构造函数内部无需写return,返回值即为新创建的对象
  • 构造函数内部的return返回值无效

实例化执行过程

  1. 创建新对象
  2. 构造函数this指向新对象
  3. 执行构造函数代码,修改this,添加新的属性
  4. 返回新对象

成员类型

  • 实例成员: 通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
  • 静态成员: 构造函数的属性和方法被称为静态成员。

内置构造函数

包装类型

当把字符串、数字这种原始类型当作对象使用时,JavaScript会自动包装他们,把他们包装为对象。

  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

// String/Number/Boolean
let str = 'hello'
console.log(typeof str) // string
str = new String('hello') // JS内部,自动 new String(),得到一个对象
console.log(typeof str) // object
console.log(str.length)
console.log(str.toUpperCase())

Object

  • Object.keys(obj): 获取对象中的所有属性
  • Object.values(obj): 获取对象中的所有属性值
  • Object.assign(obj): 用于对象拷贝

Array

  • forEach: 遍历数组,不返回,用于不改变值,经常用于查找打印输出
  • filter: 筛选数组元素,并生成新数组
  • map: 迭代数组,返回新数组,经常用于处理数据
  • reduce: 累加器,返回函数累计处理的结果
  • 注意: arr.reduce(function(total,current){},起始值),起始值可以省略,如果写就作为第一次累积的起始值
  • some: 根据条件,如果有元素符合,返回true,反之false
  • every: 根据条件,如果全部元素符合,返回true,反之false
  • find: 根据条件,如果有符合的元素,返回第一个元素,并终止查找,如果没有,返回undefined
  • findIndex: 根据条件,如果有符合条件的元素,则返回这个元素的下标,反之返回-1
  • sort: 语法: arr.sort((a,b) => {})
  • reverse: 翻转数组
  • splice: 从指定的下标,删除几个元素,并可以添加新的元素
  • Array.from(): 将伪数组转成真数组

String

  • split: 将字符串按指定的符号分割为数组
  • substring: 字符串截取(包头不包尾)
  • includes: 判断字符串是否包含xxx,如果有指定下标,表示从指定下标开始,看字符串是否包含xxx
  • startWith(): 判断字符串开头,如果有指定下标,表示从指定下标开始的开头
  • endsWith(): 判断字符串结尾,如果有指定下标,表示先截取n个字符,看这n个字符的结尾是否以xxx结尾
  • replace(): 替换字符串的某个字符,如果要全部替换,使用正则表达式

Number

  • toFixed(aly.play01.net): 去掉多余的小数(四舍五入)

03: 对象高级知识

面向对象与面向过程

  • 面向对象: 把需求中遇到的事物,分为一个个对象。
  • 三个特点: 封装、多态、继承
  • 优点: 性能高
  • 缺点: 没有面向对象易维护、易复用、易扩展
  • 面向过程: 一步一步的完成需求。
  • 优点: 易维护,可以设计出高内聚低耦合的系统
  • 缺点: 性能比面向过程低

构造函数

构造函数的this指向实例对象。

原型对象

任何函数都有原型对象,构造函数、内置构造函数也不例外。

  • 语法: 构造函数.prototype
  • 注意: 构造函数和原型对象中的this都指向实例对象。

实际上,实例对象的[[Prototype]]属性才指向原型对象,由于[[Prototype]]是隐藏属性,不可访问,所以浏览器才实现__proto__,用于访问原型对象,但是不推荐使用,建议使用Object.getPrototypeOf(实例对象)来找原型对象。

原型继承

  • 注意:
  • 通过修改构造函数的原型对象,可以实现对象的继承
  • 通过原型继承后,需要手动修改原型对象的constructor属性,否则不符合上述的三角关系
  • 固定语法: 构造函数.prototype.constructor = 构造函数

原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链(面试爱问)。

instanceof运算符

检测在p的原型链上,是否有这个构造函数。

  • 语法: 实例对象 instanceof 构造函数

04: 对象赋值

对象赋值

  • 直接赋值:
  • 原始类型(字符串,数字...)的值,直接赋值之后,只是把存储的值赋值给变量。赋值之后,两个变量互不影响。
  • 引用类型的值,因为存储的是地址,等号赋值之后,是把地址赋值给了另一个变量,修改其中一个对象的属性,另一个对象也跟着改了。

浅拷贝

只关注对象的第一层,只拷贝对象第一层的属性和属性值,这样的拷贝,就叫做浅拷贝。

  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

let obj1 = {
  name: 'zs',
  age: 20,
  height: 180
}
let obj2 = {} 
// obj2.name = 'zs'
// obj2.age = 20
// 循环遍历 obj1,循环一次,取obj1里面的一个属性,然后给obj2加上
for (let key in obj1) {
  // key 是 name、age、height
  // obj1[key] 是 zs、20、180
  // console.log(obj1[key]) // key是变量,必须用[]语法,而且绝对不能加引号
  obj2[key] = obj1[key]
}

深拷贝

通过递归,逐层将对象的属性、属性值拷贝给另一个对象,使得两个对象能够完全分开。

  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

let obj2 = {}
function fn(obj1, obj2) {
  for (let key in obj1) {
    // 循环的时候,先判断,参见上面的 1、 2
    if (Array.isArray(obj1[key])) {
      obj2[key] = []
      fn(obj1[key], obj2[key])
    } else if (obj1[key] instanceof Object) {
      obj2[key] = {}
      fn(obj1[key], obj2[key])
    } else {
      // 如果进入else,说明对象的值是普通的值,则直接拷贝
      obj2[key] = obj1[key]
    }
  }
}
fn(obj1, obj2)
// 尝试修改一个值
obj1.dog.color = '绿色'

其他拷贝方案

  • Object.assign(): 本质是实现对象的合并,但是可以用于实现对象的浅拷贝。
  • JSON.parseJSON.stringify: 注意:JSON中不能出现函数,undefined,但是可以实现深拷贝。
  • 代码示例:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

// 通过 JSON.stringify 将上述对象先转为 JSON 字符串
let str = JSON.stringify(obj1)
// 把 str 字符串,转成对象
let obj2 = JSON.parse(str)
  • 使用前端知名的库lodash实现深、浅拷贝(注意要加载lodash.min.js)。

递归

自己调用自己,注意写递归时,必须设置一个终止条件,否则会形成死循环。

异常处理

  • 语法1: thrownew Error('错误提示')(了解)
  • 语法2:

代码语言:JavaScript

代码运行次数:0

自动换行运行

AI代码解释

try {
  // 代码
} catch(e) {
  // 错误处理
}

this指向及修改this指向

  • 普通函数中的this,表示调用者
  • 箭头函数中的this,需要按照作用域链去查找即可也可以分开记忆,以下是非箭头函数的总结:
  • 全局中的this表示window
  • 普通函数中的this表示window
  • 定时器中的this表示window
  • 事件处理函数中的this表示事件源
  • 构造函数、对象方法、原型对象方法中的this,表示实例对象
  • 静态方法中的this,表示构造函数
相关文章
|
14天前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
164 0
|
3月前
|
缓存 安全 Java
Java反射机制:动态操作类与对象
Java反射机制是运行时动态操作类与对象的强大工具,支持获取类信息、动态创建实例、调用方法、访问字段等。它在框架开发、依赖注入、动态代理等方面有广泛应用,但也存在性能开销和安全风险。本文详解反射核心API、实战案例及性能优化策略,助你掌握Java动态编程精髓。
|
4月前
|
存储 Java
Java对象的内存布局
在HotSpot虚拟机中,Java对象的内存布局分为三部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头包含Mark Word、Class对象指针及数组长度;实例数据存储对象的实际字段内容;对齐填充用于确保对象大小为8字节的整数倍。
|
5月前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
193 1
|
5月前
|
前端开发 Java 数据库连接
java bo 对象详解_全面解析 java 中 PO,VO,DAO,BO,POJO 及 DTO 等几种对象类型
Java开发中常见的六大对象模型(PO、VO、DAO、BO、POJO、DTO)各有侧重,共同构建企业级应用架构。PO对应数据库表结构,VO专为前端展示设计,DAO封装数据访问逻辑,BO处理业务逻辑,POJO是简单的Java对象,DTO用于层间数据传输。它们在三层架构中协作:表现层使用VO,业务层通过BO调用DAO处理PO,DTO作为数据传输媒介。通过在线商城的用户管理模块示例,展示了各对象的具体应用。最佳实践包括保持分层清晰、使用工具类转换对象,并避免过度设计带来的类膨胀。理解这些对象模型的区别与联系。
330 1
|
6月前
|
Java
深入JavaSE:详解Java对象的比较。
总的来说,Java对象的比较就像海洋生物的比较,有外在的,有内在的,有面对所有情况的,也有针对特殊情况的。理解并掌握这些比较方式,就能更好地驾驭Java的世界,游刃有余地操作Java对象。
102 12
|
7月前
|
编解码 JavaScript 前端开发
【Java进阶】详解JavaScript的BOM(浏览器对象模型)
总的来说,BOM提供了一种方式来与浏览器进行交互。通过BOM,你可以操作窗口、获取URL、操作历史、访问HTML文档、获取浏览器信息和屏幕信息等。虽然BOM并没有正式的标准,但大多数现代浏览器都实现了相似的功能,因此,你可以放心地在你的JavaScript代码中使用BOM。
193 23
|
7月前
|
Java 数据安全/隐私保护
Java 类和对象
本文介绍了Java编程中类和对象的基础知识,作为面向对象编程(OOP)的核心概念。类是对象的蓝图,定义实体类型;对象是具体实例,包含状态和行为。通过示例展示了如何创建表示汽车的类及其实例,并说明了构造函数、字段和方法的作用。同时,文章还探讨了访问修饰符的使用,强调封装的重要性,如通过getter和setter控制字段访问。最后总结了类与对象的关系及其在Java中的应用,并建议进一步学习继承等概念。
147 1
|
8月前
|
设计模式 缓存 Java
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
471 3
|
8月前
|
存储 算法 安全
Java对象创建和访问
Java对象创建过程包括类加载检查、内存分配(指针碰撞或空闲列表)、内存初始化、对象头设置及初始化方法执行。访问方式有句柄和直接指针两种,前者稳定但需额外定位,后者速度快。对象创建涉及并发安全、垃圾回收等机制。
Java对象创建和访问