一、深入对象
1.创建对象三种方式
(1)利用对象字面量创建对象
(2)利用 new Object 创建对象
(3)利用构造函数创建对象
// const obj = new Object()
// console.log(obj)
// obj.uname = '灰太狼'
const obj = new Object({uname: '灰太狼'})
console.log(obj)
2.构造函数:一种特殊的函数,主要用来初始化对象(通过构造函数来快速创建多个类似的对象)
注意:

<body>
<script>
// 创建一个猪 构造函数
function Pig(uname, age) {
this.uname = uname
// this.age = age
}
// console.log(new Pig('皮皮', 1))
// console.log(new Pig('taylor', 1))
const peppa = new Pig('皮皮')
console.log(peppa) // {name: '皮皮'}
// const pepa = {uname: '灰太狼', age: 1}
function Goods(name, price, count) {
this.name = name
this.price = price
this.count = count
}
const mi = new Goods('小米', 1999, 20)
console.log(mi)
const hw = new Goods('华为', 3999, 59)
console.log(hw)
</script>
</body>

实例化执行过程:

3.实例成员&静态成员
(1)实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员
// 实例成员和静态成员
// 1 实例成员, 实例对象上的属性和方法属于实例成员
function Pig(name) {
this.name = name
}
const peiqi = new Pig('皮皮')
const qiaozhi = new Pig('乔治')
peiqi.name = '小猪皮皮' // 实例属性
peiqi.asyHi = () => { //实例方法
console.log('hi~~')
}
console.log(peiqi)
console.log(qiaozhi)
console.log(qiaozhi === peiqi)

(2)静态成员:构造函数的属性和方法被称为静态成员
// 2 静态成员 : 构造函数上的属性和方法
function Pig(name) {
this.name = name
}
Pig.eyes = 2 // 静态属性
Pig.sayHi = function () { // 静态方法
console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes) // 2

二、内置构造函数
基本数据类型: 字符串、数值、布尔、undefined、null
引用类型:Object,Array,RegExp,Date 等
包装类型:String,Number,Boolean 等
1.Object(用于创建普通对象)【推荐使用字面量方式声明对象,而不是 Object 构造函数】
三个常用静态方法(静态方法就是只有构造函数Object可以调用的)
(1)Object.keys 静态方法获取对象中所有属性(键)
语法:

注意: 返回的是一个数组
(2)Object.values 静态方法获取对象中所有属性值
语法:

注意: 返回的是一个数组
(3)Object. assign 静态方法常用于对象拷贝
语法:

使用:经常使用的场景给对象添加属性

const o = {uname: '灰太狼', age: 18 }
// 1 获得所有的属性名
console.log(Object.keys(o)) // 返回数组['uname', 'age']
// 2 获得所有的属性值
console.log(Object.values(o)) // ['灰太狼', 18]
// 3 拷贝对象(给对象添加属性)
// const oo = {}
// Object.assign(oo. o)
// console.log(oo)
Object.assign(o, {gender: '女'})
console.log(o)
2.Array(用于创建数组 )【创建数组建议使用字面量创建,不用 Array构造函数创建】
(1)数组常见实例方法-核心方法


作用:reduce 返回函数累计处理的结果,经常用于求和等
基本语法: ![]()
参数:起始值可以省略,如果写就作为第一次累计的起始值
语法:![]()

// 数组reduce方法
// arr.reduce(function(上一次值, 当前值){},初始值)
const arr = [1, 5, 8]
// 1 没有初始值
const total = arr.reduce(function(prev, current) {
return prev + current
})
console.log(total)
// 2 有初始值
const total = arr.reduce(function(prev, current) {
return prev + current
}, 10)
console.log(total)
// 3 箭头函数的写法
const total = arr.reduce((prev,current) => prev + current, 10)
console.log(total)
(2)数组常见方法-其他方法


<body>
<script>
// const arr = ['red', 'blue', 'green']
// const re = arr.find(function (item) {
// return item === 'blue'
// })
// console.log(re)
const arr = [
{
name: '小米',
price: 1999
},
{
name: '华为',
price: 3999
},
]
// 1 find 查找
// 找小米 这个对象,并且返回这个对象
const mi = arr.find(function (item) {
// console.log(item) //
// console.log(item.name) //
return item.name === '小米'
})
console.log(mi)
const mi = arr.find(item => item.name === '小米')
console.log(mi)
// 2 every 每个是否都符合条件, 如果都符合返回 true, 否则返回 false
const arr1 = [10, 20, 30]
// const flag = arr1.every(item => item >= 10) //true
const flag = arr1.some(item => item >= 20) //只要有一个符合条件就可以
console.log(flag)
</script>
</body>
(3)数组常见方法- 伪数组转换为真数组
静态方法 Array.from()
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// Array.from(lis) 把伪数组钻换为真数组
const lis = document.querySelector('ul li')
// console.log(lis)
// lis.pop() 报错
const liss = Array.from(lis)
liss.pop()
console.log(liss)
</script>
</body>
3.String
(1)常见实例方法

