JSCore01
第三阶段
-
JS高级 --- 面试时重点考察的 JS 核心技巧 都在这里 (难度极高)
-
学习技巧: 重在理解, 不要边看边写
-
-
DOM -- 用JS灵活操作 HTML+CSS 的技术
-
可以让网页变化起来
-
学习技巧: 多写多练
-
-
jQuery
-
Vue
声明提升
亮亮老师阶段: var 声明的变量 带有声明提升
学习 JS语言 必须知道的一个 语言特征:
其他
编程语言都没有
的特征为什么其他语言都没有这个特征?? 因为 此特征特别
垃圾
JS语言在运行时, 实际代码分两次运行.
第一次: 先找到所有的声明操作(
var/function/let/const/class
), 提取到顶部第二次: 再执行提升后的代码
面试题中经常考:
考察你的JS基础如何!
JS中:眼见不一定为实, 底层会隐式 先提升
再执行
提升后的代码
var 和 function 都是声明, 谁先谁后?
JS的作者从来没有明确说明过到底谁先谁后
但是先后顺序不重要
函数提升时 会携带函数体
var提升时, 只提升变量声明没有值
不管谁先写谁后写, 提升后的值 都是函数
function a(){} var a var b function b(){}
作用域
作用域 是 一类拥有特殊操作的 对象类型的 称呼
全局
局部/函数
块级
脚本
宿主环境: JS运行在哪个平台上, 这个平台就称为 JS的宿主环境
-
node.js: 操作数据库/网络请求/操作文件系统...
-
浏览器: 提供了操作浏览器相关的
技能包
-
存储在 window 对象中, 此对象也称为
全局作用域
-
因为 利用
var/function
声明的变量 都会自动存储在window里
-
var 具有
全局变量污染
特征:
全局: window
var声明的变量会自动存储在 window 里,导致window原有的结构被污染了!
函数作用域
函数在运行
时, 会自动创建一个对象
, 用来保存函数中
声明的变量
这个特殊的对象就称为: 函数作用域 也叫 局部
作用域
局部作用域 和 全局作用域 是独立关系, 互相不影响
因为函数运行时创建 作用域对象, 在 函数运行结束后 会
自动销毁
所以, 必须利用
断点调试
功能才能查看到 这个特殊的作用域对象
脚本作用域
块级作用域
作用域链
闭包
闭包 Closure, 就是函数作用域的别名
当一个函数出生时, 会自动保存其所在的 词法作用域
(所在的作用域们)
存储在函数的 scopes
属性里
为什么要存??? 以后函数不管在哪里使用, 永远都是曾经的少年--携带固有属性
这些固有的作用域中, 函数作用域
称为 闭包
当你听到
闭包
这个称号: 就应该知道 这是个函数作用域 被保存在另一个函数的 scopes 里了
例如案例中,
c
函数在b
函数里声明的,b
函数作用域保存在c
的scopes
里此时
b
函数作用域 就是c
函数的闭包
今日内容回顾
JS高级: 了解JS的本质,底层设计 -- 面试必考
-
声明提升
-
JS语言独有的特征, 其他语言没有 -- 此设计特别
渣
-
JS代码在真正运行时, 会先调整代码的结构, 然后才会执行
-
把 声明操作(
var/function/let/const
) 提升到作用域的顶部, 然后再执行 -
注意
:千万不要
按照正常人从上到下
的顺序来阅读JS代码
-
-
作用域
-
作用域的本质就是个对象类型的变量, 只是因为这个对象类型特殊
-
window: 浏览器提供的对象类型, 存储了所有浏览器的功能代码. 称为全局对象
-
使用 var 和 function 声明的变量, 自动存储在window里
-
-
局部作用域: 函数在调用时, 会自动产生一个对象类型, 用于存储函数中声明的变量, 其会在函数执行
完毕
时,自动销毁
-
脚本作用域: ES6 2015年开始, 使用
let /const / class
在 script 中声明的, 存储在脚本区 -
块级: ES6 2015年开始, 在
{}
中 使用let/const/class
关键词声明的变量, 存储在 块级作用域 对象里
-
-
作用域链
-
根据代码的书写, 函数中使用一个变量时, 优先从自身作用域中查找, 自身没有 则到
上层
作用域中查找 -- 直到找到位置(就近原则)
-
-
闭包
-
函数在声明时,会保存其所在的
词法环境(所有父作用域)
到 scopes 属性里 -
如果是函数作用域, 就称为闭包
-
简单粗暴理解: 外层函数 是内层函数的闭包 -- 必须是函数套函数的方式
-
缺点: 浪费内存. 外层函数的作用域不会自动销毁, 会被内层函数保存
-
作用: 在ES6 2015年之前, 用于避免全局变量污染.
-
利用闭包, 为函数提供私有的变量, 避免全局变量污染
-
-
今日代码
声明提升
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>声明提升</title>
</head>
<body>
<!-- HTML中, 有两个特殊的标签 -->
<!-- style: 其中用于书写 css 代码 -->
<!-- script: 其中用于书写 JavaScript 代码 -->
<script>
// function: 用于声明函数
// 函数就是把 一段代码保存起来, 起了个名字
// 注意: 函数必须调用之后, 才能执行{}中的代码
// 调用函数的格式: 函数名(). 通过()来触发
// JS的反人类设计: 不能按照正常人的阅读顺序来理解代码
// 你所见到的 并非 JS 实际运行时的模样
// 因为JS拥有: 声明提升的机制
// 声明: 制作出来一个变量 就叫声明 -- 变量出生的时候
// 用于声明变量的关键词: var/function + let/const/class
a()
function a(){
console.log(111)
}
var a //声明了变量a
var b //声明了变量b
function c(){} //声明了函数c
</script>
<script>
// 在阅读JS代码时, 一定不要当正常人
// 正常人: 从上向下阅读
// JS具有声明提升特征: 先隐式调整代码的顺序, 然后再执行
function b(){
console.log(111)
}
b()
function b(){
console.log(222)
}
b()
</script>
</body>
</html>
小练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
a()
function a() {
console.log(111);
}
function a() {
console.log(222);
}
a()
a = function () { console.log(333) }
a()
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// = :称为赋值符号. 把一个值保存到一个变量里
// 如果变量中 已经有值了 会怎样, 旧的会清理掉, 换成新的
var a = 5
var a = 8
console.log(a);
function b() { }
console.log(b);
b = 666
console.log(b);
// 函数作为 表达式的一部分, 称为函数表达式
// 表达式中的函数: 不会提升
b = function () { console.log(222); }
console.log(b);
// 整个表达式 就是一个函数, 可以提升
function c() { }
</script>
<script>
console.log('-------------------------');
ff = function () { console.log(222); }
ff()
function ff() {
console.log(1111);
}
ff()
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 此处是合写格式
// var a; a = 5;
var a = 5
function a() {
console.log(111);
}
console.log(a);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 为一个变量重新赋值, 只能用 = 进行
function a() { }
// 声明a, 如果a中没有值, 就是undefined
// 而不是: 声明a的同时 赋值成 undefined
// 但是 a 中存储了函数, 所以a的值就是函数
var a //这是赋值吗?? 不是!!! 声明操作
// 没有 = 就不会给a赋值
console.log(a);
</script>
</body>
</html>
作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作用域</title>
</head>
<body>
<script>
// JS有4种作用域: 全局 函数 脚本 块级
// 专业名词: 宿主环境 -- 被寄生的就叫宿主
// JS语言有两种常见的宿主: node.js 和 浏览器
// JS寄生在宿主上之后, 就可以使用宿主的各种功能
// JS寄生在浏览器上时, 就拥有了操作浏览器的各种技能
// 这些技能存储在 浏览器提供的 window 对象属性中
console.log(window);
console.log(typeof window); // object:对象类型
// 作用域 就是 一个特殊的对象类型的值
// window对象,就是浏览器提供的全局作用域
// 使用 var 在脚本中声明的变量, 就会存储在全局作用域 window对象里
var aa = 'AAA'
function aab() { }
// 用法1: 读取 全局作用域 window对象中的值
console.log(window.aa);
// 用法2: 直接用变量, 默认会从全局作用域获取
console.log(aa);
</script>
</body>
</html>
局部作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局部作用域</title>
</head>
<body>
<script>
// 2015年之前, 即ES6 -- JS的第六次升级版本 前
// 只有两种作用域: 全局 , 局部(函数)
function a() {
var x = 11
var y = 22
var z = 33
console.log(x, y, z);
}
// 注意: 函数需要调用才能触发
// 调用时, 会触发 {} 中的代码, 并制作一个 局部作用域--对象类型
// 需要浏览器提供的 断掉调试 软件, 来查看函数运行时的作用域
a()
</script>
</body>
</html>
脚本作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>脚本作用域</title>
</head>
<body>
<!--
由于 var 在 script 中声明的变量, 自动出现在 window 对象里,
而 window 对象是浏览器提供的 用于存储 系统方法 的对象
把自定义的变量存储在 window 中, 会造成全局变量污染
所在 从 2015年开始 的 第六个JS版本, 称为 ES6
提供了新的脚本作用域, 专门存储 自定义的 变量
-->
<script>
// var在 script 声明的变量, 存储在 window 对象里
// 和系统提供的内容 混合在一起: 全局变量污染
var aa = 'AAA'
// let/const : ES6中提供的 声明变量的 关键词
// 在 script 中声明的变量, 存储在 script 对象里 而非 window
// 避免的全局变量污染
let bb = 'BBB'
// 取舍:
// 理论上: 推荐用 let/const 代替 var -- var就特性,被淘汰!
// 实际上: 为了兼容性的考虑, 对于2015年之前的浏览器--IE8
// -- 有些政府项目中, 要考虑适配 旧版本浏览器, 只能用var
console.log('-----');
</script>
</body>
</html>
块级作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>块级作用域</title>
</head>
<body>
<script>
// 块级: 由 ES6 中提供的作用域, 类似 局部作用域
// {} 配合 let/const 使用, 就会生成独立的块级作用域对象
// 但是 var 对块级无效, 依然在全局中
// 代码中: if for while switch 都有 {}, 会产生块级
{
let a = 5
const b = 6
var c = 7
console.log('....');
}
function d() {
// 函数的{} 不会产生块
// 依然是 函数作用域
let a
{
// 依然形成块级作用域
let b
console.log(b);
}
console.log(a);
}
d()
</script>
</body>
</html>
作用域链
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作用域链</title>
</head>
<body>
<script>
// JS中, 代码结尾的分号 非必备, 是可选的
// 由于大家越来越懒.. 现在很多人习惯不写 分号
// undefined: 声明了但是没赋值
// undeclare: a is not defined 代表变量不存在, 即没声明
// 当使用一个变量时, 会按照就近原则, 到作用域链中查找并使用
// 细节: 查找时 向上层作用有中查找
// 找祖宗 不找 儿子
var a = 11
function b() {
var a = 22;
// 函数的自调用 (function(){})()
// 细节: 自调用写法 和 上一行代码之间 必须有 分号 间隔
(function () {
var a = 33
function c() {
var a = 44
}
c()
// 如果 a = 33 不写, 会到上层作用域中查找
// 不会到 c 中查找
console.log(a) //??
})()
}
b()
</script>
</body>
</html>
综合练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>综合练习</title>
</head>
<body>
<!-- 声明提升: JS独有的特征, 先隐式调整代码的结构, 把 声明操作 var/function 提升到作用域的顶部, 然后再运行 -->
<!-- 作用域: 一些特殊的对象. window全局 函数触发后-局部 -->
<!-- 作用域链: 使用一个变量时, 就近原则 从自身向上层作用域查找 -->
<script>
var a = 10
function b() {
a = 20
console.log(a);
}
b()
console.log(a);
</script>
<script>
// 代码提升后的样子如下:
function b() {
// 变量a在哪?? 函数自身没有用 var 声明变量a
// 所以 到上层作用域中查找: 找到了window中的a
a = 20 // 把 a 的值替换成 20
console.log(a) // 打印出20
}
// var a = 10
var a
//
a = 10
b() //触发函数体中的代码
console.log(a);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>综合练习</title>
</head>
<body>
<script>
var a = 10
function b() {
// var: 用于在当前作用域中声明变量
// 当前在函数作用域中, 所以声明了变量a
// var a = 20
var a
// 因为当前作用域中有 a , 所以就给当前作用域的a赋值
a = 20
console.log(a) //20
}
b()
console.log(a);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>综合练习</title>
</head>
<body>
<script>
var a = 10
function b() {
// 考点: 声明提升; var a 实际运行时会先提升到作用域的顶部
// var a
a = 20
console.log(a);
var a
}
b()
console.log(a);
</script>
</body>
</html>
闭包特性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包特征</title>
</head>
<body>
<script>
// 类似: 亮亮出生时 在 成家, 所以姓成
// 如果亮亮以后 入赘到 涛哥家里.. 是否依然姓成?
// 答案: 依然是, 出生时就锁死. 携带在自身的固有属性
// 函数 在声明时, 即 function 书写时, 就已经把自身要用到的各种变量 都携带在身上, 不管以后在哪里使用, 都是固有的属性
// scope: 作用域 scopes:作用域们
// 函数的 scopes 属性中, 存储来函数 出生时, 所在的作用域们
// closure: 闭包 -- 函数作用域的别名
// global: 全局
var a = 10
function b() {
var a = 20
// c出生时: 在b作用域中, b出生时在全局里
function c() {
// 这里的a ,根据作用域链的元素, 使用 b函数作用域中的a
// 但是 把 c 作为返回值, 传递到外部, 为什么依然能打印出a?
console.log(a) //打印出什么?
}
return c // 返回c函数
}
var c = b() // b的返回值, 存储在 变量c 中
console.log(c);
c() // 触发全局中的c函数 打印出什么?
// 打印有很多种方式, 最常见:log --具有美化功能
// console.log(c);
// dir: direct直接, 输入函数原本的模样
console.dir(c);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包特征</title>
</head>
<body>
<!--
JS的固有设定: 函数在出生时 会保存 其所在的所有作用域 到 函数的scopes 属性里
例如: 亮亮老师在出生的时候, 就会保存: 爸爸是谁,妈妈是谁, 爷爷奶奶, 姥爷姥姥 , 北京的5套房子. 永远都携带在身上
-->
<script>
{
// let 配合 {} 会形成块级作用域
let a = 10
// alt+上/下 : 移动当前行
// 把函数c 放在另一个函数中声明
function b() {
var x = 999
// b函数作用域 是 c函数的闭包, 存储在 c的 scopes 属性里
function c() {
// 细节: 函数只存对自身有用的,
// 如果没有使用变量a, 则不会存储a 所在的作用域
console.log(a, x)
}
console.dir(c) //利用dir 直接查看函数本体, 展开查看 scopes 属性
}
b()
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包特性</title>
</head>
<body>
<!--
闭包: 函数在声明时, 会保存其所在的所有作用域 到 scopes 属性里, 如果这个作用域 是个函数作用域, 称为 闭包Closure
粗暴理解: 函数保存所有的父元素到自身的scopes属性里
闭包的形成: 必须是 函数中声明另一个函数
外层的函数 是 内层函数的 闭包
-->
<script>
// Closure:闭包
(function () {
var a = 10
// 声明函数,
function b() { console.log(a) }
// b的scopes属性中存储了 b函数声明时所在的作用域们 (祖先们)
// 匿名函数作用域 被保存在 b 的scopes 中, 称为 b的闭包
// 本质上 闭包就是函数作用域, 只是因为被b存储了, 所以称为闭包
// 例如: 迪丽热巴. 假如 嫁给了家乐, 称为 家乐的妻子
// 迪丽热巴 别名是 妻子. 说明是别人的妻子
console.dir(b)
})()
</script>
</body>
</html>
关于函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于函数</title>
</head>
<body>
<script>
// 关于函数: 分两种状态 -- 静态 动态
// 静态的: 制作了一个函数
function b() {
// return 返回结果
// 不写return 则默认是 undefined
// return 111
}
// 打印静态的函数
console.log(b);
// 动态: 利用()触发; 打印函数运行后的
console.log(b());
</script>
</body>
</html>
闭包的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包的应用</title>
</head>
<body>
<!--
闭包的设计的缺点:
- 正常 函数执行时 会形成 局部作用域对象, 当函数执行结束时, 这个对象会销毁, 来节省内存
- 但是: 闭包机制会导致 函数作用域 被保存到另一个函数中, 无法释放. 导致内存的浪费
所以: 在ES6中, 提供了 块级作用域的写法, 来代替闭包
由于兼容性 和 习惯: 闭包依然非常常见
-->
<script>
// 闭包作用: 为函数提供私有变量, 避免全局污染 -- ES6之前
// 例如: 记录一个函数的调用次数
// var声明的变量, 存储在 window 全局中: 污染全局变量
// 如何避免全局污染?? let/const -- ES6 2015年出现
// 2015年之前 靠什么? 需要闭包 -- 提供一个函数作用域来存储变量
console.log(window);
// 习惯使用匿名函数自调用, 来提供一个函数作用域
// 声明变量, 保存函数的返回值; 此时就可以在外部使用b
var b = (function () {
// 在匿名函数作用域中, 声明变量: 不在全局区 就没有全局污染
var count = 0
function b() {
// var count = 0
count++
console.log('调用了第' + count + '次');
}
// 通过return 把b暴露到外部 -- return 大腰子
return b
})()
// 目的是:让count变成私有的, 避免全局污染
console.dir(b) // 查看 count 是不是存在 scopes 里
b()
b()
b()
b()
</script>
</body>
</html>
JSCORE02
JS高级部分: 重在理解
复习
JS高级部分主要讲解: JS底层的设计理念 -- 面试必考
-
声明提升
-
JavaScript语言 在实际运行时的方式 --
不要看表面
-
JS代码在实际运行时, 会先
隐式
调整代码结构, 然后再执行调整过后的代码:如果按照代码从上向下阅读, 并不准确
-
声明
操作的代码, 会提升到其作用域的顶部, 然后再执行-
声明操作:
var/function/let/const/class
-
-
考点: JS代码运行的 和 你所见的 并非同一个, 隐式调整结构
-
-
作用域
-
作用域本质是个
对象类型
的变量, 只是因为这个对象类型拥有特殊的功能 -
4种作用域对象
-
全局作用域对象: 浏览器作为
宿主环境
时, 就是 window 属性-
使用
var
在 script 中声明的变量 存储在 window 里. 所以有全局变量污染
-
-
局部作用域对象: 函数在 被
触发
时, 会临时
生成一个对象, 存储函数中声明的变量. 在函数执行结束时,自动销毁
-
脚本作用域对象:
ES6 2015 JS的第6个版本
使用let/const
关键词在 script 里声明的变量 -
块级作用域:
{}
配合let/const
使用时, 会形成块级作用域-
if/for/switch/while 都带有{} 会形成块级作用域
-
-
-
-
作用域链
-
按照代码的书写位置, 函数中使用一个变量时, 会先在
自身作用域
中查找, 如果自身没有 则向上级作用域
查找 --就近原则
-
-
闭包
-
函数在声明时, 会保存其所在的
词法作用域
到自身的 scopes 属性里. 其中如果词法作用域是函数作用域
的话, 就称为闭包-
词法作用域
有4类: 全局, 脚本, 块级,闭包(局部作用域/函数作用域)
-
闭包本质就是 局部作用域, 只是在当前场景中, 称为闭包
-
闭包的形成: 必须是 外部函数 中声明了 内部函数, 此时 外部函数就是内部函数的闭包
-
闭包的缺点: 本来函数作用域会自动销毁, 由于闭包的保存操作, 不再自动销毁, 浪费内存!
-
函数分两种状态: 静态 动态
-
静态: 声明函数 -- 闭包存储在静态的函数中
-
动态: 调用函数 -- 执行的时候会使用到闭包里的值
-
-
-
闭包的作用:
-
在
ES6
之前, 只有两种作用域: 全局 / 局部(函数) -
通过var 声明的变量, 存储在全局里, 造成全局变量污染
-
闭包
: 可以把变量声明在函数作用域中, 避免全局变量污染-
制作
私有的变量
, 规避全局变量污染
-
-
-
arguments
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数的arguments</title>
</head>
<body>
<script>
// 计算任意数量 参数的和
function add() {
// 函数隐式带有一个 arguments 属性, 其中存储了函数收到的所有参数
// 通过打印可以发现: arguments 是个对象类型
// 只是属性名是 0 1 2 3 ... 跟数组相似而已
// 我们称这种样子的对象: 伪数组/ 类(似)数组
// 像数组 但是没有数组的各种方法 -- 太监 - 像男人..但是不行
console.log('arguments:', arguments);
var sum = 0
for (var i = 0; i < arguments.length; i++) {
var value = arguments[i] //下标取值
sum += value //累加到sum上 sum = sum + value
}
console.log(sum);
}
add(10, 20)
add(100, 200, 10, 20)
add(100, 200, 10, 20, 40, 50)
</script>
</body>
</html>
函数的重载
函数的一种制作的技巧: 可以让一个函数 拥有更多的功能, 更加强大
通过判断参数的 个数 或 类型, 来执行不同的逻辑代码
具体实现方式:
依赖函数自带的 arguments 属性: 存储函数接收的所有参数
利用 arguments 来判断 函数接受的参数数量
或者 从arguments 中读取参数, 判断其类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数的重载 10:07</title>
</head>
<body>
<!-- 函数的重载: 通过判断参数的 数量 或 类型 来执行不同的逻辑代码 -->
<!-- 优点: 让一个函数具有多种作用, 功能更加强大 -->
<!-- 具体实现方式: 利用 arguments 属性, 来读取参数的个数 或者 某个参数的类型, 通过if判断来执行不同的逻辑代码 -->
<script>
// 参数是数字类型, 当做时间戳(距离1970年1月1日的秒数)来识别
// 系统提供的 Date, 其会根据参数的类型不同, 用不同的方式进行转换, 最终得到时间
var t = new Date(1453243343453)
console.log(t);
var t = new Date('2020/10/20')
console.log(t);
// 制作一个求和的函数, 两种作用: 求出多个参数的和 或 数组的和
function add() {
// 参数的个数如果是1个 且 是数组类型
// console.log(arguments);
// Array.isArray(): 判断值是否为数组类型
if (arguments.length == 1 && Array.isArray(arguments[0])) {
// arguments = { 0: [11, 22, 33, 44, 66] }
var nums = arguments[0]
// nums = [11, 22, 33, 44, 66]
var sum = 0
// 数组.length: 获取数组的长度
for (var i = 0; i < nums.length; i++) {
sum += nums[i]
}
console.log(sum);
} else {
// 否则: 一堆数字的场景
// 例如 arguments: {0: 10, 1: 20, 2: 30, 3: 40, length: 4}
var sum = 0
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
console.log(sum);
}
}
// add: 可以传递一堆数字 或 传递 一个数组
add(10)
add(10, 20, 30, 40)
add(10, 20, 30, 40, 6, 7, 8)
add([11, 22, 33, 44, 66])
</script>
<script>
// 练习: 制作一个函数, 计算出数字的累加之和
function getSum() {
console.log(arguments);
// arguments: 接受到的参数们
if (arguments.length == 1) {
var sum = 0
// 看下成亮老师讲解的 对象的 下标取值 -- 对象访问器语法
for (var i = 1; i <= arguments[0]; i++) {
sum += i
}
console.log(sum);
}
if (arguments.length == 2) {
var sum = 0
for (var i = arguments[0]; i <= arguments[1]; i++) {
sum += i
}
console.log(sum);
}
if (arguments.length == 3) {
var sum = 0
for (var i = arguments[0]; i <= arguments[1]; i += arguments[2]) {
sum += i
}
console.log(sum);
}
}
// 升级: 如果3个参数, 第三参数代表步长 50 52 54 56... 间隔2
getSum(50, 200, 2)
getSum(50, 200, 3)
// 例如:
getSum(100) //累加出 1 - 100 之间数字的总和
getSum(50, 150) // 累加出 50 - 150 之间的数字总和
getSum(150, 10)
</script>
</body>
</html>
函数部分--高级操作
-
函数作用域: 函数触发时自动创建的对象, 用于存储函数中声明的变量
-
作用域链: 函数中使用的变量, 优先使用自身作用域的, 如果自己没有则到上层查找
-
闭包: 函数在声明时, 把 其所在的
作用域们
, 保存到scopes属性里-
作用域们中的 : 函数作用域 叫闭包
-
-
函数隐式具有arguments属性: 存储了函数
接受
到的所有实参
, 类(似)数组类型. 像数组但是没有数组的各种方法 -- 太监: 像男人,但... -
技巧-函数重载: 通过判断函数的参数 个数 或 类型, 来执行不同的逻辑代码
-
具体实现:依赖 arguments 属性来进行
-
属性访问器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象的访问器语法</title>
</head>
<body>
<script>
// 对象的访问器语法
// JS官方文档: MDN - https://developer.mozilla.org/zh-CN/
// JS提供了两种 属性访问器 语法, 分别是 点号 和 方括号
var emp = { ename: 'mike', age: 19, 1: 'one' }
console.log(emp);
// 读取ename属性的值
console.log(emp.ename) //点语法读取
console.log(emp['ename']) //方括号语法
// 如何读取 属性1 的值
// console.log(emp.1); // .1 是小数的表达方式 11.2
console.log(emp[1]); // 数字只能用 方括号语法
// 错误理解: []下标取值 是数组特有的语法!
// 正确理解: 由于数组类型的对象, 其下标都是 数字类型, 无法使用 .语法 只能用[]
console.log(['mike', 'lucy', 'lily']);
// 数组 和 对象的关系是? 数组是对象的一种
// 就好像 人 和 男人的关系. 男人 是 人的一种
var stu = {}
var a = 'gege'
// []: 里面是JS代码, a是变量, 其中的值是 'gege'
stu[a] = '格格'
// 问 : 格格这个值的 属性名是什么?
// 选项1: a 选项2: gege.
stu['a'] = '子轩'
// 问: 子轩的属性名是?
// 选项1: a 选项2: 'gege'
var a = 'gg'
stu.a = '家乐'
// 问: 家乐的属性名是? 1
// 选项1: a 选项2: gg
// 语法
// 对象.属性名 : 在点语法中, 就是个属性名, 不会变化
// 对象[]: 方括号中是 JS 代码
console.log(stu);
</script>
</body>
</html>
对象是引用类型
内存分两种:
把内存想想成是一本字典/书: 分两个部分 目录 和 详情页
为什么?
: 目录记录简单的标题, 其中存储 详情的页数, 通过页数找到存放详细内容的页
目录的内容少, 查询速度快
栈内存: 相当于目录, 存储少量数据, 查询速度快
堆内存: 相当于详情页, 存储大量数据, 拥有一个固定的页数
在JS中, 基础数据类型属于少量数据的范畴
-
number: 123, 12.3, 456
-
boolean:
true
false
-
null
-
undefined
-
string: 字符串 是由多个
单个
字符组成的
基本数据类型, 赋值时, 值传递
对象类型: 属于存储很多很多数据的类型,
{}
中可以存储任意数量的数据在内存中存储在
堆内存
里, 相当于 书本中的详情页
, 带有一个页号var obj ={ xx: xxx, xx: xxx, xxx:xxx }
对象是引用(内存地址
)类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象是引用类型</title>
</head>
<body>
<!-- JS的数据类型有哪些? -->
<!-- 基本数据类型: string boolean number null undefined + symbol bigInt -->
<!-- 在 ES6 2015年 新增了 symbol(唯一,做属性名用) bigInt(大整型) -->
<!-- 引用/对象类型: object -->
<script>
// 猜猜 b 的值
// 数字属于 number类型, 属于基础数据类型 -- 赋值时 属于 值传递
var a = 5
var b = a
a = 10
console.log(b) //5
// 猜猜打印的结果
var x = { num: 10 }
var y = x
x.num = 20
console.log(y.num);
</script>
</body>
</html>
拷贝/克隆
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象的克隆</title>
</head>
<body>
<!-- 克隆: clone --复制,拷贝 -->
<script>
var x = { num: 10, ename: 'mike', age: 18 }
// 克隆 x 对象: 分两步
// 1. 创建1个空对象
var y = {}
// 2. 遍历x对象, 把其中的属性挨个存入空对象里
// for..in.. 用于遍历对象
for (var key in x) {
// key: 是 x 对象中的属性
console.log('key:', key);
var value = x[key] // 对象 方括号取值
// 把值存储在 y 这个空对象里
y[key] = value
}
console.log('x:', x);
console.log('y:', y);
// 对比是否是同一个
console.log('x == y:', x == y) //false: 不是同一个
// 因为 x 和y 非同一个, 所以x的修改 不会影响 y
x.age = 99
console.log(y.age) // 18
</script>
<script>
// 扩展小课堂:
var a = { num: 10, count: 20 }
var num = 'count'
// 纯语法问题: -- 成亮老师的JS基础有讲解!
// 对象.属性名 : 点后面的就是属性名, 不是js代码
// 对象[JS代码]
console.log(a.num) //10
// a[num] -> a['count']
console.log(a[num]) //20
</script>
</body>
</html>
函数的this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>this关键词</title>
</head>
<body>
<!-- this: 代表函数运行时所在的对象 -->
<!--
关键词 `我` : 代表说话时 的 人
亮亮: 我今天要给 2204班上课
娅楠: 我今天休息
铭铭: 我今天被隔离, 在家直播
这些 `我` 字 都代表什么?
-->
<script>
// 全局中声明, 存储在window里
function show() {
// this: 这个
console.log('我是:', this);
}
show() // 猜猜this
var emp1 = { ename: '亮亮' }
emp1.show = show // show函数存储到 emp1 里
emp1.show()
var emp2 = { ename: '娅楠', show: show }
emp2.show()
var a = 10
var b = 20
function c() { console.log(this) }
// 属性名: 值(JS代码)
// 简化语法: 属性名和值一样, 可以简化成一个 {b:b} -> {b}
var obj = { num: 10, count: a, b, c: c }
// 相当于
obj.b = b
obj.c() // 打印的this是什么
console.log(obj)
// 粗暴理解:
// 对象.方法() : 方法中的this 就是前面的对象
// 我.打亮亮() : 主语是 我
// 涛哥.打亮亮() : 主语是 涛哥
// 楠姐.打亮亮() : 主语是 楠姐
// emp2.打亮亮() : this 就是 emp2
// emp3.打亮亮() : this 就是 emp3
var e1 = {
name: '楠姐',
打亮亮: function () {
console.log(this, "在打亮亮");
}
}
e1.打亮亮()
// 函数中的this: 代表谁在使用
var e2 = { name: '涛哥', 打亮亮: e1.打亮亮 }
e2.打亮亮()
// 面试的考点: 存储在window中的属性, 在使用时可以省略前缀
var 打亮亮 = e1.打亮亮
// window.打亮亮()
打亮亮() //没有前缀 触发的方法, 都是window里的
</script>
</body>
</html>
对象的创建方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象的创建方式</title>
</head>
<body>
<!-- 语法糖: 作者提供的简化语法, 使用起来非常方便, 让人感觉幸福, 所以像吃糖一样甜蜜! -->
<!-- 创建一个对象, 通常分两种方式 -->
<!-- 1. 字面量: 一种语法糖,简化创建 -->
<!-- 2. 构造方式 -->
<script>
// 数组的语法糖: 字面量
var names = ['mike', 'lucy', 'lily']
console.log(names);
// 构造方式
var names = new Array('tom', 'jerry', 'shirley')
console.log(names);
// 对象类型
var emp = { name: '亚楠', age: 18, phone: '18332434344' }
console.log(emp);
// 构造方式
var emp = new Object()
emp.name = '凯凯'
emp.age = 32
emp['phone'] = '15534343444'
console.log(emp);
// 函数
function aa(a, b) { console.log(a + b) }
// 函数的构造写法: 前几个参数 是形参, 最后一个是函数体
var bb = new Function('a', 'b', 'console.log(a + b)')
console.log(bb);
bb(20, 40)
// 下节课主题: 我们自己做构造函数试一试
</script>
</body>
</html>
this的作用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>this的作用 16:10</title>
<!-- 在对象中复用函数,省内存 -->
</head>
<body>
<script>
// 矩形
// 1个矩形, 长10 宽5 拥有方法area 来计算面积
var r1 = {
length: 10,
width: 5,
area: function () {
// 长 x 宽
return this.width * this.length
}
}
console.log(r1);
console.log(r1.area());
// 创建r2对象, 长 20, 宽 30 area方法 算面积
var r2 = {
length: 20,
width: 30,
area: function () {
return this.width * this.length
}
}
console.log(r2.area());
// 创建r3对象, 长 120, 宽 35 area方法 算面积
var r3 = {
length: 120,
width: 35,
area: function () {
return this.width * this.length
}
}
console.log(r3.area());
// 思考1: r1 r2 r3 中都有area函数, 函数体一模一样
// 他们是同一个函数吗??
console.log(r1.area == r2.area) //不是同一个
// area在不同的对象中声明的, 所以并非同一个函数
// 思考2: 有必要吗? 同样功能的函数 声明3个 在不同对象里
// 没必要, 浪费内存, 一个足够了
// 怎么办? 提取area函数放在外面 共享即可
function area() { return this.width * this.length }
var r4 = {
width: 100,
length: 200,
// area: area 语法糖: 属性名和变量名相同, 可以合写
area
}
console.log(r4.area());
// r5: 宽300, 长100 area方法
var r5 = { width: 300, length: 100, area }
console.log(r5.area()); // 函数() ()是调用函数, 让函数运行
// 思考1: r4的area 和 r5的area 是否相同? 是
// 因为都引用的同一个函数
console.log(r4.area == r5.area) // true
// 思考2: 为什么函数书写在外部, 引入到不同的对象里, 就能为所在的对象服务??
// 因为 灵活的 this 关键词: 函数在哪个对象里执行, 就代表哪个对象
// this是特别好用的特性: 实现节省内存, 在不同对象里复用函数
</script>
</body>
</html>
构造函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>构造函数</title>
</head>
<body>
<script>
function area() { return this.width * this.length }
// 手动方式: 创建一个对象 -- 麻烦,容易写错
var r1 = { width: 10, length: 20, area }
console.log(r1.area());
// 利用函数快速创建对象
// 相当于工厂/流水行: 提供原材料 即 30, 50
// 内部就会加工成 矩形对象并 返回
function Rectangle(a, b) {
// 函数的形参名随便起
var obj = {}
obj.width = a
obj.length = b
obj.area = area
return obj
}
var r2 = Rectangle(30, 50)
console.log(r2);
console.log(r2.area());
// 自动化思想: 利用函数 把步骤都固定写好
// 每次使用时, 提供参数即可, 函数自然会完成剩余的所有操作
// 这种用来生产 对象的函数 -- 称为 构造(对象的)函数
var r3 = Rectangle(10, 40)
var r4 = Rectangle(440, 44)
</script>
</body>
</html>
构造函数的两件事
-
this关键词: 把对象中共同的方法, 提取到外部存储
-
构造函数: 自动化思想, 把制作对象的步骤 封装成函数, 调用函数即可以自动完成对象的创建
构造函数练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习</title>
</head>
<body>
<script>
// 员工对象
// salary: 薪资
var e1 = {
ename: '楠姐',
age: 18,
salary: 22000,
salary_year: function () {
return this.salary * 12
}
}
console.log(e1);
console.log(e1.salary_year());
// 制作一个生产 员工对象的 构造函数, 用来自动生成对象
// 练习: 模仿上节课的, 实现这个 构造函数
function salary_year() {
return this.salary * 12
}
// 变量名: 见名知意
function Employee(ename, age, salary) {
// 1.制作一个空对象
var obj = {}
// 2.把传入的参数存储在对象里
obj.ename = ename
obj.age = age
obj.salary = salary
obj.salary_year = salary_year
// 3.返回制作完毕的对象
return obj
}
// e2 凯凯 32 15000
var e2 = Employee('凯凯', 32, 15000)
console.log(e2.salary_year());
// e3 涛哥 40 30000
var e3 = Employee('涛哥', 40, 30000)
console.log(e3.salary_year());
</script>
<script>
// 立方体:
var c1 = {
length: 100, //长
width: 40, //宽
height: 10, //高
//体积: 长x宽x高
volume: function () {
return this.width * this.length * this.height
},
// 周长: (长+宽+高)*4
perimeter: function () {
return (this.width + this.height + this.length) * 4
}
}
console.log(c1);
console.log(c1.volume());
console.log(c1.perimeter());
// 练习: 制作 Cube 函数, 用来生产 立方体 对象
function volume() {
return this.width * this.length * this.height
}
function perimeter() {
return (this.width + this.height + this.length) * 4
}
function Cube(length, width, height) {
// 1. 空对象
var obj = {}
// 2. 加入数据
obj.length = length
obj.width = width
obj.height = height
// 3. 添加函数 -- 省内存, 把函数在外部声明
obj.volume = volume
obj.perimeter = perimeter
// 4. 返回
return obj
}
// 使用时:
var c2 = Cube(10, 20, 30) // 顺序是 长 宽 高
console.log(c2.volume());
console.log(c2.perimeter());
</script>
</body>
</html>
今日内容总结
-
函数的arguments
-
函数中自带的一个 隐藏的变量
-
保存了 函数接收到的所有 实参
-
类型是对象, 长得像数组 : 类数组 / 伪数组
-
不能使用数组的各种方法
-
-
-
函数的重载: 通过判断函数的参数 数量/类型 不同, 执行不同的逻辑操作
-
作用: 让1个函数更加强大, 能完成不同的任务
-
做法: 利用 if 判断, 搭配 arguments 属性
-
-
访问器语法: 点语法 方括号语法 --- 到亮亮的部分复习
-
对象是引用类型
-
堆栈概念
-
栈: 存储少量数据, 查询速度快 -- 类似图书的目录
-
堆: 存储大量数据, 类似图书的详情页
-
对象类型存储在栈中的是 其内存地址, 所以称为:地址传递
-
-
克隆对象:
-
制作一个空的新对象, 遍历目标对象, 挨个把属性赋值到空对象即可
-
-
this关键词:
函数是哪个对象调用的, 其中的this就是哪个对象
-
楠姐.打亮亮()
: 打亮亮中的this 就是楠姐
-
-
为什么有this
-
把多个对象中的 共同函数, 提取到外部存储.
-
利用this的灵活变化, 在哪个对象执行 就服务于哪个对象
-
-
对象的构造函数
-
构造函数: 类似一个工厂, 提供原材料, 就会按照固定的步骤制作出商品
-
给函数传参, 生成一个固定结构的对象
-
作业
函数重载练习
制作一个max函数, 能够找出最大值
max(11,22,33,44)
max(43,54,65,656,12,5)
max([32,4,546,57])
max([32,4,546,57, 5, 67,7])
提示:
-
参数有两种传递方式: 传入1堆数字 或者 传入1个数组类型
-
找出最大值的思路: 先声明一个变量a 等于 数组的第一个元素, 然后遍历数组从序号1开始. 如果新的值比a大, 就替换掉a中的值即可
构造函数练习
制作一个 生产 学生对象的 构造函数
学生对象的样子:
var s1 = { sname: '凯凯', age: 19, phone:'18844341111', // 返回是否成年 isAdult: function(){ return this.age >= 18 } }制造的构造函数使用时的样子
var s2 = Student('亚楠', 18, '198382932323') console.log(s2.isAdult()) var s3 = Student('梦瑶', 15, '158779787874') console.log(s3.isAdult())
JSCORE03
复习
-
声明提升
-
JS独有的特色 -- 不好
-
JS代码书写完毕后, 真正执行时会
隐式
调整代码结构, 把声明操作都提升到作用域的顶部存放, 然后再执行面试考点: JS代码的运行 和 你眼睛看到的不一样
-
声明关键词:
var/function/class/let/const
-
-
作用域: 本质是个
对象
类型, 只是因为有特殊作用 所以叫作用域-
全局: window对象
-
局部: 函数
运行时
会临时生成一个对象, 其中保存函数中声明的变量 -
脚本: 在 script 中使用
let/const/class
声明的 -
块级:
{}
中使用let/const/class
声明的
-
-
作用域链: 代码的书写环境 --
词法环境
-
当函数中使用一个变量, 优先在自身作用域查找. 如果没有 则向上级查找
-
-
闭包
-
函数在声明时, 会携带所在的所有作用域, 存储在
scopes
属性里-
静态: 函数声明 -- 作用域们存储在 scopes 里
-
动态: 函数调用
()
-- 从scopes里读取作用域中的值 来用
-
-
其中: scopes中存储的 函数作用域 -- 称为闭包 Closure
-
作用: 在ES6之前, 声明局部的变量, 避免全局污染
-
-
arguments
-
函数中
隐式
带有的一个属性, 存储了 函数使用时 收到的所有实参
-
-
函数的重载
-
一种技巧, 函数体中 通过判断 参数的个数 或 类型, 来执行不同的逻辑
-
作用: 让一个函数能做多件事, 更加强大
-
做法:
if
配合arguments
使用
-
-
引用类型
-
数据类型有8种
-
基础类型(
体积小
): string number boolean null undefined symbol bigInt -
引用类型(体积不固定,大): object
-
-
内存部分: 堆内存 栈内存
-
栈内存: 相当图书的目录, 适合存储小的数据, 查询速度快
-
堆内存: 相当于图书的详情页, 适合存储大型数据, 带有内存地址
-
-
对象类型作为大型的数据, 存储在堆内存里; 变量保存对象类型, 实际存储的是对象类型的地址 -- 地址传递/引用(内存地址)类型
-
-
this
-
函数中的一个关键词, 代表函数运行时在哪个对象里
-
楠姐.打亮亮()
:打亮亮
方法中的this就是楠姐
-
-
作用: 让同一个函数 可以在不同的对象中使用, 因为this关键词所以会自动适应所在的对象 节省内存
-
-
构造函数:
构造 固定结构的对象 的函数
-
把一些对象的制造过程, 封装成一个函数. 以后调用函数就能自动生成对象
-
作业
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作业</title>
</head>
<body>
<script>
// max函数, 求最大值
function max() {
// arguments: 存储了所有的参数
// console.log(arguments);
// 思维: 先看第一个, 暂定为最大
// 然后再看下一个, 如果下一个更大, 则 替换掉之前 暂定的最大
// 写程序 就是把 你的思维 转换成 代码 告诉计算机如何操作
if (Array.isArray(arguments[0])) {
//读取参数数组
var arr = arguments[0]
var m = arr[0]
for (var i = 1; i < arr.length; i++) {
if (arr[i] > m) m = arr[i]
}
console.log(m);
} else {
var m = arguments[0]
for (var i = 1; i < arguments.length; i++) {
// if判断的 {}中只有一行, 可以省略{}
if (arguments[i] > m) m = arguments[i]
}
console.log('最大数:', m);
}
}
max(12, 43, 4565, 675)
max(12, 43, 4565, 675, 1111, 23, 43)
max([43, 5, 65, 7, 78])
</script>
<script>
// 构造学生对象
// 把对象中使用的函数 在外部声明: 共享 省内存
// 思考: 共享的函数 如果存储在 全局区, 会造成全局变量污染
// 办法: 作者为函数提供了一个专有的属性 prototype(原型), 专门存储共享的方法
function isAdult() {
return this.age >= 18
}
function Student(sname, age, phone) {
var obj = {}
obj.sname = sname
obj.age = age
obj.phone = phone
// 错误: function关键词会声明新的函数. 每次运行都会声明一个新的. 浪费内存
// obj.isAdult = function () { }
obj.isAdult = isAdult
return obj
}
var s1 = Student('凯凯', 19, '18844341111')
console.log(s1);
console.log(s1.isAdult());
</script>
</body>
</html>
JSCORE04
复习
-
声明提升
-
JS代码在
真正运行
时, 需要隐式
调整代码的顺序, 把声明操作的代码提升到作用域的顶部执行 -
书写的代码 和 运行的代码, 实际顺序是不同的 --
面试考点
-
声明的关键词:
var/function/let/const/class
-
-
作用域
-
什么是作用域?
本质就是一个对象类型, 只是因为有特殊作用, 所以称为 作用域
-
4种作用域:
-
全局作用域: 在
浏览器
这个宿主环境
中, 就是 window 对象 -
局部作用域: 函数在
运行时
临时创建的对象类型, 存储了函数中声明的变量-
在函数执行开始:
创建
; 在函数执行结束:销毁
-
-
脚本作用域: ES6开始
-
利用
let/const
在 script 中声明的, 存储在脚本区
-
-
块级作用域: ES6 开始
-
利用
{}
配合let/const
使用
-
-
-
-
闭包: 函数自带的一个技能 -- 函数在声明时, 会保存其所在的
词法作用域
到scopes
属性里-
词法作用域
: 函数所在的外层的所有作用域 -
闭包就是
函数作用域
在保存到scopes
属性中时的一个特殊称呼a 函数在触发时, 会形成函数作用域, 其中有一个变量 x
b 函数在a函数中声明, 使用了a函数的 变量x
a函数的作用域就会存储在 b 函数的 scopes 属性里, 称为闭包
function a(){ var x = 10 function b(){ console.log(x) } } a()
-
-
函数的重载
-
arguments: 函数中自带的一个变量, 存储了函数收到的所有参数
-
使用场景: 当一个函数的参数数量
不固定
时, 使用
-
-
应用: 函数重载
-
在函数体中, 判断参数的
个数
或类型
不同, 执行不同的代码逻辑 -
让1个函数 能做多种操作
-
-
-
对象
-
数据类型8种
-
基础: string number boolean null undefined + symbol 和 bigInt
-
对象类型 object
-
-
引用类型:
-
堆内存: 相当于图书的详情页, 存储的东西多, 带有
内存地址
-
栈内存: 相当于图书的目录, 查询速度快,存储的内容少
-
-
对象类型, 存储在堆内存中
-
变量存储在 栈内存里, 其值是 对象类型的
地址
-
引用了
对象类型的地址
, 对象之间的赋值---地址传递
-
-
this关键词
-
函数的this关键词, 代表函数
运行时
所在的对象
-
作用:
省内存
-
如何实现: 一个函数可以在多个对象中重复使用:
复用
-
-
构造函数
-
用来创建对象类型的函数, 就是构造函数
-
普通写法:
function Demo(x, y){ var obj = {} //1. 先创建空对象 // 把参数存储在对象里 obj.x = x obj.y = y // 原型链 __proto__ 指向 函数的原型 obj.__proto__ = Demo.prototype return obj //2. 返回对象 } // 对象共享的方法, 存储在原型中 Demo.prototype.area = function(){} Demo.prototype.xxyy = function(){}
-
new
运算符: 作者提供的 用于简化构造函数
的关键词, 隐式完成3
行代码function Demo(x, y){ var this = {} this.x = x this.y = y this.__proto__ = Demo.prototype return this } // 配合new使用 function Demo(x, y){ this.x = x this.y = y } var d1 = new Demo(10, 20)
-
-
严格模式
-
ES5 2009年出品的版本
-
在严格模式下, 如果书写的代码有
风险
后台会报错 -
开启方式: 在JS代码的最上方书写
use strict
-
变量必须先声明, 后使用: 防止写错变量名导致全局变量污染
-
构造函数的this, 直接触发 this是undefined, 避免全局污染
-
阻止静默失败: 以前失败但不报错的, 现在报错
-
-
对象属性的精确配置
-
Object.defineProperty(对象, 属性名, 配置项)
-
配置项:
-
configurable: 是否可重新配置 -- 是否可删除属性
-
enumerable: 可遍历 -- for..in 是否能遍历到
-
writable: 可修改 -- 值是否可改动
-
value: 默认值
-
get: 计算属性 -- 值是函数类型, 但是使用时不需要() 就能触发 -- 简单
-
-
set
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>精确属性配置 - 赋值监听</title>
</head>
<body>
<script>
var emp = {
ename: '亚楠',
_age: null, // 用于存储 检查站 age 检查出来的值
}
// 为 age 属性, 添加检查站: 监听器 set(设置)
Object.defineProperty(emp, 'age', {
// 计算属性get: 特点 值是函数,但使用时不用()
// get: function () { },
get() {
// emp._age
return this._age;
},
// set: 检查站属性, 值是函数
// set: function (value) { }
// 语法糖: 省略 function
// 参数value: 就是设置给 age 属性的值,
// 例如 emp.age = 200, 就是这个200
set(value) {
console.log('age赋值为:', value);
// 检查标准: age 1~100
if (value >= 1 && value <= 100) {
// 如果正确, 则赋值给 _age
// age是监听器/检查站, 只负责检查, 不负责存值
// _age 是配合 age检查站使用的属性, 用来存储值
this._age = value
} else {
// 抛出错误, 提醒用户
throw Error('age合法范围1~100, 您的赋值:' + value)
}
}
})
// 修改age 年龄
// 问题: 娅楠的年龄不可能是200, 明显的错误
// 期望: 监听这个赋值操作, 如果值明显不对, 则报错, 给出提示
// emp.age = 200
// 用户看到: 把100 存储到 age 属性里
// 本质age是检查站不存值, 但是用户不知道, 用户只看表面
emp.age = 100
// 外观上: 就应该从 age 读取值
console.log(emp.age);
// emp.age: 实际上age是个函数,但是因为是get计算属性, 不要()
// 从外观上看起来: 好像是在读属性
// 楠姐.打亮亮() : this就是楠姐
// emp.age() : this就是 emp
console.log(emp);
// 读取年龄: 从_age读取完全没问题
// 但是: 从用户角度来看
console.log(emp._age);
</script>
</body>
</html>
死循环
最终超出最大内存限制, 崩溃
由于 age
属性变身成检查站/监听器
, 其不再
具有存储值
的作用
解决方案: 再制作一个属性用来存储 合法的值
即可
-
命名规范: 习惯(非强制)上 存储 age 检查成功的值, 属性名叫
_age
比较合适, 添加一个_
, 读代码时一目了然
, 就知道_age
存储的是age
检查站的内容
练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习</title>
</head>
<body>
<script>
var emp = {
ename: "凯哥",
// 配合 salary 检查站, 创造一个仓库 用来存储对应的值
// 写法上: 最好是 _开头, 名字一目了然
_salary: 20000
}
// 把 salary 属性修改成 检查站/监听器, 不再负责存储数据
Object.defineProperty(emp, 'salary', {
// 让 salary 使用时, 不用() 也能触发
get() { return this._salary },
set(value) {
if (value >= 5000 && value <= 30000) {
// salary属性本身已经变身为 检查站 - 无法存数据
// 所以搭配一个 仓库用来存数据
this._salary = value
} else {
throw Error('月收入不合理:' + value)
}
}
})
// 希望报错, 合理的薪资是 5000 ~ 30000
// emp.salary = 100000
// 用户角度: 眼看着存储在 salary 属性的
// 但是却要从 _salary中读取 : 另用户感觉到迷惑
emp.salary = 12000
// 读取时: 增加一个 salary的 get计算属性, 能够返回 _salary的值
console.log(emp.salary);
console.log(emp);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习</title>
</head>
<body>
<script>
var stu = {
sname: "亮亮",
_sid: 10001 //学号
}
// 把 sid 属性, 转变成 检查站, 不再能够存储值
Object.defineProperty(stu, 'sid', {
get() { return this._sid },
set(value) {
if (value >= 1 && value <= 100000) {
this._sid = value
} else {
throw Error('学号范围错误:' + value)
}
}
})
// 学号范围是 1-100000
// stu.sid = 200000 //要求弹出报错: 提示学号范围不对
// 注释掉 19行代码
stu.sid = 2000 //能够正常存储
console.log(stu.sid) // 能够打印出2000 -- 计算属性
console.log(stu);
</script>
</body>
</html>
call
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>call</title>
</head>
<body>
<script>
// 函数的call方法: 用于指定函数使用时的this关键词指向
// 函数的this: 指向运行时所在的对象
// 例如 楠姐.打亮亮() -- this就是楠姐
// 函数分两种状态:声明时 和 运行时
// 声明: 问: this是什么? 不可能知道--运行时决定
// 举例: 马路边停了一辆车, 问: 司机是谁? -- 开起来才知道
function 打亮亮() {
console.log(this.uname, '使劲打亮亮');
}
var emp = { uname: "娅楠" }
// 把 打亮亮 这个函数, 存储到 emp 里
emp.hit = 打亮亮
emp.hit()
var emp1 = { uname: '凯哥' }
emp1.hit = 打亮亮
emp1.hit()
delete emp1.hit //删除 hit 属性, 毁灭证据
// 又把 公用的锤子
// 1. 把锤子拿到手里 2.用锤子打亮亮 3.把锤子扔掉
console.log(emp1);
// 打亮亮方法在使用时分两步:
// 1. 先把此方法存储到 对象里
// 2. 再通过对象来调用
// 3. 删除打亮亮方法, 因为是临时使用
// 官方为了用户使用方便, 封装了一个call方法, 能够自动把函数放在对象里, 进行调用, 随后进行删除
// 函数的构造方式: new Function()
// 所以 函数.__proto__ == Function.prototype
console.dir(打亮亮);
// 函数具有一个call
// 函数.call(obj): 隐式 把函数存储在obj里, 然后通过obj调用
打亮亮.call(emp1)
var emp2 = { uname: '涛涛' }
// 两种做法:
// 做法1: 手动模式
// - 把 打亮亮 存储在 emp2 中, 属性名叫 hit, 可以随便起名
emp2.x = 打亮亮
// - 调用 hit 方法
emp2.x() // 打亮亮的 this 就是 emp2
// - 删除 hit 属性, 毁灭证据
delete emp2.x
console.log(emp2);
// 做法2: 系统提供的快速隐式完成以上操作 -- call
打亮亮.call(emp2)
</script>
</body>
</html>
call带有参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>call携带参数</title>
</head>
<body>
<script>
function 打亮亮(time) {
// time: 次数
console.log(this.uname, '打亮亮', time, '次');
}
var emp1 = { uname: "楠姐" }
// 1. 把 打亮亮 放在 emp1 里
emp1.xx = 打亮亮
// 2. 调用 emp1 的打亮亮
emp1.xx(5)
// 3. 删除掉 emp1 中存储的 打亮亮
delete emp1.xx
// 使用系统提供的 call 方式
// 函数.call(对象, 参数): 自动隐式完成 - 把函数放对象里,调用后,删除
// call() 从参数2开始, 就是传递给函数本身的参数
打亮亮.call(emp1, 10) // emp1.打亮亮(10)
// 范式: fn.call(A, B) ->实际执行 -> A.fn(B)
// 计算 n个月 支付的薪资
function pay(n) {
console.log(this.salary * n);
}
var emp2 = { ename: "凯凯", salary: 14000 }
var emp3 = { ename: "涛涛", salary: 24000 }
// 计算 emp2 10个月的薪资
pay.call(emp2, 10)
// 计算 emp3 4个月的薪资
pay.call(emp3, 4)
function show() {
console.log(arguments);
// 向 arguments 中, 新增 55 66 77 3个值
// arguments: 类数组, 长得像数组 但 原型不是, 没有数组的方法
// 数组: push
// 把数组的 push 方法临时放到 arguments 中使用一次
// arguments.push(55, 66, 77)
Array.prototype.push.call(arguments, 55, 66, 77)
// 数组的push方法: 构造函数Array的原型prototype里
}
show(11, 22, 33, 44)
</script>
</body>
</html>
apply
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>apply</title>
</head>
<body>
<script>
// apply: 与 call 作用十分相似
// 都是临时把函数放在对象里执行
function pay(n, off) {
// off: 折扣
console.log(this.salary * n * off);
}
var emp = { ename: "凯凯", salary: 14000 }
// 用call 算出10个月工资: 把pay临时放到 emp 中执行
pay.call(emp, 10, 0.7)
// apply: 临时把函数放在对象里执行, 差异: 参数放数组中传递
pay.apply(emp, [10, 0.7])
// apply的用途: 把 1个1个 传递的参数, 改为用数组传递
// 求最大值: Math.max()
var m = Math.max(12, 43, 2154, 657, 67, 21)
console.log(m);
// 找出数组中的最大值
var nums = [123, 345, 546, 657, 67]
// max: 只能接收 1个1个传递的参数
// apply: 把数组作为函数的参数, 数组->1个1个传递的
// Math.max.apply(A, B) -> A.Math.max(B)
// Math.max 不需要存储在任何对象中, 独立就能运行, 所以参数1 随便写, 没有任何影响
var m = Math.max.apply(0, nums)
// ES6之前, 只能用 apply 来转换数组 为参数
// ES6之后, 有扩展符 ... 更方便, 后续讲解
console.log(m);
</script>
</body>
</html>
bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bind</title>
</head>
<body>
<script>
// bind: 与call相似
// call: 临时 把函数放在对象中, 立即执行
// bind: 永久 把函数放在对象中, 不会立即执行, 返回处理后的函数
function pay(n) {
console.log(this.salary * n);
}
var emp = { ename: "凯凯", salary: 14000 }
// 用call实现 10个月工资
pay.call(emp, 10)
// bind(): 不会触发pay函数, 而是返回一个值
// 返回值是一个函数, 其中的 BoundThis 属性存储了this指向
// 其中的 BoundArgs : 存储了相关参数
// 打比方: 一把手枪
// call: 填充弹药 -> 立刻激发
// bind: 填充弹药 -> 返回这把带有弹药的手枪
var pay_b = pay.bind(emp, 10)
console.dir(pay_b);
// 在后续, 用()来触发
pay_b()
// 实际工作用途:
// 1. 配合定时器使用: 延时执行
// 2. 在 React 框架中常用: 2个月后的 5阶段会用到
</script>
</body>
</html>
函数的call,bind,apply
面试
常考题, 造火箭
时使用, 日常
用的较少
-
call: 临时修改函数的this指向,
立刻触发
, 参数1个1个
传递 -
apply: 临时修改函数的this指向,
立刻触发
, 参数数组
传递 -
bind: 把参数和函数捆绑在一起, 返回这个函数.
不会触发
, 等到后续随时触发都可以
箭头函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>箭头函数</title>
</head>
<body>
<script>
// ES6 2015年提供的 新语法 - 箭头函数
// 优点1: 丰富的语法糖 带来简化写法
// 作用: 带来了 新的 this 指向方式
// 注意: 不能做 构造函数
// function (){}
// 从格式上: 简化了 匿名函数自调用的写法
(function () { })();
(() => { })()
// 语法糖1: 箭头函数只有一个参数, () 可以省略
var a = (name) => {
console.log(name, '666');
}
// 简化
var a = name => {
console.log(name, '666');
}
a('凯凯')
a('楠楠')
// 语法糖2: 函数体只有1行代码时, 可以省略 { return ... }
var a = (n) => { return 14000 * n }
var a = n => 14000 * n
console.log(a(12));
// 练习: 尝试省略
var b = (x, y) => { return x + y }
var b = (x, y) => x + y
var c = (y) => { return 12 * y }
var c = y => 12 * y
var d = () => { return 666 * 888 }
var d = () => 666 * 888
// 坑:
var e = (name, age) => { return { name: name, age: age } }
// 返回值是对象类型, 带有{}, 会被识别为 函数体的{}
// 用()包围, 才会识别为 普通的对象类型
var e = (name, age) => ({ name, age })
console.log(e('马鑫鑫', 23));
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>箭头函数的this</title>
</head>
<body>
<script>
// function: this指向运行时所在的对象
// 箭头函数: this指向声明时所在的作用域的this
// 类似: 涛哥买个锤子, 打亮亮
// function: 楠姐.打亮亮() 锤子的使用者是楠姐 this
// 箭头函数: 楠姐.打亮亮() 锤子的购买者 - 涛哥: 永远不变
// 另一个例子:
// 家乐的妈妈: - 相当于 箭头函数, 家乐不管在哪里都不会变, 出生的时候就锁死
// 家乐的女朋友: 今天是 热巴 明天是杨幂 后天 -马尔扎哈 - function. 会变化
console.log(this) // 全局中打印this, 就是window
// 这个箭头函数 在哪个对象中声明的: 全局中 window
var 三打亮亮 = () => {
console.log(this, '三打亮亮');
}
function 打亮亮() {
console.log(this, '在打亮亮');
}
var emp = { uname: "楠姐" }
emp.x = 打亮亮
emp.x()
// 把箭头函数放在 emp 里
emp.y = 三打亮亮
emp.y()
</script>
</body>
</html>
every
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>every</title>
</head>
<body>
<script>
// 数组的高阶函数
// 高阶函数: 如果一个函数中, 使用了另一个函数, 就叫高阶函数
// 类似: 家乐 有孩子了, 家乐称为: 父亲
// 例如: 数组有一个 sort 排序, 例如 sort( (a,b)=> a-b )
// 以后: 从服务器上查询出数据, 往往都是数组类型, 在JS中提供了大量的处理 数组类型的 函数
// every: 每一个. 用于遍历数组, 检查每一个元素是否符合条件
var nums = [12, 22, 14, 12, 4]
// 检查: 数组的每个元素是否都是 偶数
// every的参数: 要求是函数类型, 函数固定接收3个参数
// every会遍历 nums 中的每一个元素, 传递给函数
var a = nums.every((value, index, array) => {
// 箭头函数的形参名: 随便起
// 顺序固定: 值, 序号, 数组本身
console.log('value:', value);
console.log('index:', index);
console.log('array:', array);
console.log('------------------');
return value % 2 == 0
})
console.log('a:', a);
console.log(a ? '都是偶数' : '非都是偶数');
// 练习
var nums = [12, 3, 5, 65, -3]
// 判断是否都是正数 > 0
var a = nums.every((value, index, array) => {
return value > 0
})
// 没用到的参数, 可以不用声明
var a = nums.every((value) => {
return value > 0
})
// 箭头函数: 单参数省略() 函数体只有1行 {return }
var a = nums.every(value => value > 0)
// 参数名随便起, 但是原则: 见名知意
var a = nums.every(x => x > 0)
console.log(a);
console.log(a ? '都是正数' : '非都是正数');
var nums = [12, 3, 45, 5465, 765]
// 判断是否都大于10
var a = nums.every(v => v > 10)
console.log(a ? '都大于10' : "非都大于10");
// 实际案例:
var products = [
{ pname: 'iPhone', price: 9000, count: 10 },
{ pname: 'v80x', price: 6000, count: 1 },
{ pname: 'findx5', price: 5500, count: 5 },
{ pname: 'magic 5', price: 6000, count: 6 },
]
// 需求: 判断商品的单价是否都高于 5000
// 数组的每个元素是对象类型
var a = products.every(value => value.price > 5000)
console.log(a ? '都大于5000' : '非都大于5000');
// 判断: 数量是否都少于3个的
var a = products.every(value => value.count < 3)
console.log(a ? '都少于3个' : '非都少于3个');
</script>
</body>
</html>
some
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>some</title>
</head>
<body>
<script>
// some: 有些, 判断数组中至少有一个满足条件的元素
// 类似于 逻辑或 || -- 有一个真 最终结果是真
var nums = [11, 43, 5, 677, 1]
// 判断: 是否有偶数
// some的所有参数 都和 every 一样
var a = nums.some((value, index, array) => {
return value % 2 == 0
})
// 简化后:
var a = nums.some(value => value % 2 == 0)
console.log(a ? '有偶数' : '没有偶数');
var emps = [
{ ename: "凯凯", age: 32 },
{ ename: "亮亮", age: 37 },
{ ename: "亚楠", age: 18 },
]
// 查看是否 有 小于20岁的人
var a = emps.some(value => value.age < 20)
console.log(a ? '有' : '没有');
</script>
</body>
</html>
filter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>filter</title>
</head>
<body>
<script>
// filter: 过滤器, 把数组中满足条件的元素 组成新的数组返回
var nums = [12, 3, 43, 65, 12, 546, 7, 6]
// 找出所有的偶数
var a = nums.filter((value, index, array) => {
// 真: 过滤出来 假: 阻拦
return value % 2 == 0
})
// 简化:
var a = nums.filter(value => value % 2 == 0)
console.log(a);
var emps = [
{ ename: "凯凯", salary: 14000 },
{ ename: "亮亮", salary: 34000 },
{ ename: "楠姐", salary: 20000 },
{ ename: "涛涛", salary: 24000 },
]
// 找出所有 薪资大于20000的人
var a = emps.filter(value => value.salary > 20000)
console.log(a);
</script>
</body>
</html>
map
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>map</title>
</head>
<body>
<script>
//map: 映射 - 把数组中的每个元素 处理后, 返回值组成新的数组
var nums = [12, 34, 5, 65, 23, 43, 6]
// 希望: 每个数字都 x2
var a = nums.map((value, index, array) => {
return value * 2
})
// 简化:
var a = nums.map(value => value * 2)
console.log(a);
// 实际用途: 把数据转换成 HTML 字符串, 最常用
var girls = ['迪丽热巴', '古力娜扎', '马尔扎哈', '努尔哈赤']
// 把每个元素, 放在按钮标签里 <button>xx</button>
var a = girls.map(value => {
// ES6提供了 模板字符串, 转为拼接HTML而生
// 符号 `` 反引号, 特殊标识 ${} 代表JS代码
return `<button>${value}</button>`
})
console.log(a);
var webs = [
{ name: "百度一下", href: "http://www.baidu.com" },
{ name: "Tmooc", href: "http://www.tmooc.cn" },
{ name: "斗鱼", href: "http://www.douyu.com" },
{ name: "哔哩哔哩", href: "http://www.bilibili.com" },
]
// 要求: 映射成 超链接 字符串组成的数组
// 例如 <a href="http://www.baidu.com">百度一下</a>
var a = webs.map(value => `<a href="${value.href}">${value.name}</a>`)
console.log(a);
</script>
</body>
</html>
内容总结
-
属性的 set配置
-
把一个属性转换成
监听器/检测站
: 实现赋值检测 -
搭配 另一个属性, 来存储实际的值
-
通过 get 计算属性, 欺骗用户, 提供一个友好的读值写法
-
-
函数的3个触发方式
-
call: 触发函数同时指定this的对象
-
bind: 不触发函数, 把参数和this绑定好, 后期随时触发
-
apply: 触发函数同时指定this的对象, 其参数是数组
-
-
箭头函数
-
语法糖:
(name)=>{ return name + 11 }
省略()
和{}
name => name + 11
-
this指向: 声明时所在作用域的this
-
-
数组高阶函数
-
every: 每一个元素都满足条件, 类似逻辑与 && 必须都真才行
-
some: 至少有一个元素满足条件, 类似 逻辑或 || 有一个真就行
-
filter: 过滤器, 把满足条件的元素过滤出来, 组成新的数组
-
map: 映射, 把每个元素处理后的返回值, 组成新的数组 (
很常用
)
-
作业
作业1:
var p = { pname:"iPhone", count: 5, price: 9999}
p.count = -10 //报错: 范围在 0 - 100000
p.count = 40 //正常赋值
console.log(p.count) //正常输出 40
p.price = -10 //报错: 范围在 2000 - 20000
p.price = 4000 //正常赋值
console.log(p.price) //正常输出 4000
作业2:
var emps = [
{ename:"凯凯1", age: 23, salary:7000, married:true},
{ename:"凯凯2", age: 33, salary:17000, married:true},
{ename:"凯凯3", age: 44, salary:9000, married:false},
{ename:"凯凯4", age: 28, salary:12000, married:true},
{ename:"凯凯5", age: 35, salary:33000, married:false},
]
// 判断是否所有人都大于30岁
// 判断是否有人工资超过3w
// 找出所有已婚的人
// 把数组映射成html组成的数组, 其中元素样子如下: 注意 已婚 class是ok, 未婚class是err
// <a class='ok'>凯凯1-23-7000</a>
JSCORE05
复习
-
对象属性的精确配置
-
writable: 是否可写
-
enumerable: 是否可遍历
-
在 谷歌浏览器中, 不可遍历的属性是
浅色
的
-
-
configurable: 是否可重新配置 -- 属性能否删除
-
value: 默认值
-
get: 计算属性, 值是
函数
, 但是使用时不用()
就能触发 -
set: 监听器, 把一个属性转换成监听器, 监听赋值
-
-
函数的3个触发方式
-
call: 立刻触发函数; 临时把函数放在对象中执行 -- 修改this指向;
-
参数部分, 1个1个传递
-
-
apply: 立刻触发函数; 临时把函数放在对象中执行 -- 修改this指向;
-
特殊: 参数部分用 数组进行传递
-
-
bind: 不会触发函数; 把 参数和this指向 都保存在函数自身, 返回绑定好的函数; 以后随时都能触发 --
延时执行
-
-
箭头函数
-
从
格式
上提供了更简单的匿名函数写法:() =>{}
-
两种语法糖:
-
参数只有一个, () 可是省略:
x => { return x * 2 }
-
函数体只有一行,
{return }
省略:x => x * 2
-
-
this关键词的指向
-
function的this:
运行时
所在的对象楠姐.打亮亮()
-
箭头的this:
声明时
所在的对象 -- 固定的,永远不变 -
构造函数的this(
new
): 构造出来的那个对象-
new隐式3件事:
-
var this = {};
-
this.__proto__ =函数.prototype
-
return this
-
-
-
严格模式下, 函数的this
-
如果直接在全局调用, 则this指向 undefined
-
-
-
-
数组的高阶函数
-
高阶函数
: 函数中使用了其它的函数 -
every: 每一个元素都满足条件则为真, 类似于 逻辑与 &&
-
some: 至少有一个元素满足条件则为真, 类似于 逻辑或 ||
-
filter: 把满足条件的元素
过滤
出来, 组合成新的数组 -
map: 映射. 遍历每个元素, 处理后把返回值组成新的数组;
-
使用场景: 把数据转换成HTML字符串
-
-
作业
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作业</title>
</head>
<body>
<script>
var p = {
pname: 'iPhone',
_count: 5,
_price: 9999,
c() { return this._count },
// get: 变身成计算属性, 使用时不需要()
get d() { return this._count },
get count() { return this._count }
}
// 使用时:
console.log(p.c())
console.log(p.d)
console.log(p.count)
// count: 对赋值进行检查 -- 改造成 监听器
Object.defineProperty(p, 'count', {
get() { return this._count },
// set: 设置. 用于把属性改为赋值监听器
set(value) {
if (value >= 0 && value <= 100000) {
// 把正确的值保存下来
// count属性已经被转换成监听器, 不再具有保存的能力
// 需要额外制作一个属性, 来存储数据
// 属性名: 可以随便起, 但是见名知意是最佳的选择
this._count = value
} else {
throw Error('数量赋值范围错误:' + value)
}
}
})
// p.count = -10 //报错
p.count = 40
// 读取 p._count 可以读取到值, 但是对于用户的观感不好
// 用户的感觉: 存储在 count 属性里
// 用户应该觉得: 从 p.count 读取更合理
console.log(p.count);
// defineProperties: 可以同时完成 price和count的制作
Object.defineProperty(p, 'price', {
get() { return this._price },
set(value) {
if (value >= 2000 && value <= 20000) {
this._price = value
} else {
throw Error('价格赋值错误:' + value)
}
}
})
// p.price = -100
p.price = 3000
console.log(p.price);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作业</title>
</head>
<body>
<script>
var emps = [
{ ename: '凯凯1', age: 23, salary: 7000, married: true },
{ ename: '凯凯2', age: 33, salary: 17000, married: true },
{ ename: '凯凯3', age: 44, salary: 9000, married: false },
{ ename: '凯凯4', age: 28, salary: 12000, married: true },
{ ename: '凯凯5', age: 35, salary: 33000, married: false },
]
// 判断是否所有人都大于30岁
var a = emps.every(value => value.age > 30)
console.log(a ? '都大于30' : '非都大于30');
// 判断是否有人工资超过3w
var a = emps.some(value => value.salary > 30000)
console.log(a ? '有超过3w' : '没有超过3w');
// 找出所有已婚的人
// value.married == true: 真==真 , 返回真; 麻烦没必要
var a = emps.filter(value => value.married)
console.log(a);
// 把数组映射成html组成的数组, 其中元素样子如下: 注意 已婚 class是ok, 未婚class是err
// <a class='ok'>凯凯1-23-7000</a>
var a = emps.map(value => {
return `<a class='${value.married ? 'ok' : 'err'}'>${value.ename}-${value.age}-${value.salary}</a>`
})
console.log(a)
</script>
</body>
</html>
forEach
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>foreach</title>
</head>
<body>
<script>
// foreach: 属于数组的高阶函数, 其功能非常简单: 简单的遍历数组, 没有返回值
var nums = [11, 22, 33, 44, 55, 66, 77]
// 遍历数组的4种方式
// 最原始:
for (let i = 0; i < nums.length; i++) {
console.log(i, nums[i]);
}
// 简化版: for..in 非数组独有, 是遍历对象的公用方法
for (let i in nums) {
console.log(i, nums[i]);
}
// ES6提供了专门遍历数组的 for..of
// 用于直接遍历值, 不读取序号
for (let value of nums) {
console.log(value);
}
// 形参, 声明的变量: 都是自定义的
// var/function/let/const/class: 声明自定义变量
// forEach: 数组的高阶函数, 单纯遍历没有返回值
nums.forEach((value, index, array) => {
console.log(index, value);
})
// 计算出 nums 中元素的总和
var a = 0
for (const value of nums) {
a += value
}
console.log(a);
var a = 0
// 得益于语法糖的存在, forEach完成一些简单的循环, 更加简洁
nums.forEach(value => a += value)
console.log(a);
var products = [
{ pname: "iPhone1", price: 8999, count: 4 },
{ pname: "iPhone2", price: 7999, count: 1 },
{ pname: "iPhone3", price: 6999, count: 6 },
{ pname: "iPhone4", price: 5999, count: 8 },
]
// 计算出所有商品的总金额
var a = 0
products.forEach(value => a += value.price * value.count)
console.log(a);
</script>
</body>
</html>
reduce
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>reduce</title>
</head>
<body>
<script>
// reduce: 合并数组数据, 遍历数组的元素, 把处理后的返回值累加在一起
var nums = [11, 22, 33, 44, 55, 66, 77, 88]
// 把 数组的所有元素 累加在一起
// a : 相当于捐款箱, 收集每次累加的结果, 然后交给下一个元素
// 参数2: 0 是a的初始值, 即第一次循环时的初值
var s = nums.reduce((a, value) => {
return a + value
}, 0)
// 每次遍历, 都会把结果传递给 下一次的参数1, 即a
console.log(s);
var products = [
{ pname: "iPhone1", price: 8999, count: 4 },
{ pname: "iPhone2", price: 7999, count: 1 },
{ pname: "iPhone3", price: 6999, count: 6 },
{ pname: "iPhone4", price: 5999, count: 8 },
]
// reduce的流程
// 1个雪糕 -> 家乐(吃一口) -> 思琪(吃一口) -> 波波(吃一口) -> 最终返回 雪糕棍
var s = products.reduce((a, value) => {
return a + value.price * value.count
}, 0)
// 首次1: a = 0, 累加第一个商品的数量x单价, 传递给下一次循环
// 第2次: a = 上一次的返回值, 累加当前的, 再传给下一次
// 第3次: a = 上一次的返回值, 累加当前的, 再传给下一次
// ...
// 最终reduce的结果就是 最后一次的 返回值
console.log(s);
</script>
</body>
</html>
let/const
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>let_const</title>
</head>
<body>
<script>
// let/const: 都是 ES6 中推出的新特性, 用来声明变量
// 特征1: 没有全局变量污染, 存储在 script 作用域中
// 特征2: 没有声明提升(不准确) -- 作者设定了 暂存死区 的概念
// 没声明a: 报错 a is not defined
// let声明的变量: 有声明提升, 阅读报错发现--知道a的存在
// 报错: cannot access 'a' before initialization
// 不能 访问 a 在 初始化代码 之前
// 准确解释: let 提升了, 但是在声明代码行执行之前, 一直处于暂存死区状态 -- 无法使用
// 作者想法: 没有去掉声明提升, 但是利用报错强制用户必须在声明之后再使用变量 -- 效果相当于没有声明提升
let a
console.log(a);
// const: 常量, 声明时必须赋值, 之后就无法重新赋值
const PI = 3.14
// const A //必须赋值, 否则报错
// PI = 999 //无法重新赋值
// 注意: 常量的值如果是对象类型, 可以修改
const 家乐的女孩 = { name: '古力娜扎', hair: '黑色' }
// 对象特殊: 不更换地址, 可以修改其中的值
家乐的女孩.hair = '金黄色'
家乐的女孩.age = 33
console.log(家乐的女孩);
// 不允许换人, 指向其他内存地址
// 家乐的女孩 = { name: "迪丽热巴" }
</script>
</body>
</html>
保护对象的方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>保护对象的方式</title>
</head>
<body>
<script>
// defineProperty: 操作方式更加 细致, 可以配置每个属性
// 当前学习的: 直接配置整个对象 而非某个属性 -- 影响范围广
'use strict'
var emp = { ename: "凯凯", age: 33 }
// 修改操作有: 增删改
// 让对象无法 增加 属性
// prevent阻止 Extensions扩展
// Object.preventExtensions(emp)
// 不可 增删
// seal: 密封
// Object.seal(emp)
// 不可 增删改
// freeze: 冻结
Object.freeze(emp)
// emp.wife = '迪丽热巴'
// delete emp.age
emp.age = 49
console.log(emp);
</script>
</body>
</html>
数组展开符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数组的展开语法</title>
</head>
<body>
<script>
// 来自 ES6 的新增语法糖: 展开语法
var a = [11, 22, 33, 767, 77, 88, 88]
// ... 运算符: 去掉 数组的 []
var b = [44, 55, ...a]
console.log(b);
// max: 的参数只能是 1个1个的, 不接数组类型
// ES6之前: Math.max.apply(0, a)
// ES6之后: ... 来去掉[], 把数组里面的东西拿出来
var m = Math.max(...a)
console.log(m);
</script>
</body>
</html>
对象的展开语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象的展开语法</title>
</head>
<body>
<script>
var a = { x: 10, y: 20 }
var b = { y: 30, z: 40 }
// ... : 去掉对象/数组的 外层括号
// 同名的属性, 后写的覆盖先写的
var c = { w: 100, ...a, ...b }
// 相当于: { w:100, x: 10, y: 20, y: 30, z: 40 }
console.log(c);
</script>
</body>
</html>
剩余参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>剩余参数</title>
</head>
<body>
<script>
// ES6之前: 通过 arguments 来使用函数的 不固定数量的参数
// arguments的缺点:
// - 隐藏的变量
// - 类型是 类数组, 不具备数组的各种方法
// ES6之后: 允许用 ... 来表示接受剩余参数
function show(...args) {
// 习惯上, 变量名叫 args, 名字随便
console.log(arguments);
console.log(args);
// args: 数组类型, 可以用数组的方法
args.push(1111)
}
show(11, 22, 33, 44, 55)
// 剩余参数
// ...: 代表剩余参数, 存储剩下的 没有专门声明参数的
function showGirls(主食, ...菜品) {
console.log(主食);
console.log(菜品);
}
showGirls('米饭', '炒菜', '海鲜汤', '炒肉')
</script>
<script>
// ...: 固定运算符
// nums: 自定义变量, 名字无所谓
// ... 就代表把收到的所有参数, 保存到后方的变量里(数组类型)
function show(x, ...nums) {
console.log('nums:', nums);
// nums = [2, 3, 4]
// nums[0]: 数组的下标取值, 获取数据
}
function show(x, y, ...nums) {
console.log('x:', x);
console.log('nums:', nums);
}
show(1, 2, 3, 4)
</script>
</body>
</html>
参数默认值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>参数的默认值</title>
</head>
<body>
<script>
// ES6的 函数参数增强特征: 可以为函数的参数设置默认值
function show(name = '家乐') {
// 相当于: var name = '家乐'
// 如果传参了: name = '思琦'
console.log(name);
}
show() // 没有传递参数, 则 采用默认值
show('思奇') // 如果传递参数, 则 使用传递的值
</script>
</body>
</html>
数组解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数组解构语法</title>
</head>
<body>
<script>
// 解构语法: 可以快速从数组中抓取元素 保存到变量里
var skills = ['唱', '跳', 'rap', '打篮球']
// 保存到不同的变量里
let s1 = skills[0]
let s2 = skills[1]
let s3 = skills[2]
let s4 = skills[3]
// 解构语法:
let [q, w, e, r] = skills
console.log(q, w, e, r);
// 练习
var r1 = [10, 20, 50] // 分别存储到变量 x, y, z 中
var [x, y, z] = r1
console.log(x, y, z);
// 可选解构
var names = ['凯凯', '楠楠', '亮亮', '铭铭']
// n1 n2 n3
var [n1, n2, , n3] = names //不想解构的, 可以省略不写
console.log(n1, n2, n3);
// 灵活用法: 互换变量的值
var yy = 100
var rr = 200;
// [yy, rr] = [200, 100]
// 先组合出一个数组 [rr, yy] == [200, 100]
// 然后解构
[yy, rr] = [rr, yy] // = [200, 100]
console.log(yy, rr);
</script>
</body>
</html>
对象解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象解构语法</title>
</head>
<body>
<script>
//
var emp = {
ename: "家乐",
age: 18
}
// 传统写法
var ename = emp.ename
var age = emp.age
// 解构语法
var { ename, age } = emp
console.log(ename, age);
// 练习
var p1 = { pname: "iPhone", price: 9999, count: 5 }
// 把属性读取出来, 保存在变量里
var { count, pname } = p1
// 对象类型:无序的
console.log(count, pname);
// 起别名
var p2 = { pname: "mike", count: 10 }
// count -> c2
var { count: c2 } = p2
console.log(c2);
</script>
</body>
</html>
复杂解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>复杂解构</title>
</head>
<body>
<script>
var emp = {
ename: '家乐',
age: 23,
skills: ['唱', '跳', 'rap', '篮球'],
love: ['杨洋', '王一博', '胡歌']
}
// 把内容解构出来
var { ename, age: eage, skills: [q, w, e, r], love } = emp
console.log(ename, eage, q, w, e, r, love);
var iPhone = {
maker: "Apple",
price: 9999,
tags: ['高刷', 'iOS', '最佳cpu'],
}
//
var { maker, price: p_price, tags: [tag1, tag2, tag3] } = iPhone
console.log(maker, p_price, tag1, tag2, tag3);
</script>
</body>
</html>
参数解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数的参数解构</title>
</head>
<body>
<script>
// 计算 立方体的 体积
// 参数解构: 收到对象类型的参数, 直接解构
function volume({ x, y, z }) {
// 解构出来再用
// const { x, y, z } = rect
console.log(x * y * z);
// console.log(rect.x * rect.y * rect.z);
}
volume({ x: 10, y: 20, z: 30 })
var emps = [
{ ename: '凯凯1', age: 23, salary: 7000, married: true },
{ ename: '凯凯2', age: 33, salary: 17000, married: true },
{ ename: '凯凯3', age: 44, salary: 9000, married: false },
{ ename: '凯凯4', age: 28, salary: 12000, married: true },
{ ename: '凯凯5', age: 35, salary: 33000, married: false },
]
// map: 把数组映射成 HTML 代码
var a = emps.map(value => {
return `<a class='${value.married ? 'ok' : 'err'}'>${value.ename}-${value.age}-${value.salary}</a>`
})
// 利用解构简化:
var a = emps.map(value => {
const { ename, age, salary, married } = value
return `<a class='${married ? 'ok' : 'err'}'>${ename}-${age}-${salary}</a>`
})
// 参数解构: 参数解构, 需要用() 包围, 防止歧义
var a = emps.map(({ ename, age, salary, married }) => {
return `<a class='${married ? 'ok' : 'err'}'>${ename}-${age}-${salary}</a>`
})
console.log(a)
var products = [
{ price: 1000, count: 4, pname: '凯凯1' },
{ price: 2000, count: 7, pname: '凯凯2' },
{ price: 1500, count: 6, pname: '凯凯3' },
{ price: 1200, count: 1, pname: '凯凯4' },
]
// 过滤出总价格大于 4000 的项目: filter
var a = products.filter((value) => value.price * value.count > 4000)
var a = products.filter(({ price, count }) => price * count > 4000)
// 别名:
var a = products.filter(({ price: p, count: c }) => p * c > 4000)
console.log(a);
</script>
</body>
</html>
class语法
静态属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>class语法</title>
</head>
<body>
<script>
// class语法: 从 Java 语言中出现
// JS最初开发的时候, 作者认为 Java 的class语法过于庞大, 所以设计了更加简单的 万物皆对象/构造函数 语法
// 随着时间的发展: JS竟然一跃成为 非常火的语言, 广受关注
// 群众的呼声: 希望引入 Java 中更加强大的 class 语法, 在 2015 年 的 ES6 中引入了 class 语法 -- 对于会java的程序员很友好
// 但是: 尴尬的情况 -- 传统的JS程序员比较守旧, 排斥 class 语法
// 所以: 目前class好用 但是 用的少
// 对象类型: 在js中
var emp = {
ename: "凯凯",
age: 22,
phone: '18800120033'
}
console.log(emp);
// 关键词 class 用于声明 类(java的称呼)
class Emp1 {
// static: 称为静态属性(java的称呼)
// 特点: 存储在对象中, 可以直接使用
static ename = '凯凯'
static age = 22
static phone = '19900120033'
}
// 本质上是 JS 的函数
console.dir(Emp1);
console.log(Emp1.ename, Emp1.age, Emp1.phone);
</script>
<script>
// JS 的对象
var math = {
PI: 3.1415,
LN2: 0.69314,
E: 2.718,
LN2: 0.693
}
// 改造成: class 的写法, 名字不能重复, 所以叫 math1
class math1 {
// 在JS中, 结尾分号可以省略
static PI = 3.1415;
static LN2 = 0.69314;
static E = 2.718;
static LN2 = 0.693;
}
console.dir(math1);
console.log(math1.PI, math1.LN2);
</script>
</body>
</html>
构造函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>构造语法-class</title>
</head>
<body>
<!--
总结:
在 JS中制作一个构造函数, 需要两步:
- 构造函数本身
- 构造函数的原型 prototype 中添加共享方法 (需要程序员会原型)
在 java中制作一个构造函数
- constructor: 固定的名称, 放构造函数
- 不需要会原型, 就能制作出完美的构造函数 --(对java程序员极具吸引力)
-->
<script>
// JS的构造函数: 分两部分书写
// 1. 生成对象 -矩形
function Rect(x, y) {
// this是什么? new触发, 当前生成的对象, 此处就是r1
this.x = x
this.y = y
}
// 2. 原型中存储共享的方法
// JS程序员必须懂原型, 才能写出构造函数
Rect.prototype.area = function () {
return this.x * this.y
}
Rect.prototype.perimeter = function () {
return (this.x + this.y) * 2
}
var r1 = new Rect(10, 30)
console.dir(Rect);
console.log(r1);
</script>
<script>
// java的构造函数 -- 精髓
class Rect1 {
// java的class中, 不需要function关键词, 就能声明函数
// 关键词,不可修改: constructor, 固定名称, 称为 构造方法
// 当 new 运算符时, 自动触发
constructor(x, y) {
console.log('constructor: 被触发');
this.x = x
this.y = y
}
// 在java中, 书写的所有方法, 都会 `自动` 放到原型里
area() {
return this.x * this.y
}
// get关键词: 变身为计算属性, 使用时不用() 能自动触发
get perimeter() {
return (this.x + this.y) * 2
}
}
var r2 = new Rect1(20, 50)
console.dir(Rect1); //查看 prototype 属性
console.log(r2);
console.log(r2.area());
// 由于 是 get 计算属性, 所以不用() 自动就能触发
console.log(r2.perimeter);
</script>
</body>
</html>
继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>继承</title>
</head>
<body>
<script>
// 面向对象开发的 三大特征: 封装 继承 多态
// 封装: 利用{} 把一些代码括起来, 形成一个整体, 以后可以复用
// 在JS中的具体表现: 函数
// 继承: 当使用对象的一个属性时, 自身没有, 则到父中查找
// 在JS中的具体表现: 原型链
// 多态: 同一个构造函数生成的对象, 对象可以自定义. 效果如下:
// 自身有用自己的, 自身没有用父的 -- 龙生九子,各有不同
var emp = { age: 99 }
emp.__proto__.age = 18
console.log(emp.age);
console.log(emp);
class A {
show() {
console.log('我是A');
}
}
// extends: 继承. 本质上把原型链 指向 A, 认A做父
class B extends A { }
console.dir(B); //查看原型 prototype
var b = new B()
b.show()
</script>
</body>
</html>
多态
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多态</title>
</head>
<body>
<script>
class A {
constructor(x, y) {
this.x = x
this.y = y
}
area() {
return this.x * this.y
}
}
// B继承A, 可以使用A的东西
class B extends A {
// java的称呼: 重写 -- 书写一个与父中相同的函数
// 根据原型链的就近原则: B中有area, A中也有area
// 优先使用 B中的area : 自己有用自己的
area() {
// java提供了关键词 super: 代表父元素, 即A
console.log(super.area())
console.log('我是B中的area');
}
}
var b = new B(10, 50)
console.log(b);
console.log(b.area());
</script>
</body>
</html>
class总结
class是来自java
语言的一套语法, 特别擅长制作 构造函数
但是
: JS开发实战中, 传统的 function 使用更多, class使用较少
-
建议: 了解为主
-
实在感兴趣: 百度搜下视频 JS的class语法
作业题
var products = [
{ pname: "iPhone1", price: 8999, count: 4, checked:true },
{ pname: "iPhone2", price: 7999, count: 1, checked:true },
{ pname: "iPhone3", price: 6999, count: 6, checked:false },
{ pname: "iPhone4", price: 5999, count: 8, checked:true },
]
// 计算出 checked 为 true, 即 勾选状态 为真的 商品总价格, --- 关联以后的 购物车制作
// 分别用foreach 和 reduce 两种方式实现
// 使用参数的解构语法书写
var obj = {
nickname: "睡懵的渣皇",
rid: 8922441,
roomName: "有梦想谁都了不起!!",
tags:['颜值', '帅气逼人', '直播达人']
}
// 把 obj 中的变量都解构出来, rid 别名为 roomId. tags解构成 t1 t2 t3
// JS的构造函数如下, 修改成 class 语法
function Cube(width, length, height){
this.width = width
this.length = length
this.height = height
}
Cube.desc = '立方体'
Cube.prototype.volume = function (){
return this.width * this.length * this.height
}
Cube.prototype.area = function (){
const {width:w, height:h, length:l} = this
return (w*h + w*l + h+l)*2
}
Cube.prototype.perimeter = function (){
const {width:w, height:h, length:l} = this
return (w+h+l)*4
}
var c1 = new Cube(100, 40, 22)
console.log(Cube.desc)
console.log(c1.volume())
console.log(c1.area())
console.log(c1.perimeter())
JSCORE06
复习
-
数组的高阶函数
-
every: 判断数组中
每一个
元素都符合条件. 类似逻辑与, 都真 才是真 -
some: 判断数组中
至少有一个
元素符合条件. 类似逻辑或, 有真 就是真 -
filter: 过滤器. 把满足条件的元素 组成新的数组
-
map: 映射. 把数组的数据处理后 返回值组成新的数组. 特别适合 数据 ->HTML
-
forEach: 简单的遍历数组. 没有返回值
-
ES6提供了:
for...of
也能快速遍历数组
-
-
reduce: 归纳. 把数组中的元素挨个遍历后, 归纳成一个值
-
-
ES6新增的语法
-
展开符
...
-
...数组
: 把数组展开, 即去掉数组的[]
包围 -
...对象
: 把对象展开, 即去掉对象的{}
包围
-
-
函数增强语法
-
剩余参数:
function 函数名(...变量)
-
变量是数组类型, 接收收到的所有参数, 作用与
arguments
一样
-
-
参数默认值:
function 函数(参数=值)
-
使用函数时, 如果不传递参数, 则 参数使用默认值
-
-
-
解构语法
-
数组解构:
var [a, b, ,c] = [11, 22, 33, 44]
-
a=11; b=22; c=44
-
-
对象解构:
var {age, salary, sid} = {sid:11, salary:9999, age:33}
-
别名语法:
var {salary: s} = {salary: 4488}
-
-
函数的参数解构 -
必须能看得懂
var obj = {x: 10, y:30, z: 44} function show( {x, y} ){ return x + y } show(obj)
-
-
-
class语法
-
来自于 Java 的语法, 特别擅长
构造函数
的创建 -
但是: 好用 然而 JS程序员比较守旧 -- 目前普及率不高
-
静态属性写法:
不如JS方便
// js中 var emp1 = {ename:'凯凯', age:33} emp1.ename // java class emp2 { static ename = '凯凯'; static age = 33 } emp2.ename
-
构造函数
class Emp{ // 固定名称的函数, 在new 的时候触发 constructor(x, y){ this.x = x this.y = y } // 全自动操作原型, 把所有的函数自动存储到原型中 area(){ return this.x * this.y } } var e = new Emp(11,22)
-
面向对象的 3 大特征:
-
封装: 用
{}
把代码封装在一起, 后续随时触发-
具体表现: 函数
-
-
继承: 原型链指向其他的对象. 自己没有的 到原型中查找
-
多态: 子中可以重写一个 与 父相同的方法, 使用时优先使用子中带有的
class A{ show(){ console.log('我是A') } } class B extends A{ // B中可以书写一个同名的 show方法 // 使用时, 优先使用自身的show方法 show(){ console.log('我是B') // 关键词: super super.show() // 使用父, A的show方法 } } var b = new B() b.show() // B自身没有show方法, 则使用父A的 show方法
-
-
回调地狱
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>回调地狱</title>
</head>
<body>
<!-- 回调地狱: 在JS中进行一些异步操作时(网络操作), 层层嵌套, 会导致代码阅读困难 -->
<!-- 如下场景: 注册操作 -->
<!-- 1. 先发请求, 验证用户名是否合法 -->
<!-- 2. 再发请求, 验证邮箱是否合法 -->
<!-- 3. 再发请求, 验证手机号是否合法 -->
<!-- 4. 再发请求, 开始注册 -->
<script>
function register() {
// 用 定时器 来模拟 延时操作, 0.5 秒完成
console.log('检查用户名...');
setTimeout(() => {
// 通过随机数来 随机决定成功与否
if (Math.random() > 0.7) {
console.log('用户名正确, 开始验证邮箱...');
setTimeout(() => {
if (Math.random() > 0.7) {
console.log('邮箱正确, 开始验证手机号...');
setTimeout(() => {
if (Math.random() > 0.7) {
console.log('手机号正确, 开始注册...');
setTimeout(() => {
if (Math.random() > 0.7) {
console.log('注册成功');
} else {
console.log('注册失败!');
}
}, 500);
} else {
console.log('手机号错误!');
}
}, 500);
} else {
console.log('邮箱错误');
}
}, 500);
} else {
console.log('用户名错误!');
}
}, 500);
}
</script>
</body>
</html>
解决方案 : Promise
Promise
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise</title>
</head>
<body>
<!-- Promise: ES6提供的一个构造函数, 专门解决回调地狱问题 -->
<!-- 代码量一点都不少, 只是格式上更加容易阅读 -->
<script>
// 先练习基本的格式
// 固定写法, 先抄10遍
new Promise((resolve, reject) => { }).then(res => { }).catch(err => { })
// 安排工作.做完了.失败了
// Promise: 承诺
// resolve: 解决
// reject: 拒绝
// then: 然后
// catch: 抓取
new Promise((resolve, reject) => {
// 当调用 resolve 时, 会触发 then 中的箭头函数
// resolve的参数, 会传递给 then 中的箭头函数
// resolve('调用resolve') // 这代表成功
// reject 会触发 catch, 其参数传递到 catch 中
reject('调用reject') // 这代表失败
// resolve和 reject 互斥, 同一时间只能触发一种
})
.then(res => console.log('res:', res))
.catch(err => console.log('err:', err))
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise的使用</title>
</head>
<body>
<script>
// 家乐 学开车
// 教练: 踩油门 车就能跑; 踩刹车 车就能停
// 家乐问题: 油门哪来的? 踩油门为什么能跑, 发动机原理是什么?
// 但是: 家乐应聘当司机
// 考试题: 说说汽车的原理 -- 面试造火箭 工作拧螺丝
// 面试考 Promise原理的极少, 但是确实存在 -- 不是课上讲解的
// -- 考 3-4年 高级工程师的题 -- 扩展视频
function jiale() {
return new Promise((resolve, reject) => {
// 利用定时器模拟异步操作
setTimeout(() => {
var a = Math.random()
if (a > 0.5) {
resolve('家乐求婚成功:' + a)
} else {
reject('家乐被拒绝了:' + a)
}
}, 1000);
})
// return p
}
// 波波求婚
function bobo() {
// prom : 安装 ES6 插件后
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('波波求婚成功') : reject('波波失败!')
}, 1000);
});
}
function haoyang() {
// prom
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('浩洋成功求婚') : reject('浩洋失败!')
}, 1000);
});
}
jiale()
.then(res => {
console.log('res:', res)
// 如果成功, 则触发波波的求婚操作
return bobo()
// 返回值, 会触发下一次的 .then
})
.then(res => {
console.log('res:', res)
//
return haoyang()
})
.then(res => console.log('res:', res))
.catch(err => console.log('err:', err))
</script>
</body>
</html>
关于Promise的常考概念:
Promise有
3
种状态
刚new出来:
new Promise
--pending
触发 resolve 之后 --
fulfilled
触发 reject 之后 --
rejected
正则表达式
一套用与模糊匹配字符串
格式的语法
字符 | 含义 |
---|---|
\ | 依照下列规则匹配:在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解。例如,前面没有 "" 的 "b" 通常匹配小写字母 "b",即字符会被作为字面理解,无论它出现在哪里。但如果前面加了 "",它将不再匹配任何字符,而是表示一个字符边界。在特殊字符之前的反斜杠表示下一个字符不是特殊字符,应该按照字面理解。详情请参阅下文中的 "转义(Escaping)" 部分。如果你想将字符串传递给 RegExp 构造函数,不要忘记在字符串字面量中反斜杠是转义字符。所以为了在模式中添加一个反斜杠,你需要在字符串字面量中转义它。/[a-z]\s/i 和 new RegExp("[a-z]\\s", "i") 创建了相同的正则表达式:一个用于搜索后面紧跟着空白字符(\s 可看后文)并且在 a-z 范围内的任意字符的表达式。为了通过字符串字面量给 RegExp 构造函数创建包含反斜杠的表达式,你需要在字符串级别和正则表达式级别都对它进行转义。例如 /[a-z]:\\/i 和 new RegExp("[a-z]:\\\\","i") 会创建相同的表达式,即匹配类似 "C:" 字符串。 |
^ | 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。例如,/^A/ 并不会匹配 "an A" 中的 'A',但是会匹配 "An E" 中的 'A'。当 '^ ' 作为第一个字符出现在一个字符集合模式时,它将会有不同的含义。反向字符集合 一节有详细介绍和示例。 |
$ | 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。例如,/t$/ 并不会匹配 "eater" 中的 't',但是会匹配 "eat" 中的 't'。 |
* | 匹配前一个表达式 0 次或多次。等价于 {0,} 。例如,/bo*/ 会匹配 "A ghost boooooed" 中的 'booooo' 和 "A bird warbled" 中的 'b',但是在 "A goat grunted" 中不会匹配任何内容。 |
+ | 匹配前面一个表达式 1 次或者多次。等价于 {1,} 。例如,/a+/ 会匹配 "candy" 中的 'a' 和 "caaaaaaandy" 中所有的 'a',但是在 "cndy" 中不会匹配任何内容。 |
? | 匹配前面一个表达式 0 次或者 1 次。等价于 {0,1} 。例如,/e?le?/ 匹配 "angel" 中的 'el'、"angle" 中的 'le' 以及 "oslo' 中的 'l'。如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。还用于先行断言中,如本表的 x(?=y) 和 x(?!y) 条目所述。 |
. | (小数点)默认匹配除换行符之外的任何单个字符。例如,/.n/ 将会匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不会匹配 'nay'。如果 s ("dotAll") 标志位被设为 true,它也会匹配换行符。 |
(x) | 像下面的例子展示的那样,它会匹配 'x' 并且记住匹配项。其中括号被称为捕获括号。模式 /(foo) (bar) \1 \2/ 中的 '(foo) ' 和 '(bar) ' 匹配并记住字符串 "foo bar foo bar" 中前两个单词。模式中的 \1 和 \2 表示第一个和第二个被捕获括号匹配的子字符串,即 foo 和 bar ,匹配了原字符串中的后两个单词。注意 \1 、\2 、...、\n 是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像 $1 、$2 、...、$n 这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1') 。$& 表示整个用于匹配的原字符串。 |
(?:x) | 匹配 'x' 但是不记住匹配项。这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式。看看这个例子 /(?:foo){1,2}/ 。如果表达式是 /foo{1,2}/ ,{1,2} 将只应用于 'foo' 的最后一个字符 'o'。如果使用非捕获括号,则 {1,2} 会应用于整个 'foo' 单词。更多信息,可以参阅下文的 Using parentheses 条目. |
x(?=y) | 匹配'x'仅仅当'x'后面跟着'y'.这种叫做先行断言。例如,/Jack(?=Sprat)/会匹配到'Jack'仅当它后面跟着'Sprat'。/Jack(?=Sprat|Frost)/匹配‘Jack’仅当它后面跟着'Sprat'或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。 |
(?<=y)x | 匹配'x'仅当'x'前面是'y'.这种叫做后行断言。例如,/(?<=Jack)Sprat/会匹配到' Sprat '仅仅当它前面是' Jack '。/(?<=Jack|Tom)Sprat/匹配‘ Sprat ’仅仅当它前面是'Jack'或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。 |
x(?!y) | 仅仅当'x'后面不跟着'y'时匹配'x',这被称为正向否定查找。例如,仅仅当这个数字后面没有跟小数点的时候,/\d+(?!.)/ 匹配一个数字。正则表达式/\d+(?!.)/.exec("3.141")匹配‘141’而不是‘3.141’ |
(?<!*y*)*x* | 仅仅当'x'前面不是'y'时匹配'x',这被称为反向否定查找。例如, 仅仅当这个数字前面没有负号的时候,/(?<!-)\d+/ 匹配一个数字。 /(?<!-)\d+/.exec('3') 匹配到 "3". /(?<!-)\d+/.exec('-3') 因为这个数字前有负号,所以没有匹配到。 |
x|y | 匹配‘x’或者‘y’。例如,/green|red/匹配“green apple”中的‘green’和“red apple”中的‘red’ |
{n} | n 是一个正整数,匹配了前面一个字符刚好出现了 n 次。 比如, /a{2}/ 不会匹配“candy”中的'a',但是会匹配“caandy”中所有的 a,以及“caaandy”中的前两个'a'。 |
{n,} | n是一个正整数,匹配前一个字符至少出现了n次。例如, /a{2,}/ 匹配 "aa", "aaaa" 和 "aaaaa" 但是不匹配 "a"。 |
{n,m} | n 和 m 都是整数。匹配前面的字符至少n次,最多m次。如果 n 或者 m 的值是0, 这个值被忽略。例如,/a{1, 3}/ 并不匹配“cndy”中的任意字符,匹配“candy”中的a,匹配“caandy”中的前两个a,也匹配“caaaaaaandy”中的前三个a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的a。 |
[xyz\] | 一个字符集合。匹配方括号中的任意字符,包括转义序列。你可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义。他们不必进行转义,不过转义也是起作用的。 例如,[abcd] 和[a-d]是一样的。他们都匹配"brisket"中的‘b’,也都匹配“city”中的‘c’。/[a-z.]+/ 和/[\w.]+/与字符串“test.i.ng”匹配。 |
[^xyz\] | 一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。你可以使用破折号(-)来指定一个字符范围。任何普通字符在这里都是起作用的。例如,abc 和 a-c 是一样的。他们匹配"brisket"中的‘r’,也匹配“chop”中的‘h’。 |
[\b\] | 匹配一个退格(U+0008)。(不要和\b混淆了。) |
\b | 匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,例如在字母和空格之间。注意,匹配中不包括匹配的字边界。换句话说,一个匹配的词的边界的内容的长度是0。(不要和[\b]混淆了)使用"moon"举例: /\bm/匹配“moon”中的‘m’; /oo\b/并不匹配"moon"中的'oo',因为'oo'被一个“字”字符'n'紧跟着。 /oon\b/匹配"moon"中的'oon',因为'oon'是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。 /\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。备注: JavaScript的正则表达式引擎将特定的字符集定义为“字”字符。不在该集合中的任何字符都被认为是一个断词。这组字符相当有限:它只包括大写和小写的罗马字母,十进制数字和下划线字符。不幸的是,重要的字符,例如“é”或“ü”,被视为断词。 |
\B | 匹配一个非单词边界。匹配如下几种情况:字符串第一个字符为非“字”字符字符串最后一个字符为非“字”字符两个单词字符之间两个非单词字符之间空字符串例如,/\B../匹配"noonday"中的'oo', 而/y\B../匹配"possibly yesterday"中的’yes‘ |
\c*X* | 当X是处于A到Z之间的字符的时候,匹配字符串中的一个控制符。例如,/\cM/ 匹配字符串中的 control-M (U+000D)。 |
\d | 匹配一个数字。``等价于[0-9] 。例如, /\d/ 或者 /[0-9]/ 匹配"B2 is the suite number."中的'2'。 |
\D | 匹配一个非数字字符。``等价于[^0-9] 。例如, /\D/ 或者 /[^0-9]/ 匹配"B2 is the suite number."中的'B' 。 |
\f | 匹配一个换页符 (U+000C)。 |
\n | 匹配一个换行符 (U+000A)。 |
\r | 匹配一个回车符 (U+000D)。 |
\s | 匹配一个空白字符,包括空格、制表符、换页符和换行符。等价于[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如, /\s\w*/ 匹配"foo bar."中的' bar'。经测试,\s不匹配"\u180e",在当前版本Chrome(v80.0.3987.122)和Firefox(76.0.1)控制台输入/\s/.test("\u180e")均返回false。 |
\S | 匹配一个非空白字符。等价于 [^ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] 。例如,/\S\w*/ 匹配"foo bar."中的'foo'。 |
\t | 匹配一个水平制表符 (U+0009)。 |
\v | 匹配一个垂直制表符 (U+000B)。 |
\w | 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_] 。例如, /\w/ 匹配 "apple," 中的 'a',"$5.28,"中的 '5' 和 "3D." 中的 '3'。 |
\W | 匹配一个非单字字符。等价于 [^A-Za-z0-9_] 。例如, /\W/ 或者 /[^A-Za-z0-9_]/ 匹配 "50%." 中的 '%'。 |
\*n* | 在正则表达式中,它返回最后的第n个子捕获匹配的子字符串(捕获的数目以左括号计数)。比如 /apple(,)\sorange\1/ 匹配"apple, orange, cherry, peach."中的'apple, orange,' 。 |
\0 | 匹配 NULL(U+0000)字符, 不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列。 |
\xhh | 匹配一个两位十六进制数(\x00-\xFF)表示的字符。 |
\uhhhh | 匹配一个四位十六进制数表示的 UTF-16 代码单元。 |
\u{hhhh}或\u{hhhhh} | (仅当设置了u标志时)匹配一个十六进制数表示的 Unicode 字符。 |
正则匹配
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正则表达式</title>
</head>
<body>
<script>
// 正则表达式: Regular Expression 简称 RegExp
// 正则不是 JS 的特性, 而是一套通用的 字符串模糊 匹配方案
// 官方提供了一个字典: 一些字符 对应 一些特殊的说明含义
// \d : 匹配一个数字
// 要求: 查找到 字符串中所有的 数字
// match: 匹配.字符串的一个方法,用来查询出符合正则表达式要求的字符
// 参数要求是正则表达式, 正则要求存放在 // 中
// 类似于 字符串要放在 引号中; "\d" -字符串 /\d/ -正则
// g: global 全局 ; 是正则表达式的修饰符, 代表匹配出所有符合条件的
var words = '家乐兜里有500元, 花了100, 还剩400. Nice!'
// 找到英文字母
// []: 代表自定义的字符集合
// 查询出中文: 中文在Unicode字典中的编码是 \u4e00 开始, \u9fa5 结束
var a = words.match(/[\u4e00-\u9fa5]/g)
console.log(a);
// i : ignore 忽略, 属于正则修饰符, 代表 忽略大小写
var a = words.match(/[a-z]/ig)
console.log(a);
var a = words.match(/[a-zA-Z]/g)
console.log(a);
var a = words.match(/[a-z]/g)
console.log(a);
var a = words.match(/\d/g)
console.log(a);
</script>
</body>
</html>
正则替换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正则替换</title>
</head>
<body>
<script>
// replace: 替换
var words = '家乐欠债500元, 已还100, 还剩400未还'
// 把数字变为 *
// 把参数1 正则匹配到的字符, 替换成 参数2
var a = words.replace(/\d/g, '*')
var a = words.replace(/家乐/, '**')
console.log(a);
// 进阶用法: 搭配捕获组使用
var phone = '18879798888'
// 要求 把手机号中间 4位, 变为 ****
var a = phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
// 猜: $1 和 $3 是什么意思?
// () :称为捕获组, 用于抓取出匹配到的指定内容
// {n} : 有n个 例如 \d{3} 代表3个数字
// $n: 代表 第n个捕获组中的值, 例如 $1就是第一个小括号里的值
console.log(a);
// 练习: 改为 188-7979-8888
var phone = '18879798888'
var a = phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3')
console.log(a);
// 颠倒位置 18879798888 -> 18888887979
var a = phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1$3$2')
// 第1个() 第2个 第3个
console.log(a);
</script>
</body>
</html>
正则验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正则验证</title>
</head>
<body>
<script>
// 通过浏览器 收集用户输入的号码
var p = prompt('请输入手机号')
console.log('p:', p)
// 判定是否为手机号: 11位数字 1开头 第二位 3-9
// ^ : 代表字符串的开头
// $ : 代表字符串的结尾
var phone_reg = /^1[3-9]\d{9}$/
// 使用正则 来 验证字符串
var a = phone_reg.test(p) // test:验证,测试
console.log(a ? '是手机号' : '不是手机号');
</script>
</body>
</html>
正则的构造方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正则的构造方式</title>
</head>
<body>
<script>
// 所有的字面量, 都有对应的构造方式 -- 本体
// new Array(); new Object();
// new Number(); new String()
// new RegExp() 创造正则
var words = '亮亮99 涛涛88 家乐59 波波dd'
// 构造方式创建正则
// JS的 \ 是转义符, \d 转义成 d
// 需要用 \\d 来转义成 普通的 \d
var r = new RegExp('\\d\\d', 'g')
var a = words.match(r)
console.log(a)
var a = words.match(/\d\d/g)
console.log(a);
</script>
</body>
</html>
关于JS高级
总体分两个环节:
-
理论:
面试必考
, 作为成熟的JS程序员就应该会的理论-
声明提升
-
闭包
-
原型
-
精确配置对象
-
-
实战应用
-
ES5新特性
-
ES6新特性 -- 实际开发用得到
-
展开符
-
解构
-
函数增强
-
正则
-
-