JavaScript 高级(进阶)
JavaScript 高级(进阶)
作用域&解构&箭头函数
作用域
目标:了解作用域对程序执行的影响及作用域链的查找机制,使用闭包函数创建隔离作用域避免全局变量污染。
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。
作用域分为:
- 局部作用域
- 全局作用域
局部作用域
局部作用域分为函数作用域和块作用域。局部作用域声明的变量外部不能使用。
1.函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
总结:
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
2.块作用域:
在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将**【有可能】**无法被访问。
JavaScript 中除了变量外还有常量,常量与变量本质的区别是:常量必须要有值且不允许被重新赋值,常量值为对象时其属性和方法允许重新赋值。
<script>// 必须要有值const version = '1.0.0';// 不能重新赋值// version = '1.0.1';// 常量值为对象类型const user = {name: '小明',age: 18}// 不能重新赋值user = {};// 属性和方法允许被修改user.name = '小小明';user.gender = '男';
</script>
总结:
- let 声明的变量会产生块作用域,var声明的变量不会产生块作用域。
- const 声明的常量也会产生块作用域。
- 不同代码块之间的变量无法互相访问。
- 推荐使用 let 或 const。
注:开发中 let
和 const
经常不加区分的使用,如果担心某个值会不小被修改时,则只能使用 const
声明成常量。
全局作用域
script标签和.js 文件的最外层就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问。
注意:
- 为 window 对象动态添加的属性默认也是全局的,不推荐!
- 函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
- 尽可能少的声明全局变量,防止全局变量被污染
全局作用域有哪些? <script 标签内部和 .js 文件。
<script>// 此处是全局function sayHi() {// 此处为局部}// 此处为全局
</script>
JavaScript 中的作用域是程序被执行时的底层机制,了解这一机制有助于规范代码书写习惯,避免因作用域导致的语法错误。
作用域链
作用域链本质上是底层的变量查找机制。
- 在函数被执行时,会优先查找当前函数作用域中查找变量
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按着从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域
JS垃圾回收机制
了解JS垃圾回收机制的执行过程。目的: 为了闭包做铺垫。
1. 什么是垃圾回收机制?
垃圾回收机制(Garbage Collection) 简称 GC。
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
如果不了解JS的内存管理机制,非常容易造成内存泄漏(内存无法被回收)的情况。不再用到的内存,没有及时释放,就叫做内存泄漏。
2.内存的生命周期
JS环境中分配的内存, 一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
说明: 全局变量一般不会回收(关闭页面回收);一般情况下局部变量的值, 不用了, 会被自动回收。
总结:
- 什么是垃圾回收机制?
- 简称 GC
- JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收
- 什么是内存泄漏?
- 不再用到的内存,没有及时释放,就叫做内存泄漏
- 内存的生命周期是什么样的?
- 内存分配、内存使用、内存回收
- 全局变量一般不会回收; 一般情况下局部变量的值, 不用了, 会被自动回收掉
3.垃圾回收算法说明
所谓垃圾回收, 核心思想就是如何判断内存是否已经不再会被使用了, 如果是, 就视为垃圾, 释放掉。
两种常见的浏览器垃圾回收算法: 引用计数法 和 标记清除法。
引用计数:
IE采用的引用计数算法, 定义“内存不再使用”的标准,就是看一个对象是否有指向它的引用。
算法:
- 跟踪记录每个值被引用的次数。
- 如果这个值的被引用了一次,那么就记录次数1。
- 多次引用会累加。
- 如果减少一个引用就减1。
- 如果引用次数是0 ,则释放内存。
const person = {age: 18,
name: 'pink老师'
}
const p = person
person = 1
p = null
引用计数算法是个简单有效的算法。但它却存在一个致命的问题:嵌套引用。如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
function fn() {let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。
标记清除法:
现代的浏览器已经不再使用引用计数算法了,现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心:
- 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
- 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
- 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
function fn() {let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
根部已经访问不到,所以自动清除。
闭包
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。
简单理解:闭包 = 内层函数 + 外层函数的变量。闭包是一种比较特殊的函数,使用闭包能够访问内层函数作用域中的变量。
闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量。闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。
闭包的基本格式:
闭包应用:实现数据的私有。
<script>function foo() {let i = 0;// 函数内部分函数function bar() {console.log(++i);}// 将函数做为返回值return bar;}// fn 即为闭包函数let fn = foo();fn(); // 1
</script>
注:回调函数也能访问函数内部的局部变量。
闭包可能引起的问题:内存泄漏。
变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)。用var关键字声明变量会有变量提升。
<script>// 访问变量 strconsole.log(str + 'world!');// 声明变量 strvar str = 'hello ';
</script>
注意:
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前即被访问,变量的值为 undefined
- let/const 声明的变量不存在变量提升
- 变量提升出现在相同作用域当中
- 实际开发中推荐先声明再访问变量
说明:
JS初学者经常花很多时间才能习惯变量提升,还经常出现一些意想不到的bug,正因为如此,ES6 引入了块级作用域,用let 或者 const声明变量,让代码写法更加规范和人性化。
变量提升是什么流程?
- 先把var声明的变量提升到当前作用域于最前面
- 只提升变量声明, 不提升变量赋值
- 然后依次执行代码
我们不建议使用var声明变量。关于变量提升的原理分析会涉及较为复杂的词法分析等知识,而开发中使用 let
可以轻松规避变量的提升。
函数进阶
函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
<script>// 调用函数foo()// 声明函数function foo() {console.log('声明之前即被调用...')}// 不存在提升现象bar() // 错误var bar = function () {console.log('函数表达式不存在提升现象...')}
</script>
总结:
- 函数提升能够使函数的声明调用更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
函数表达式必须先声明和赋值,后调用 ,否则报错。
函数参数
函数参数的使用细节,能够提升函数应用的灵活度。
函数的参数有实参和形参,形参可以理解为默认参数。函数还有动态参数和剩余参数。
默认参数:
<script>// 设置参数默认值function sayHi(name="小明", age=18) {document.write(`<p>大家好,我叫${name},我今年${age}岁了。</p>`);}// 调用函数sayHi();sayHi('小红');sayHi('小刚', 21);
</script>
总结:
- 声明函数时为形参赋值即为参数的默认值
- 如果参数未自定义默认值时,参数的默认值为
undefined
- 调用函数时没有传入对应实参时,参数的默认值被当做实参传入
动态参数:
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
当不确定传递多少个实参的时候,使用arguments 动态参数。
<script>// 求和函数,计算所有参数的和function sum() {// console.log(arguments)let s = 0for(let i = 0; i < arguments.length; i++) {s += arguments[i]}console.log(s)}// 调用求和函数sum(5, 10)// 两个参数sum(1, 2, 4) // 三个参数
</script>
总结:
- arguments 是一个伪数组,只存在于函数中
- arguments 的作用是动态获取函数的实参
- 可以通过for循环依次得到传递过来的实参
剩余参数:
剩余参数允许我们将一个不定数量的参数表示为一个数组。
<script>function config(baseURL, ...other) {console.log(baseURL) // 得到 'http://baidu.com'console.log(other) // other 得到 ['get', 'json']}// 调用函数config('http://baidu.com', 'get', 'json');
</script>
- … 是语法符号,置于最末函数形参之前,用于获取多余的实参
- 借助 … 获取的剩余实参,是个真数组
开发中,还是提倡多使用 剩余参数。
- 剩余参数主要的使用场景是?
用于获取多余的实参。 - 剩余参数和动态参数区别是什么?开发中提倡使用哪一个?
动态参数是伪数组,剩余参数是真数组。开发中推荐使用剩余参数。
展开运算符
展开运算符(…),将一个数组进行展开。可以利用他求数组最大值以及合并数组等操作。
说明:不会修改原数组。
典型运用场景: 求数组最大值(最小值)、合并数组等。
展开运算符 or 剩余参数:
剩余参数:函数参数使用,得到真数组。
展开运算符:数组中使用,数组展开。
箭头函数
目标: 能够熟悉箭头函数不同写法。
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。
使用场景:箭头函数更适用于那些本来需要匿名函数的地方。
学习路径:
- 基本语法
- 箭头函数参数
- 箭头函数this
基本语法
语法1:基本写法
语法2:只有一个参数可以省略小括号
语法3:如果函数体只有一行代码,可以写到一行上,并且无需写 return 直接返回值
语法4:加括号的函数体返回对象字面量表达式
总结
箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上。
<script>// 箭头函数const foo = () => {console.log('^_^ 长相奇怪的函数...');}// 调用函数foo()// 更简洁的语法const form = document.querySelector('form')form.addEventListener('click', ev => ev.preventDefault())
</script>
总结:
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号
()
- 箭头函数函数体只有一行代码时可以省略花括号
{}
,并自动作为返回值被返回 - 箭头函数中没有
arguments
,只能使用...
动态获取实参
箭头函数参数
普通函数有arguments 动态参数,箭头函数没有 arguments 动态参数,但是有 剩余参数 …args。
<script>// 1. 利用箭头函数来求和const getSum = (...arr) => {let sum = 0for (let i = 0; i < arr.length; i++) {sum += arr[i]}return sum}const result = getSum(2, 3, 4)console.log(result) // 9</script>
箭头函数 this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值(谁调用的这个函数,this 就指向谁)。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
在开发中,使用箭头函数前需要考虑函数中 this 的值,事件回调函数使用箭头函数时,this 为全局的 window,因此DOM事件回调函数为了简便,还是不太推荐使用箭头函数。
- 箭头函数里面有this吗?
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this - DOM事件回调函数推荐使用箭头函数吗?
不太推荐,特别是需要用到this的时候。
事件回调函数使用箭头函数时,this 为全局的 window。
解构赋值
目标:知道解构的语法及分类,能够使用解构简洁语法快速为变量赋值。
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。
解构赋值分为:
- 数组解构
- 对象解构
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。如下代码所示:
<script>// 普通的数组let arr = [1, 2, 3];// 批量声明变量 a b c // 同时将数组单元值 1 2 3 依次赋值给变量 a b clet [a, b, c] = arr;console.log(a); // 1console.log(b); // 2console.log(c); // 3
</script>
典型应用:交互2个变量
let a = 1
let b = 2;//这里必须加分号
[b, a] = [a, b]
console.log(a, b)
数组排序的改进写法:
js 前面必须加分号情况:
- 立即执行函数
(function () { })();或者;(function () { })()
- 数组解构
使用数组的时候,当数组前面有代码时,加分号。
const str = 'pink';
[1, 2, 3].map(function (item) {console.log(item)
})
变量多 单元值少的情况:
变量的数量大于单元值数量时,多余的变量将被赋值为 undefined。
const [a, b, c, d] = [1, 2, 3]console.log(a) // 1console.log(b) // 2console.log(c) // 3console.log(d) // undefined
变量少 单元值多的情况:
利用剩余参数解决变量少 单元值多的情况:
const [a, b, ...c] = [1, 2, 3, 4];console.log(a); // 1console.log(b); // 2console.log(c); // [3, 4] 真数组
剩余参数返回的还是一个数组。
防止有undefined传递单元值的情况,可以设置默认值:
const [a = 0, b = 0] = [1, 2];// const [a = 0, b = 0] = [];console.log(a); // 1console.log(b); // 2
允许初始化变量的默认值,且只有单元值为 undefined 时默认值才会生效。
按需导入,忽略某些返回值:
const [a, b, , d] = [1, 2, 3, 4]console.log(a) // 1console.log(b) // 2console.log(d) // 4
支持多维数组的结构:
const [a, b, c] = [1, 2, [3, 4]]console.log(a) // 1console.log(b) // 2console.log(c) // [3,4]
const [a, b, [c, d]] = [1, 2, [3, 4]];console.log(a); // 1console.log(b); // 2console.log(c); // 3console.log(d); // 4
总结:
- 赋值运算符
=
左侧的[]
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量 - 变量的顺序对应数组单元值的位置依次进行赋值操作
- 变量的数量大于单元值数量时,多余的变量将被赋值为
undefined
- 变量的数量小于单元值数量时,可以通过
...
获取剩余单元值,但只能置于最末位 - 允许初始化变量的默认值,且只有单元值为
undefined
时默认值才会生效
注:支持多维解构赋值,比较复杂后续有应用需求时再进一步分析。
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法。
基本语法:
<script>// 普通对象const user = {name: '小明',age: 18};// 批量声明变量 name age// 同时将数组单元值 小明 18 依次赋值给变量 name ageconst {name, age} = userconsole.log(name) // 小明console.log(age) // 18
</script>
注意:
- 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为 undefined
给新的变量名赋值:
可以从一个对象中提取变量并同时修改新的变量名。
const { uname: username, age } = { uname: '蔡文姬', age: 18 }
//对象解构的变量名,可以重新改名。旧变量名: 新变量名
console.log(username) //蔡文姬
console.log(age) //18
冒号表示“什么值:赋值给谁”。
数组对象解构
const pig = [{uname: '佩奇',age: 6}]const [{ uname, age }] = pigconsole.log(uname)console.log(age)
多级对象解构:
const pig = {name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 6}// 多级对象解构const { name, family: { mother, father, sister } } = pigconsole.log(name)console.log(mother)console.log(father)console.log(sister)
数组对象:(格式一样,外层添加[ ]号)
总结:
- 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为 undefined
- 允许初始化变量的默认值,属性不存在或单元值为
undefined
时默认值才会生效
遍历数组 forEach 方法(重点)
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。适合于遍历数组对象。(相当于加强版的for循环)
forEach() 与map()都能遍历数组,但forEach() 只遍历数组,不返回数组;而map()遍历数组并且返回一个数组。
forEach() 方法的主要使用场景: 遍历数组的每个元素。
语法:
例如:
const arr = ['red', 'green', 'pink']arr.forEach(function (item, index) {console.log(item) // 数组元素 red green pinkconsole.log(index) // 索引号})
注意:
- forEach 主要是遍历数组
- 参数当前数组元素是必须要写的, 索引号可选。
筛选数组 filter 方法(重点)
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
主要使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组。
语法:
例:
- filter() 筛选数组
- 返回值:返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组
- 参数:currentValue 必须写, index 可选
- 因为返回新数组,所以不会影响原数组
构造函数&数据常用函数
深入对象
创建对象三种方式
利用对象字面量创建对象
利用 new Object 创建对象
const obj = new Object()obj.uname = 'pink老师'console.log(obj)
或者
const obj = new Object({ uname: 'pink' })console.log(obj)
- 利用构造函数创建对象
构造函数
构造函数 :是一种特殊的函数,主要用来初始化对象。构造函数是专门用于创建对象的函数,如果一个函数使用 new
关键字调用,那么这个函数就是构造函数。
使用场景:常规的 {…} 语法允许创建一个对象。比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过构造函数来快速创建多个类似的对象。
构造函数在技术上是常规函数。不过有两个约定:
- 它们的命名(函数名)以大写字母开头。
- 它们只能由 “new” 操作符来执行。
构造函数语法:名字为大写字母开头的函数。
创建构造函数:
说明:
- 使用 new 关键字调用函数的行为被称为实例化
- 实例化构造函数时没有参数时可以省略 ()
- 构造函数内部无需写return,返回值即为新创建的对象
- 构造函数内部的 return 返回的值无效,所以不要写return
- new Object() new Date() 也是实例化构造函数
实例化执行过程:
说明:
- 创建新的空对象
- 构造函数this指向新对象
- 执行构造函数代码,修改this,给对象添加新的属性
- 返回新对象
总结:
1.构造函数的作用是什么?怎么写呢?
构造函数是来快速创建多个类似的对象;
大写字母开头的函数;(函数名首字母大写)
2. new 关键字调用函数的行为被称为?
实例化;
3. 构造函数内部需要写return吗,返回值是什么?
不需要;
构造函数自动返回创建的新的对象;
实例成员&静态成员
实例成员:
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
<script>// 构造函数function Person() {// 构造函数内部的 this 就是实例对象// 实例对象中动态添加属性this.name = '小明'// 实例对象动态添加方法this.sayHi = function () {console.log('大家好~')}}// 实例化,p1 是实例对象// p1 实际就是 构造函数内部的 thisconst p1 = new Person()console.log(p1)console.log(p1.name) // 访问实例属性p1.sayHi() // 调用实例方法
</script>
说明:
- 实例对象的属性和方法即为实例成员
- 为构造函数传入参数,动态创建结构相同但值不同的对象
- 构造函数创建的实例对象彼此独立互不影响。
静态成员:
在 JavaScript 中底层函数本质上也是对象类型,因此允许直接为函数动态添加属性或方法,构造函数的属性和方法被称为静态成员。
<script>// 构造函数function Person(name, age) {// 省略实例成员}// 静态属性Person.eyes = 2Person.arms = 2// 静态方法Person.walk = function () {console.log('^_^人都会走路...')// this 指向 Personconsole.log(this.eyes)}
</script>
说明:
- 构造函数的属性和方法被称为静态成员
- 一般公共特征的属性或方法静态成员设置为静态成员
- 静态成员方法中的 this 指向构造函数本身
内置构造函数
在 JavaScript 中最主要的数据类型有 6 种,分别是字符串、数值、布尔、undefined、null 和 对象,常见的对象类型数据包括数组和普通对象。其中字符串、数值、布尔、undefined、null 也被称为简单类型或基础类型,对象也被称为引用类型。
在 JavaScript 内置了一些构造函数,绝大部的数据处理都是基于这些构造函数实现的,JavaScript 基础阶段的 Date
就是内置的构造函数。
<script>// 实例化let date = new Date();// date 即为实例对象console.log(date);
</script>
甚至字符串、数值、布尔、数组、普通对象也都有专门的构造函数,用于创建对应类型的数据。
包装类型
在 JavaScript 中的字符串、数值、布尔具有对象的使用特征,如具有属性和方法,如下代码举例:
<script>// 字符串类型const str = 'hello world!'// 统计字符的长度(字符数量)console.log(str.length)// 数值类型const price = 12.345// 保留两位小数price.toFixed(2) // 12.34
</script>
之所以具有对象特征的原因是字符串、数值、布尔类型数据是 JavaScript 底层使用 Object 构造函数“包装”来的,被称为包装类型。
Object
Object
是内置的构造函数,用于创建普通对象。
// 通过构造函数创建普通对象const user = new Object({name: '小明', age: 15})
推荐使用字面量方式声明对象,而不是 Object 构造函数。
// 这种方式声明的变量称为字面量。let student = {name: '杜子腾', age: 21}
三个常用静态方法(静态方法就是只有构造函数Object可以调用的):
新的方法:
作用:Object.keys 静态方法获取对象中所有属性(键);Object.values 静态方法获取对象中所有属性值;Object. assign 静态方法常用于对象拷贝;使用:Object. assign 的经常使用的场景是给对象添加属性。
注意: 返回的是一个数组。
const o = { uname: 'pink', age: 18 }// 1.获得所有的属性名console.log(Object.keys(o)) //返回数组['uname', 'age']// 2. 获得所有的属性值console.log(Object.values(o)) // ['pink', 18]//3.对象拷贝,添加属性Object.assign(o, { gender: '女' })console.log(o)//{ uname: 'pink', age: 18, gender: '女' }
<script>// 对象语法简写let name = '小红';let people = {// 相当于 name: namename,// 相当于 walk: function () {}walk () {console.log('人都要走路...');}}console.log(student.constructor);console.log(user.constructor);console.log(student instanceof Object);
</script>
总结:
- 推荐使用字面量方式声明对象,而不是
Object
构造函数 Object.assign
静态方法创建新的对象Object.keys
静态方法获取对象中所有属性Object.values
静态方法获取对象中所有属性值
Array
Array 是内置的构造函数,用于创建数组。
// 构造函数创建数组let arr = new Array(5, 7, 8);// 字面量方式创建数组let list = ['html', 'css', 'javascript']
创建数组建议使用字面量创建,不用 Array构造函数创建。数组赋值后,无论修改哪个变量另一个对象的数据值也会相当发生改变。
1.数组常见实例方法-核心方法
总结:
- 推荐使用字面量方式声明数组,而不是
Array
构造函数 - 实例方法
forEach
用于遍历数组,替代for
循环 (重点) - 实例方法
filter
过滤数组单元值,生成新数组(重点) - 实例方法
map
迭代原数组,生成新数组(重点)
reduce方法:
作用:reduce 返回函数累计处理的结果,经常用于求和等。
基本语法:
参数:起始值可以省略,如果写就作为第一次累计的起始值。
语法:
累计值参数:
- 如果有起始值,则以起始值为准开始累计, 累计值 = 起始值
- 如果没有起始值, 则累计值以数组的第一个数组元素作为起始值开始累计
- 后面每次遍历就会用后面的数组元素 累计到 累计值 里面 (类似求和里面的 sum )
reduce的使用场景:求和运算:
2. 数组常见方法-其他方法
实例方法
join
数组元素拼接为字符串,返回字符串(重点)实例方法
find
查找元素, 返回符合测试条件的第一个数组元素值,如果没有符合条件的则返回 undefined(重点)实例方法
every
检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回 true,否则返回 false(重点)实例方法
some
检测数组中的元素是否满足指定条件 如果数组中有元素满足条件返回 true,否则返回 false实例方法
concat
合并两个数组,返回生成新数组实例方法
sort
对原数组单元值排序实例方法
splice
删除或替换原数组单元实例方法
reverse
反转数组实例方法
findIndex
查找元素的索引值
数组常见方法- 伪数组转换为真数组
静态方法 Array.from()。
String
String
是内置的构造函数,用于创建字符串。
常见实例方法:
- 实例属性
length
用来获取字符串的度长(重点) - 实例方法
split('分隔符')
用来将字符串拆分成数组(重点) - 实例方法
substring(需要截取的第一个字符的索引[,结束的索引号])
用于字符串截取(重点) - 实例方法
startsWith(检测字符串[, 检测位置索引号])
检测是否以某字符开头(重点) - 实例方法
includes(搜索的字符串[, 检测位置索引号])
判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false(重点) - 实例方法
toUpperCase
用于将字母转换成大写 - 实例方法
toLowerCase
用于将就转换成小写 - 实例方法
indexOf
检测是否包含某字符 - 实例方法
endsWith
检测是否以某字符结尾 - 实例方法
replace
用于替换字符串,支持正则匹配 - 实例方法
match
用于查找字符串,支持正则匹配
注:String 也可以当做普通函数使用,这时它的作用是强制转换成字符串数据类型。
Number
Number 是内置的构造函数,用于创建数值。
// 使用构造函数创建数值let x = new Number('10')let y = new Number(5)// 字面量创建数值let z = 20
总结:
- 推荐使用字面量方式声明数值,而不是
Number
构造函数 - 实例方法
toFixed
用于设置保留小数位的长度
常用方法:
toFixed() 设置保留小数位的长度。
深入面向对象
编程思想(面向对象和面向过程)
面向过程编程
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。
面向对象编程 (oop)
面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
面向对象是以对象功能来划分问题,而不是步骤。
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。
面向对象的特性:
- 封装性
- 继承性
- 多态性
封装的本质是将具有关联的代码组合在一起,其优势是能够保证代码复用且易于维护,函数是最典型也是最基础的代码封装形式,面向对象思想中的封装仍以函数为基础,但提供了更高级的封装形式。
面向过程和面向对象的对比
面向过程编程 :
优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
缺点:没有面向对象易维护、易复用、易扩展。
面向对象编程:
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护。
缺点:性能比面向过程低。
构造函数
封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装。
<script>function Person() {this.name = '佚名';// 设置名字this.setName = function (name) {this.name = name;}// 读取名字this.getName = () => {console.log(this.name);}}// 实例对像,获得了构造函数中封装的所有逻辑let p1 = new Person();p1.setName('小明');console.log(p1.name);// 实例对象let p2 = new Person();console.log(p2.name);
</script>
同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
总结:
- 构造函数体现了面向对象的封装特性
- 构造函数实例创建的对象彼此独立、互不影响
我们学过的构造函数方法很好用,但是 存在浪费内存的问题。
面向对象编程的特性:比如封装性、继承性等,可以借助于构造函数来实现。
原型
原型 prototype
实际上每一个构造函数都有一个名为 prototype
的属性,译成中文是原型的意思,prototype
的是对象数据类型,称为构造函数的原型对象。
<script>function Person() { }// 每个函数都有 prototype 属性console.log(Person.prototype);
</script>
- 构造函数通过原型分配的函数是所有对象所 共享的。
- JavaScript 规定,每一个构造函数都有一个 prototype属性,指向另一个对象,所以我们也称为原型对象 。
- 这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存 。
- 我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
- 构造函数和原型对象中的this 都指向 实例化的对象。
利用原型对象实现方法共享:
- 原型是什么 ?
一个对象,我们也称 prototype 为原型对象 - 原型的作用是什么 ?
共享方法
可以把那些不变的方法,直接定义在 prototype 对象上 - 构造函数和原型里面的this指向谁 ?
实例化的对象
原型- this指向
构造函数和原型对象中的this 都指向 实例化的对象。
constructor 属性
每个原型对象里面都有个constructor 属性(constructor 构造函数)。
作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子。
使用场景:
如果有多个对象的方法,我们可以给原型对象采取对象形式赋值。但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
对象原型
思考:
构造函数可以创建实例对象,构造函数还有一个原型对象,一些公共的属性或者方法可以放到这个原型对象身上。但是为什么实例对象可以访问原型对象里面的属性和方法呢?
对象都会有一个属性 proto 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 proto 原型的存在。
注意:
- proto 是JS非标准属性
- [[prototype]]和__proto__意义相同
- 用来表明当前实例对象指向哪个原型对象prototype
- __proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数
总结:
- prototype是什么?哪里来的?
原型(原型对象)
构造函数都自动有原型 - constructor属性在哪里?作用干啥的?
prototype原型和对象原型__proto__里面都有
都指向创建实例对象/原型的构造函数 - __proto__属性在哪里?指向谁?
在实例对象里面
指向原型 prototype
原型继承
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。
- 封装-抽取公共部分
- 继承
创建对象,将公共的的属性和方法独立出来,然后赋值给构造函数的 prototype
,这样就可以共享公共的属性和方法了,注意让constructor指回这个构造函数。以命名空间的形式实现的继承不常用,事实上 JavaScript 中继承更常见的是借助构造函数来实现。
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链。在 JavaScript 对象中包括了一个非标准的属性 __proto__
它指向了构造函数的原型对象,通过它可以清楚的查看原型对象的链状结构。
原型链-查找规则:
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)
③ 如果还没有就查找原型对象的原型(Object的原型对象)
④ 依此类推一直找到 Object 为止(null)
⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
JavaScript 高级(进阶)相关推荐
- Javascript——高级进阶
1.基础总结深入 1.1.数据类型 1.1.1.分类 --基本(值)类型 string:任意字符串 Number:任意的数字 Bloolean:true/false underfined:underf ...
- JavaScript高级进阶之设计模式 抽象工厂模式
抽象工厂模式 负责同一类的产品,但是不对负责某一个产品的实例,实例的任务交给创建某个产品的方法 /*** @param {Object} fun 需要抽象的方法体 函数* @param {Object ...
- JavaScript 高级3 :函数进阶
JavaScript 高级3 :函数进阶 Date: January 19, 2023 Text: 函数的定义和调用.this.严格模式.高阶函数.闭包.递归 目标: 能够说出函数的多种定义和调用方式 ...
- 尚硅谷JavaScript高级教程(javascript实战进阶)学习笔记
前言 这个是我学习过程中的笔记,分享给大家,希望对大家有用. 学习内容是尚硅谷JavaScript高级教程(javascript实战进阶),这里是视频链接. 我在前面有两篇对于web前端HTML和CS ...
- JavaScript高级之函数进阶
1. 函数的定义和调用 1.1 函数的定义方式 函数声明方式 function 关键字 (命名函数) 函数表达式 (匿名函数) new Function() Function 里面参数都必须是字符串格 ...
- javascript 高级程序设计_JavaScript 经典「红宝书」,几代前端人的入门选择
人的一生中总要读几本经典书,在这个"经典"泛滥的年代,什么才是权威的代表,我想大概是一本的书的口碑,能积累下上佳口碑的书,往往也是能经得住时间推敲的.比如这本: 相信许多前端开发者 ...
- javascript高级程序设计pdf_一个老牌程序员推荐的JavaScript的书籍,看了真的不后悔!...
很多人问我怎么学前端?我的回答是:读书吧!相对于在网上学习,在项目中学习和跟着有经验的同事学习,书中有着相对完整的知识体系,每读一本好书都会带来一次全面的提高.而如果深一脚浅一脚的学习,写出代码的质量 ...
- javascript高级编程教程,javascript基础入门案例
谁有比较好的javascript视频教程 李炎恢的javascript教程,在verycd上可以下载. 结合<javascript高级程序设计>学习,应该会比较好,他这个教程就是参考了&l ...
- 第九章 模板高级进阶
2019独角兽企业重金招聘Python工程师标准>>> 第九章 模板高级进阶 虽然大多数和Django模板语言的交互都是模板作者的工作,但你可能想定制和扩展模板引擎,让它做一些它不能 ...
最新文章
- STP 根桥、根port、指定port是怎样选举的
- lz0-007 读书笔记09
- usaco Packing Rectangles
- Hardware assisted virtualization and data execution protection must be enabled in the BIOS
- 前端学习(3067):vue+element今日头条管理-频道筛选完成
- 全链路数据血缘在满帮的实践
- linux 开机默认启动windows系统时间,Windows和Linux双系统批改默认启动项、超时时间...
- python自动化办公真的好用吗-python如何实现自动化办公?
- vue设置页面滚动高度_vue中获取滚动高度或指定滚动到某位置
- Beyond Compare软件进行代码比对
- 百度地图API秘钥问题
- [转]现代密码学实践指南
- 优雅写代码的45个小技巧
- 火车采集器采集ajax,火车头采集器JavaScript之ajax网站采集实例教程
- mysql精度_MySQL几种数据类型精度和标度的情况
- 群晖Docker百度网盘套件一直灰界面
- 微服务架构(Microservices)
- OllyDBG 破解入门教程
- jsp空间租用应该注意两个问题
- 基于Transformer的翻译模型(英->中)
热门文章
- 如何用多台机器产生agent到A上。
- 25匹马,5个跑道,每次只能跑5匹,用最少的次数选出最快的前3匹
- ​吃了假辣椒酱的企鹅,你遇到过哪些被骗的经历?
- Excel2007版的常用功能(7):Excel公式与函数
- 什么是“黑天鹅现象”?
- 《zw版·Halcon-delphi系列原创教程》 Halcon分类函数002·AI人工智能
- 恶意软件与反病毒技术
- DICOM:基于JMeter+dcm4che2测试PACS服务器性能的解决方案(前篇)
- VS 给自己开发的Qt程序加上程序图标ico
- java+mysql基于ssm的游戏光盘租赁系统(java,web)