<body>
<script>
// 1 split 把字符串 转换为 数组 和 join() 相反
const str = '灰太狼,红太狼'
const arr = str.split(',')
console.log(arr)
const str1 = '2025-7-19'
const arr1 = str1.split('-')
console.log(str1)
// 2 字符串的截取 substring(开始的索引号[, 结束的索引号])
// 2.1 如果省略 结束的索引号 默认取到到结束
// 2.2 结束的索引号不包含想要截取的部分
const str = '我一定会回来的'
console.log(str.substring(4,8))
// 3 startsWith 判断是不是以某个字符开头
const str = '灰太狼在学习种'
console.log(str.startsWith('灰太狼'))
// 4 inclues 判断某个字符是不是包含在一个字符串里面
const str = '我是灰太狼'
console.log(str.includes('灰太狼')) // true
</script>
4.Number(用于创建数值 )
常用方法: toFixed() 设置保留小数位的长度

三、编程思想
1.面向过程介绍:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用
面向过程,就是按照我们分析好了的步骤,按照步骤解决问题
2.面向对象介绍:把事务分解成为一个个对象,然后由对象之间分工与合作
面向对象是以对象功能来划分问题,而不是步骤
特性:封装性;继承性;多态性

四、构造函数
总结:构造函数体现了面向对象的封装特性;构造函数实例创建的对象彼此独立、互不影响
构造函数存在浪费内存的问题
五、原型
1.原型:一个对象,我们也称为 prototype 为原型对象
作用:共享方法; 可以把那些不变的方法,直接定义在 prototype 对象上
构造函数和原型里面的this指向实例化的对象
(1)原型- this指向实例化的对象
<body>
<script>
let that
function Star (uname) {
// that = this
// console.log(this)
this.uname = uname
}
// 原型对象里面的 this 也是 实例对象 taylor
Star.prototype.sing = function() {
that = this
console.log('唱歌')
}
// 实例对象 taylor
// 构造函数里面的 this 就是 实例对象 taylor
const taylor = new Star('taylor')
taylor.sing()
console.log(that === taylor)
</script>
</body>
(2)数组扩展方法
<body>
<script>
// 自己定义 数组扩展方法 求和 和 最大值
// 1 我们定义的这个方法,任何一个数组实例对象都可以使用
// 2 自定义的方法写到 数组 .prototype 身上
// 1 最大值
const arr = [1, 2, 3]
Array.prototype.max = function() {
// 展开运算符
return Math.max(...this)
// 原型函数里面的 this 指向 实例对象 arr
}
// 2 最小值
Array.prototype.min = function() {
// 展开运算符
return Math.min(...this)
// 原型函数里面的 this 指向 实例对象 arr
}
console.log(arr.max())
console.log([2, 5, 9].max())
console.log(arr.min())
// const arr = new Array(1, 2)
// console.log(arr)
// 3 求和 方法
Array.prototype.sum = function () {
return this.reduce( (prev , item) => prev + item, 0)
}
console.log([1, 2, 3].sum())
console.log([11, 21, 31].sum())
</script>
</body>
2.constructor 属性:每个原型对象里面都有个constructor 属性(constructor 构造函数)
作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子
<body>
<script>
// constructor 单词 构造函数
function Star () {
}
// Star.prototype.sing = function() {
// console.log('唱歌')
// }
// Star.prototype.dance = function() {
// console.log('跳舞')
// }
console.log(Star.prototype)
Star.prototype = {
// 重新指回创造这个原型对象的 构造函数
constructor: Star,
sing: function () {
console.log('唱歌')
},
dance: function () {
console.log('跳舞')
},
}
// console.log(Star.prototype.constructor)
// const taylor = new Star()
// console.log(Star.prototype)
// console.log(Star.prototype.constructor === Star)
</script>
</body>
3.对象原型:对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象


<body>
<script>
function Star () { }
const taylor = new Star()
// 对象原型__proto__ 指向 该构造函数的原型对象
console.log(taylor.__proto__)
console.log(taylor.__proto__ === Star.prototype)
// 对象原型里面有constructor 指向 构造函数 Star
console.log(taylor.__proto__.constructor === Star)
</script>
</body>
4.原型继承:
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JS中大多是借助原型对象实现继承的特性
(1)封装-抽取公共部分

(2)继承-让男人和女人都能继承人类的一些属性和方法

(3)问题解决
需求:男人和女人不要使用同一个对象,但是不同对象里面包含相同的属性和方法

