JSCORE01
目录
前言
一、声明提升
二、宿主 window
三、断点功能
四、匿名函数解决全局污染
五、作用域链
六、闭包
七、私有
八、arguments
九、函数重载
十、方括号属性语法
十一、重载练习
十二、this
总结
前言
JSCORE01学习开始
一、声明提升
- 报错方案: 让用户自己修正代码 -- 一定是精确表达用户思想的代码, 但是 麻烦
- 自动修正: 系统帮用户修正 -- 让程序员舒服 但是 有可能
自动修正
修错了- 修正法案:
声明提升
法案<!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>声明操作 09:37</title> </head> <body> <!-- 声明: 在内存中开辟一个空间, 起个名字 用于 存放东西, 以后就可以通过这个名字来找到这个东西 主要分两大类: - 变量: 一些固定存在的值 - 函数: 可以运行的值, 用()触发运行 --> <script> // 声明变量 同时 赋值 var a = 5 var b = 10 var c = true // 声明函数 同时 赋值 function show() { } function talk() { } // // 面试考点: JS引擎 与 其他编程语言 不同的地方 // 声明提升: // -- 初衷: JS引擎会 `智能化` 的把不合理的代码 修正为 合理的代码 // 不合理: 数学运算表达式中, 参与运算的值 必然是数字类型, 才合理 // 对于不合理的代码, 有两种方案 // 1. 报错: C Java - 强类型语言 // 2. 自动修正: PHP JS - 弱类型语言; JS引擎会先阅读代码, 把不合理的进行修正, 然后再执行 console.log(10 + true) // 修改为: console.log(10 + 1) 再运行; 隐式类型转换 // 修正法案: 变量必须先声明 后使用 // 所以: JS引擎会自动把所有的声明操作提升到 作用域的顶部 function x() { // clg : 可以快速生成console.log console.log(111); } x() //222 function x() { console.log(222); } x() //222 </script> </body> </html>
二、宿主 window
<!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>window 10:22</title> </head> <body> <!-- 宿主环境: 寄生的宿主 JS语言主要有两个宿主环境 - Node.js: 提供了一套操作服务器的API -- 技能 - 浏览器: 提供了一套操作浏览器的API -- 技能 API: 应用程序接口 -- 操作软件的一些 代码 --> <script> // 按ESC 退出自动生成后的状态, 写代码才有提示 console.log(window) //clg // window: 称为 全局对象, 其中存放了能够全局使用的`技能` // window.alert('你好, window') // 设定: 使用变量 或 函数时, 如果没有声明过此变量, 则自动从window中查找使用 // alert('hello world!') // 全局变量污染 // 系统提供的技能都存放在 window 对象里 // 可以认为: window对象就是专门存放系统技能的 // var: 在哪个作用域中书写, 就会在哪个作用域中声明变量 // --- 直接在脚本中书写, 则存储在window对象里, 造成污染(自定义的值 和 官方给的值都放在一起, 就是污染) var a = 10 // 变量b 没有用var 声明, 则触发另一个场景: // 自动从window中查找, b是通过对象的属性新增语法添加的 b = 20 // window.b = 20 </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>函数作用域 10:43</title> </head> <body> <!-- 与 全局作用域 相对相应的 局部作用域(函数作用域) --> <script> // 创造函数作用域 -- 提供了一个最快捷的语法 : 匿名函数自调用 // function x() { } // x() // var x = function () { } // x() // x直接换成函数, 用()包围, 识别为一个整体 // 实际书写JS时, 为了规避全局污染, 会选择 在脚本中先书写一个匿名函数自调 // 然后把代码在这里书写即可 (function () { var a = 10 // 需要利用 浏览器提供的 断点调试 工具, 才能直观看到局部作用域 console.log(1) // var: 在当前作用域中, 声明变量 // -- 误区: var 就一定全局污染---错误; 在脚本中声明才会 // 如果找不到声明的b变量, 则从 window 中查找 b = 20 // window.b = 20 // 修正法案-- 声明提升法案 // 声明操作 都会提升到作用域的顶部, 然后才会真正运行 // var b 就在局部作用域中, b=20 就会赋值给这个b变量, 不会污染 // var b })() console.log(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>作用域链 11:23</title> </head> <body> <!-- 作用域链: 当出现多层作用域嵌套的场景, 如果使用1个变量时, 按照就近原则查找使用 --> <script> var x = 10 function a() { function b() { var x = 20 function c() { function d() { console.log(x) } d() } c() } b() } 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>闭包 11:33</title> </head> <body> <!-- 闭包closure : 闭合的,封闭的 包围,包裹 闭包: 形容的是函数作用域的一种状态 -- 被保存在其他函数的scopes 里, 绑定 原因: 防止释放 缺点: 费内存, 本来应该释放的函数作用域 无法释放; 永远占据内存 直到被销毁为止 --> <script> // 函数作用域: 是 函数 `运行时` `临时` 生成的作用域 // 临时: 函数执行完毕后, 其作用域会从内存中释放, 来节省内存 function y() { var b = 20 var c = 30 var d = 40 // 理论上: 函数运行完毕后, 就会自动释放 来 节省内存 // 但是: 下方的z函数中使用了 y 函数生成的 b 和 c 变量 // z函数为了确保自身能够正常运行, 把 y函数作用域 保存在自身 // -- 目标: 防止自身依赖的内容 `跑路` // -- 反例: 亮亮办了游泳卡, 结果游泳馆关门了 var z = function () { console.log(b, c); } // log: 输出 美化后的结果, 函数会直接打印函数字符串 // dir: direct直接 输出原始对象, 才能看到函数的本体 console.dir(z) // scopes: 作用域们, 数组类型 // 用于存放函数体中 使用到的 来自其他作用域的变量 所在的作用域 // 为了区分作用域的类型: 有两个标记词 // Global: 代表全局作用域, 即window对象 // Closure: 闭包; 代表函数作用域 // -- 此称呼代表了函数作用域的一种状态 : 被其他函数绑定了 // -- 为了节省内存: 只保留有用的变量 return z } var z = y() //开内存, 生成b和c变量存放在这里, 执行完就销毁 z() // y() //开内存, 生成b和c变量存放在这里, 执行完就销毁 // y() //开内存, 生成b和c变量存放在这里, 执行完就销毁 // y() //开内存, 生成b和c变量存放在这里, 执行完就销毁 // 函数有两种状态: 静态 和 动态 function show() { } //静态: 声明完毕; 买车后停在车库 show() //动态: 触发后; 开车 / // 面试题 var a = 10 function x() { a = 20 } // 函数不调用, 则其中代码不会运行 // x() 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>私有变量 14:01</title> </head> <body> <!-- 私有: 私人拥有, 变量只属于函数自身 --> <script> // 案例: 制作一个变量, 来记录 x 函数调用的次数 // 做法1(错误): 在函数中声明变量x, 导致每次运行函数 x 都会初始 // 做法2(错误): 在函数外声明变量x, 导致同作用域下的其他函数也能修改这个x // 做法3(正确): 把x在独立的匿名函数作用域中声明, 实现私有; 再利用闭包机制把x 绑定给函数使用 // 匿名函数自调: 为了快速生成函数作用域, 局部作用域 var x = (function () { var n = 0 //专属于 匿名函数作用域的 // 函数中的n 属于匿名函数作用域, 所以会捆绑存储在 scopes 属性里 // var x = function(){} // return x return function () { n++ console.log('调用次数:', n); } // console.dir(x) // return x //利用返回值, 暴露给外部 })() // var n = 0 // function x() { // n++ // console.log('调用次数:', n); // } x() x() x() x() // 练习: // 插件 tabout, 用tab键把光标移到括号外 var y = (function () { // 私有变量: 属于匿名函数作用域 var n = 0 // 利用闭包机制, 把匿名函数作用域 捆绑在函数上, 保存在其 scopes 属性里 // 利用return 暴露给外部使用 return function () { n++ console.log(n) } })() y() y() y() y() </script> </body> </html>
八、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 14:42</title> </head> <body> <!-- arguments: 是函数中隐式自带的关键词, 其中存储函数收到的所有实参 --> <script> // uname: 称为形参 -- 应该 见名知意 function show(uname) { // 形参和实参 出现 个数不对应的场景 很常见 console.log(arguments); } // 实参 -- 函数真正收到的值 show('亮亮', 35, '18784556898', true) / // 用途: 制作实参个数不固定的函数 var m = Math.max(12, 3, 4, 6, 7) console.log(m) var m = Math.max(12, 3, 4, 6, 7, 34, 456, 56, 76, 87) console.log(m) var m = Math.max(12, 3, 4, 6, 7, 43, 2) console.log(m) 制作 function max() { console.log(arguments) var x = arguments[0] for (var i = 1; i < arguments.length; i++) { var value = arguments[i] // 语法糖: 作者提供了一些简化语法, 当满足一些固定条件后可以使用 // 简化 -> 懒: 偷懒可以让人幸福!! 像吃糖一样 // 如果if的条件只有一行代码, {} 可以省略 if (value > x) x = value } return x //返回最终结果 } // 编程思维: 把人类解决问题的思维 用 代码方式表达出来, 让计算机执行 console.log(max(12, 33, 54, 65, 765)) console.log(max(12, 33, 54, 65, 765, 4, 345, 456, 756)) </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>函数的重载 15:26</title> </head> <body> <!-- 函数重载: 函数内部 根据 实参的 个数 或 类型 不同, 载入不同的逻辑代码 -- 用于制作 多功能函数, 让一个函数身兼多职 --> <script> // 双11 要做一个计算商品折扣信息的函数 function zheKou(money) { console.log(arguments); // 3个参数 if (arguments.length == 3) { var man = arguments[1] var jian = arguments[2] if (money >= man) money -= jian console.log('满减实付:', money); } if (arguments.length == 2) { if (typeof arguments[1] == 'number') { money *= arguments[1] console.log('折扣实付:', money); } // vip的 if (typeof arguments[1] == 'string') { // if (arguments[1] == 'vip1') money *= 0.9 // if (arguments[1] == 'vip2') money *= 0.8 // if (arguments[1] == 'vip3') money *= 0.7 var list = { vip1: 0.9, vip2: 0.8, vip3: 0.7 } // vip_n 中存放的是属性名 var vip_n = arguments[1] money *= list[vip_n] //通过属性名, 找到要乘的数字 console.log('vip:', money); } } } // 使用有多种用法 zheKou(5000, 0.7) // 7折优惠 zheKou(5000, 0.5) // 5折 zheKou(5000, 3000, 600) //满3000 减600 zheKou(5000, 4000, 800) //满4000 减800 zheKou(5000, 'vip1') // 9折 zheKou(5000, 'vip2') // 8折 zheKou(5000, 'vip3') // 7折 </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>属性 15:51</title> </head> <body> <script> // 对象的属性名语法分两种: 点语法 方括号语法 var emp = { uname: "亮亮", age: 19 } // 点语法适合直接书写属性名 console.log(emp.uname, emp.age) emp.phone = '10086' console.log(emp); // 方括号语法: 适合值是变量的场景 -- 在封装函数时才常见 var x = 'ename', y = 'age' var emp1 = { // 当编辑器看到[] 就会识别其中的代码是 JS代码 [x]: '泡泡', [y]: '18', [2 + 6]: '999', ['ni' + '好']: "哈哈" } console.log(emp1); var z = 'ni好' console.log(emp1[z]); console.log(emp1.z)// 属性名是 z 的属性 </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>练习 16:23</title> </head> <body> <!-- 函数重载: 就是利用实参的 个数 或 类型 不同, 执行不同的逻辑操作 -- if 结合 arguments 进行判断 -- 实现多功能函数 --> <script> // 累加案例 var x = 0 for (var i = 1; i <= 100; i++) { x += i } function sum() { var x = 0 if (arguments.length == 1) { for (var i = 1; i <= arguments[0]; i++) { x += i } } if (arguments.length == 2) { for (var i = arguments[0]; i <= arguments[1]; i++) { x += i } } if (arguments.length == 3) { for (var i = arguments[0]; i <= arguments[1]; i += arguments[2]) { x += i } } console.log(x) } // 目标效果: 制作一个函数, 能够得到 数字累加的和 sum(100) // 得到 1 + 2 + 3 + .... + 100 sum(200) // 1 + .. + 200 sum(50, 200) // 得到 50 + 51 + ... + 200 sum(11, 44) // 11 + .. + 44 sum(100, 200, 10) //得到 100 + 110 + 120 + ... + 200 </script> </body> </html>
十二、this
- 方式1: 函数是固定的, 值作为实参 依次传入到函数中, 执行
- 方式2:
上门服务
--函数 直接进入到 值的内部执行
<!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:50</title> </head> <body> <!-- this: 这个 -- 到底代表什么, 要看语境 函数拥有此关键词以后, 就提供了另一种用法 -- 把函数放到对象里执行 --> <script> // 保存了矩形的宽和高 var r1 = { width: 100, height: 50 } var r2 = { width: 1, height: 4 } function area2() { // this: 这个 -- 代表函数运行时所在的对象 console.log('this:', this); return this.width * this.height } r2.area2 = area2 var y = r2.area2() console.log(y) // 函数上门服务 r1.area2 = area2 // 存放到r1对象里 var x = r1.area2() // 通过 r1 对象来触发 -- 激活 console.log(x) // 算面积 function area(r) { return r.width * r.height } // 把值传入函数中, 进行处理 console.log(area(r1)) </script> </body> </html>
总结
- 声明提升
- JS引擎的
智能化法案
-- 内部有一套规则, 会把不规范的代码
自动修改为规范的
代码, 然后执行(最好的方案: 不要书写不规范的代码 )- 不规范: 表达式中参与运算的值, 类型不正确, 就会
自动类型转换(
什么类型 在什么场景 会 应用哪种转换规则 -- 需要记忆)
- 声明操作
- 规范要求: 必须
先声明, 后使用
- 一旦你的代码不规范, 则会触发
隐形的声明提升操作
, 把 所有声明操作 提升到 代码的顶部- 宿主环境: JS运行在什么环境下, 就能使用此环境中的api -
技能
- node.js: 处理服务器相关的api
- 浏览器: 处理浏览器有关的, 存放在 window 的对象里
- 作用域
- 全局:
- 局部: 函数执行时
临时
生成的- 规避全局污染:
- 利用
匿名函数自调用
生成函数作用域, 在这个作用域中声明变量 就不会污染全局- 闭包:
- 函数中使用来自其他函数中的变量, 为了防止其释放 而导致自身无法使用
- 所以: 保存在自身的 scopes 属性里 -- 绑定
- 这种被
绑架
的函数作用域, 就处于闭包状态
- 私有化:
- 利用闭包特性, 来获取私有的作用域变量
- 作用域链:
- 函数中使用变量, 会从距离最近的作用域中查找使用
- arguments:
- 函数中隐式带有的属性, 保存了所有实参
- 制作实参个数不固定的函数 -- 求最大值
- 函数重载: 多功能函数
- this:
- 这个; 代表函数运行时所在对象
- 此关键词就赋予了函数 进入到 对象中运行的能力