<body>
<script>
// 继续抽取 公共部分放到原型上
// const Person1 = {
// ear: 2,
// head: 2
// }
// const Person2 = {
// ear: 2,
// head: 2
// }
// 构造函数 new 出来的对象 结构一样, 但是对象不一样
function Person() {
this.ears = 2
this.head = 1
}
// 女人 构造函数 继承 想要 继承 Person
function Woman () {
}
// Woman 通过原型来继承 Person
// 父构造函数(父类) 子构造函数(子类)
// 子类的原型 = new 父类
Woman.prototype = new Person()
// 指回原来的构造函数
Woman.prototype.constructor = Woman
// 给女人添加一种方法 生孩子
Woman.prototype.baby = function () {
console.log('宝贝')
}
const red = new Woman()
console.log(red)
console.log(Woman.prototype)
// 男人 构造函数 继承 想要 继承 Person
function Man () {
}
// 通过原型来继承 Person
Man.prototype = new Person()
Man.prototype.constructor = Man
const blue = new Man()
console.log(blue)
</script>
5.原型链

查找原则

<body>
<script>
// function Object () {}
console.log(Object.prototype)
console.log(Object.prototype.__proto__ )
function Person() {
}
const taylor = taylor.Person()
// console.log(taylor.__proto__ === Person.prototype)
// console.log(Person.prototype.__proto__ === Object.prototype)
console.log(taylor instanceof Person)
console.log(taylor instanceof Object)
console.log(taylor instanceof Array)
console.log([1, 2, 3] instanceof Array)
console.log(Array instanceof Object)
</script>
</body>
六、深浅拷贝
1.浅拷贝:拷贝的是地址

<body>
<script>
const obj = {
uname: '灰太狼',
age: 18,
family: {
baby: '小灰灰'
}
}
// 浅拷贝
// const o = {...obj}
// console.log(o)
// o.age = 20
// console.log(o)
// console.log(obj)
const o = {}
Object.assign(o, obj)
o.age = 20
o.family.baby = '懒洋洋'
console.log(o)
console.log(obj)
</script>
</body>
2.深拷贝:拷贝的是对象,不是地址

(1)通过递归实现
函数递归: 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
<body>
<script>
const obj = {
uname: '灰太狼',
age: 18,
hobby: ['写信','听歌'],
family: {
baby: '小灰灰'
}
}
const o = {}
// 拷贝函数
function deepCopy(newObj, oldObj) {
debugger
for(let k in oldObj) {
// 处理数组的问题 一定先写数组 再写 对象 不能颠倒
if(oldObj[k] instanceof Array) {
newObj[k] = []
// newObj[k] 接收 [] hobby
// oldObj[k] ['写信','听歌']
deepCopy(newObj[k], oldObj[k])
}
else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
}
else {
// k 属性名 uname age (k是变量要用变量) oldObj[k] 属性值 18
// newObj[k] === o.uname
newObj[k] = oldObj[k]
}
}
}
deepCopy(o, obj) // 函数调用 两个参数 o 新对象 obj 旧对象
console.log(o)
o.age = 20
o.hobby[0] = '看电影'
o.family.baby = '懒洋洋'
console.log(obj)
</script>
</body>
(2) js库lodash里面cloneDeep内部实现了深拷贝
<body>
<!-- 先引用 -->
<script src="./lodash.min_1765119256219.js"></script>
<script>
const obj = {
uname: '灰太狼',
age: 18,
hobby: ['写信', '听歌'],
family: {
baby: '小灰灰'
}
}
const o = _.cloneDeep(obj)
console.log(o);
o.family.baby = '懒洋洋'
console.log(obj)
</script>
</body>
(3)通过JSON.stringify()实现
<body>
<script>
const obj = {
uname: '灰太狼',
age: 18,
hobby: ['写信', '听歌'],
family: {
baby: '小灰灰'
}
}
// 把对象转换为 JSON 字符串
// console.log(JSON.stringify(obj))
const o = JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby = '懒洋洋'
console.log(obj)
</script>
</body>
七、异常处理
1.throw 抛异常

<body>
<script>
function fn(x, y) {
if (!x || !y) {
console.log(11)
// throw '没有参数传递进来'
throw new Error('没有参数传递进来')
}
return x + y
}
console.log(fn())
</script>
</body>
2.try /catch 捕获异常

<body>
<p>123</p>
<script>
function fn() {
try {
// 可能发送错误的代码 要写到 try
const p = document.querySelector('.p')
p.style.color = 'red'
} catch(err) {
// 拦截错误, 提示浏览器提供的错误信息, 但是不中断程序的执行
console.log(err.message)
throw new Error('错误了吧')
// 需要加return 中断程序
return
}
finally {
// 不管你的程序对不对, 一定会执行的代码
alert('弹出对话窗')
}
console.log(11)
}
fn()
</script>
</body>
3.debugger
我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息)

八、处理this
1.this指向
(1)普通函数:普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】
普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined
<body>
<button>点击</button>
<script>
// 普通函数, 谁调用我,我指向谁
console.log(this) // window
function fn() {
console.log(this) // window
}
Window.fn()
window.setTimeout(function() {
console.log(this) // window
}, 1000)
document.querySelector('button').addEventListener('click', function () {
console.log(this) // button
})
const obj = {
sayHi: function () {
console.log(this) // obj
}
}
obj.sayHi()
</script>
</body>
(2)箭头函数
箭头函数中的this与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this!


2.改变this
(1)call()
使用 call 方法调用函数,同时指定被调用函数中 this 的值
语法:![]()
<body>
<script>
const obj = {
uname: '灰太狼'
}
function fn(x, y) {
console.log(this) // window
console.log(x + y)
}
// 1 调用函数
// 2 改变this指向
// fn.call(obj) // this 指向 obj
fn.call(obj, 1,2)
</script>
</body>
(2)apply()
使用 apply 方法调用函数,同时指定被调用函数中 this 的值
语法: ![]()
<body>
<script>
const obj = {
age: 18
}
function fn(x, y) {
console.log(this) // {age: 18}
console.log(x + y)
}
// 1 调用函数
// 2 改变this指向
// fn.apply(this指向谁, 数组参数)
// fn.apply(obj)
fn.apply(obj, [1, 2])
// 3 返回值 本身就是在调用函数, 所以返回值就是函数的返回值
// 使用场景: 求数组最大值
// const max = Math.max(1, 2, 3)
// console.log(max)
const arr = [100, 45, 63]
const max = Math.max.apply(Math, arr)
const min = Math.min.apply(Math, arr)
console.log(max, min)
// 使用场景: 求数组最大值
console.log(Math.max(...arr))
</script>
</body>

(3)bind()
bind() 方法不会调用函数。但是能改变函数内部this 指向
语法:![]()
<body>
<button>点击</button>
<script>
const obj = {
age: 18
}
function fn() {
console.log(this)
}
// 1 bind 不会调用函数
// 2 能改变this指向
// 3 返回值是函数, 但是这个函数里面的this 是更改过的obj
const fun = fn.bind(obj)
// console.log(fun)
fun()
// 需求: 有一个按钮,点击,里面就禁用,2秒后开启
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
// 禁用按钮
this.disabled = true
window.setTimeout(function () {
// 在这个普通函数里面,我们要this由原来的window 改变为 btn
this.disabled = false
}.bind(this), 2000)
})
</script>
</body>

九、性能优化
1.防抖:
指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间
函数实现:
<body>
<div class="box"></div>
<script>
// 利用防抖实现性能优化
// 需求: 鼠标在盒子上移动, 里面的数组变化 + 1
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
// 如果里面存在大量消耗性能的代码, 比如dom操作, 比如数据处理, 可能造成卡顿
}
// box.addEventListener('mousemve', mouseMove)
// 手写防抖函数
// 核心是利用setTimeout定时器来实现
// 1.声明定时器变量
// 2,每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器
// 3.如果没有定时器,则开启定时器,存入到定时器变量里面
// 4.定时器里面写函数调用
function debounce(fn, t) {
let timer
// return 返回一个匿名函数
return function() {
// 2.3.4
if(timer) clearTimeout(timer)
setTimeout(function(){
fn() // 加小括号调用fn函数
}, t)
}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))
// debounce(mouseMove, 500) // 调用函数
// debounce(mouseMove, 500) = function() {2.3.4}
</script>
</body>
2.节流:就是指连续触发事件但是在 n 秒中只执行一次函数
函数实现:
<body>
<script>
// 利用防抖实现性能优化
// 需求: 鼠标在盒子上移动, 里面的数组变化 + 1
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
// 如果里面存在大量消耗性能的代码, 比如dom操作, 比如数据处理, 可能造成卡顿
}
// box.addEventListener('mousemve', mouseMove)
// 利用lodash库实现防抖 - 500毫秒后采用+1 (没有下载lodash)
// 语法: _.throttle(fun, 时间)
// box.addEventListener('mousemove', _.throttle(mouseMove, 500))
//手写一个节流函数-每隔500ms+1
//节流的核心就是利用定时器(setTimeout)来实现
// 1.声明一个定时器变量
// 2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
// 3.如果没有定时器则开启定时器,记得存到变量里面
// 3.1定时器里面调用执行的函数
// 3.2定时器里面要把定时器清空
function throttle(fn, t) {
let timer = null
return function() {
if(!timer) {
timer = setTimeout(function() {
fn()
// 清空定时器
timer = null
}, t)
}
}
}
box.addEventListener('mousemove', throttle(mouseMove, 500))
</script>
</body>

270

被折叠的 条评论
为什么被折叠?



