JSCORE02
目录
前言
一、复习
二、函数在对象中触发方式
2.1.call
2.2.apply
2.3.bind
三、引用类型
四、构造函数
五、new
六、原型理念
七、原型
八、class
九、严格模式
十、ES6
十一、let与const
总结
前言
JSCORE02学习开始
一、复习
- JS引擎具有
自动修正代码
的功能 --隐式完成
- 程序员可能会发现: 你书写的代码 和 实际运行的代码, 效果不一致
- 隐式类型转换:
表达式中参与运算的值 类型错误, 会自动猜测转换类型
- 声明提升:
正规做法 -- 先声明 再使用
- 系统会自动把所有
声明操作
移动/剪切到 作用域的顶部, 然后再执行代码- 宿主环境: JS寄生在不同的环境中, 就拥有相应的能力
- node.js: 拥有对服务器操作的能力
- 浏览器: 拥有对浏览器操作的能力
- API存储在
window
对象里, 称为全局对象
- 作用域
- 全局作用域: 脚本中直接书写的代码
- 局部作用域: 函数中书写的代码
- 函数两种状态: 静态 和 动态
- 静态: 书写的函数代码
- 动态: 利用
()
触发后的函数 --按下了机器的开关
- 全局污染解决
- 用匿名函数自调用: 创建函数作用域, 在这里声明变量 书写代码
- 闭包(状态)
- 函数作用域在 函数运行完毕后, 会自动释放
- 如果其他函数使用了这个函数中的变量, 为了防止其释放 会保存到自身的
scopes
属性里- 而这个被保存起来的无法自动释放的函数作用域, 称为
闭包状态
- 私有变量
var 函数 = (function(){ var 变量 = 值 return function(){ 变量 } })()
- 作用域链
- 多层作用域嵌套的场景下, 函数中使用变量时, 按照
就近原则
从最近的作用域中找到变量使用- arguments
- 函数自带的隐形的变量, 存储了函数收到的所有实参
- 用法1: 实参个数不固定的函数
- 做法2: 函数重载 -- 根据 实参个数 或 实参类型 不同, 执行不同的代码逻辑
- 关键词 this
- 这个, 代表函数运行时所在的
这个
对象 ---灵活
- 赋予了函数 放到 对象中执行的能力
二、函数在对象中触发方式
- call: 临时放到对象里执行, 修改了函数中关键词 this 的指向
- bind: 把 实参 和 运行时所在对象绑定 在函数上, 返回一个新的函数; 后续可以随时触发
- apply: 可以把 数组 打散成 1个1个 实参, 传递给函数使用
2.1.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 17:25</title> </head> <body> <!-- call: 短暂的拜访, 造访 --> <!-- 在万物皆对象的JS中, 函数也是对象类型 --> <script> // 函数对象 拥有一个 call 方法 // call: 作用- 把函数 临时/短暂 的放到对象里, 执行 function show() { // this: 这个 - 这个函数使用时所在的对象 console.log(this.uname + '666!'); } var emp1 = { uname: "凯凯" } show.call(emp1) //show函数 临时放到 emp1 对象里执行 // show函数执行完毕后, 会自动从对象中删除 console.log(emp1); var emp2 = { uname: '泡泡' } show.call(emp2) // show.短暂访问(emp2) var stu1 = { uname: "亮亮" } show.call(stu1) console.dir(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>call 带参数 17:42</title> </head> <body> <script> // 计算: 1个员工 n个月的薪资, 按比例扣税 function total(n, bili) { return this.salary * n * bili } var emp1 = { uname: "凯凯", salary: 20000 } // total 放到 emp1 里, 计算12个月的薪资, 5% 扣税 // 参数1: 执行时所在对象 // 其他参数 按顺序填写实参 var x = total.call(emp1, 12, 0.05) console.log(x) //练习 function add(y, z) { return this.x + y + z } var m = { x: 100 } // 要求: add 放到m对象里, 计算出和 200, 300 相加的和 var aa = add.call(m, 200, 300) console.log(aa) </script> </body> </html>
2.2.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 09:24</title> </head> <body> <!-- apply: 与 call 方法相同, 临时把函数放到对象里执行 --> <script> var m = { x: 10 } var n = { x: 100 } function add(y, z) { return this.x + y + z } // 短暂访问m 携带一些礼物(参数) console.log(add.call(m, 20, 30)) console.log(add.call(n, 200, 300)); // apply: 申请 -- 申请到某个对象中执行 // 申请到 m对象中执行, 携带一些礼物--打包起来的 console.log(add.apply(m, [20, 30])) //实参放数组里传递 console.log(add.apply(n, [200, 300])) // 制作一个 max 的函数, 能求出任意数量实参中的最大值 // 参数: 接收 1个1个数字的参数 function max() { var m = arguments[0] for (var i = 1; i < arguments.length; i++) { var value = arguments[i] if (value > m) m = value } return m } var nums = [12, 3, 43, 56, 7, 78] // 求这个数组中元素的最大值 // apply: 可以把数组 打散成 1个1个的值, 作为实参传递给函数 // 函数.apply(对象, 参数数组) // max函数体中, 没有用到this关键词, --- 说明其不依赖放在对象里 // apply的参数1 必须写一个对象 -- 随便写个就行 // 1: 是number类型的对象, 简短 - 比较推荐 console.log(max.apply(1, nums)) console.log(max(1, 2, 34, 3)) console.log(max(1, 2, 34, 3, 54, 56, 76)) // 计算总和的函数 function sum() { var m = 0 for (var i = 0; i < arguments.length; i++) { m += arguments[i] } return m } var nums = [12, 3, 43, 56, 7, 78] console.log(sum.apply(null, nums)); console.log(sum(12, 3, 435, 445)) </script> </body> </html>
2.3.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 10:16</title> </head> <body> <!-- 事件 --> <!-- 在 元素上进行一些操作时, 会触发相关的事件 --> <!-- onclick : 当点击时触发代码 --> <!-- 耦合性太强: HTML中 掺杂了大量的 JS代码 --> <button onclick="show.call(emp, '泡泡', 18)">Click Me</button> <!-- 触发绑定的函数 --> <button onclick="m()">Click!!</button> <script> var emp = { eid: '11001' } function show(name, age) { alert("Hello" + name + age + this.eid) } // bind: 可以把函数 和 其运行时所在对象 和 实参 绑定在一起, 返回新的函数 var m = show.bind(emp, '凯凯', 32) // bind仅绑定, 不会触发函数的执行 console.dir(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>object 引用类型 10:40</title> </head> <body> <!-- JS的数据类型分两类 - 基础类型: null undefined string number boolean + symbol bigint - 引用类型: object --> <script> var a = 5 var b = a a = 10 console.log(b) // 5 var m = { x: 10 } var n = m m.x = 100 console.log(n.x) </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:55</title> </head> <body> <!-- 构造函数: 功能是 用于创造对象的函数 --> <script> // 制作一些员工对象, 存放 web教研部的 老师 var emps = [ // 除非凑字数: 否则应该出现大量重复粘贴的内容 { ename: "孙凯", alias: "追封少年", age: 32, married: false }, { ename: "铭铭", alias: "小铭", age: 34, married: true }, { ename: "亮亮", alias: "闪亮", age: 39, married: true }, { ename: "泡泡", alias: "永远18", age: 18, married: true }, { ename: "小新", alias: "宅", age: 35, married: true }, { ename: "楠楠", alias: "剁成肉泥", age: 22, married: false }, ] // 作用: 把参数 组合在一起, 形成1个对象 // 这个函数 就可以叫 构造函数 function emp(ename, alias, age, married) { var obj = {} //新建空对象 obj.uname = ename // 添加到对象里 obj.alias = alias obj.age = age obj.married = married return obj //返回 } var emps = [ emp('泡泡', '永远18', 18, true), emp('凯凯', '追封少年', 32, false), emp('亮亮', '闪亮', 38, true), ] console.log(emps) </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 list = [ { gname: "海洋之谜", price: 1290, count: 3 }, { gname: "SK-II神仙水", price: 690, count: 5 }, { gname: "TUF Game 3070", price: 2200, count: 1 }, { gname: "TUF Game 3090", price: 5000, count: 2 }, ] // 制作 Goods 构造函数, 来简化上方代码的书写 // 为了让使用者能够区分 -- 见名知意, 推荐用大驼峰命名法 function Goods(gname, price, count) { var obj = {} obj.gname = gname obj.price = price obj.count = count return obj } var list = [ Goods('海洋之谜', 1299, 3), Goods('SK-II神仙水', 690, 5), Goods('TUF Game 3070', 2200, 1), Goods('TUF Game 3090', 5000, 1), ] console.log(list) </script> </body> </html>
五、new
<!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>new关键词 11:40</title> </head> <body> <!-- 构造函数首字母应该大写, 让用户能够从名称区分作用 --> <script> // 数据通常分两种创建方式: 构造方式 和 字面量 var a = 10 //字面量: 更方便快捷 var aa = new Number(10) // 构造: 书写繁琐, 使用少 // 一些构造函数: String, Boolean, Array, Object console.log(a, aa) console.log(a.toFixed(2)) console.log(aa.toFixed(2)) // 字面量语法: function show(a, b) { console.log(a + b); } show(10, 20) // 构造语法 var show1 = new Function('a', 'b', 'console.log(a + b)') console.log(show1) show1(10, 20) // // new运算符: 专门配合构造函数使用的运算符, 能够简化构造函数的书写 function Goods(gname, price, count) { // 作者提供new运算符, 会自动完成如下代码 // this: 此关键词 被作者大量使用, 不同的语境含义不同 // this 在 new 触发的函数中, 代表当前构造的对象 // var this = {} this.gname = gname this.price = price this.count = count // return this } var list = [ new Goods('海洋之谜1', 1999, 3), new Goods('海洋之谜2', 1999, 3), new Goods('海洋之谜3', 1999, 3), ] console.log(list) </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>new运算符 15:38</title> </head> <body> <!-- new运算符隐式完成的任务 --> <script> function Rect(w, h) { // new运算符会完成3件事 // 1. 把变量this 指向空对象 // this = {} this.w = w this.h = h // 2. 把对象的原型链 链接到 构造函数的原型 // this.__proto__ = Rect.prototype // 3. 返回实例对象 // return this } Rect.prototype.area = function () { } var r1 = new Rect(10, 20) console.log(r1) </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> var list = [ // 矩形对象, { width: 100, height: 200 }, { width: 10, height: 20 }, { width: 20, height: 40 }, { width: 30, height: 40 }, ] // 修改为构造函数方式创建矩形对象 -- Rect function Rect(width, height) { // new 触发的函数, this代表当前创建的对象 -- 实例对象 this.width = width this.height = height // 计算面积的方法 -- 函数在对象中书写, 称为方法 // this.area = function () { // return this.width * this.height // } } console.dir(Rect) //prototype原型属性: 存放共享数据 - 亮亮的媳妇 // 在共享区中, 存放area方法 Rect.prototype.area = function () { return this.width * this.height } var list = [ // new: 触发构造函数, 可以生出新的对象 // 生出来的对象中, 会自动保存 其`母亲`是谁 -- 共享数据 new Rect(100, 200), //0 new Rect(20, 40), //1 new Rect(10, 20), //2 new Rect(22, 44) //3 ] // 构造函数生成的对象, 自带一个 __proto__ 属性 // 这个属性指向 其构造函数的 prototype 属性 // 小亮的妈妈: __proto__ -> 亮亮的妻子prototype console.log(list[0].__proto__ == Rect.prototype); // 查看方式: 必须用核心比较旧的 360安全浏览器才可见 // 其他浏览器都遵循最新的WEB标准, 把 __proto__ 美化后展示 // 原型链机制 // 小亮 会自动 跟他妈妈要东西用 // 对象使用属性时, 如果自己没有, 则会自动到 __proto__ 关联的共享区获取 console.log(list[0].area()) // 同一个构造函数 创建出来的对象中, area方法是否相同?? console.log(list[0].area == list[1].area) console.log(list) // var nums = [12, 4354, 45] // push: 存储在对象的原型链 __proto__ 关联的对象里 console.log(Array.prototype); nums.push(999) console.log(nums) </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>prototype 15:20</title> </head> <body> <!-- 这两个单词是 对同一个对象的称呼, 角色不同 称呼不同 --> <!-- prototype(妻子): 原型 - 构造函数对 共享区的称呼 --> <!-- __proto__(妈妈): 原型链 - 对象 对 共享区的称呼 --> <script> // 立方体 function Cube(x, y, z) { this.x = x this.y = y this.z = z } // 体积 Cube.prototype.volume = function () { return this.x * this.y * this.z } var c1 = new Cube(10, 20, 30) console.log(c1) // 原型链机制: 如果在自身找不到 目标属性 // 自动到 __proto__ 中查找 console.log(c1.volume()); console.log(c1.house) // 实在找不到, 就undefined var nums = [12, 32, 344] console.log(nums) //查看数组的方法在哪里 var mm = new String('timi') console.log(mm) //查看原型 var nn = new Date() console.dir(nn) //查看原型 </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 14:46</title> </head> <body> <!-- 原型机制是非常重要的理念, 必须掌握才能完成构造函数的书写 但是: 这套语法过于复杂 在 ES6 JS的第六个版本, 2015年6月 -- 借鉴广受好评的 JAVA 中的 class语法 制作了一个语法糖 --> <script> // Java的构造函数语法 // class 称为类 -- 千万别跟 CSS 的 class 搞混, 毫无关系 // 当 JS引擎读取到 class 这个关键词时, 就会自动把他转换成 JS的构造函数 // 程序员书写就变简单了 // 系统会自动把 class 中的所有函数, 存储在 prototype 里 // 有了这个语法之后, 降低了 构造函数制作的门槛 // 就算你不知道原型理论, 也能书写构造函数 // 面试时, 原型理论是必考的 class Rect { // 固定的名称, 称为 构造函数 // 通过 new Rect() 触发 constructor(w, h) { this.w = w this.h = h } // 省略function area() { return this.w * this.h } } //使用 console.dir(Rect) var r1 = new Rect(10, 20) console.log(r1) r1.area() </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> <script> // ES6之前的构造函数写法, 要拆分成两个部分书写 // function Cube(x, y, z) { // this.x = x // this.y = y // this.z = z // } // Cube.prototype.area = function () { } // Cube.prototype.volume = function () { } // Cube.prototype.perimeter = function () { } // ES6 提供的class语法: 把构造函数涉及到的资源存放在一起 // -- 封装的更好 class Rect { constructor(x, y, z) { this.x = x this.y = y this.z = z } // 相关联的方法 area() { } volume() { } perimeter() { } } var r1 = new Rect(10, 20, 30) console.log(r1) </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:30</title> </head> <body> <!-- 严格模式来自于 ES5 也称 ES2009; 2009年发布的 JS的第5个版本 此模式下的JS代码 将具有更多的报错 -- 通过报错强制程序员书写出 健康的代码; 把有可能的风险扼杀在萌芽里 --> <script> // 在脚本开头书写 'use strict' 则下方代码将进入严格模式管理 // 规则1: 变量必须声明过, 才能使用 'use strict' var servername = 'localhost' //服务器名 // 修改 // 由于变量名拼写错误, 如果此变量不存在, 会自动转为对象的新增属性语法 // 则: window.servename = '...' // 导致全局变量污染 servename = 'xin88.top' console.log(window) console.log(servername) </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>严格模式 17:00</title> </head> <body> <script> // 关键词 this 有如下使用场景: // 函数() : 在全局中调用, this是window // 对象.函数() : 这个运行时所在的对象 // new 函数() : this就是构造出来的 实例对象 'use strict' // 严格模式下, 直接调用函数, 其中的this指向undefined // 有效防止了全局变量污染的可能 function Rect(w, h) { // 忘记写new , 会造成this指向window, 导致全局污染 console.log('this', this) this.w = w this.h = h } // 理论上, 看到函数是 大驼峰命名法, 就应该想到用 new 关键词配合使用 // var r1 = new Rect(10, 20) // 忘记写new var r1 = Rect(10, 20) console.log(r1) 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>取消静默失败 17:15</title> </head> <body> <script> // 以后书写JS代码尽量开启严格模式, 有效辅助你写出健康的JS代码 // 严格模式的功能 除了讲解的3点之外, 还有很多其他的 'use strict' // 在JS中, 有一些失败的操作 并不会报错; 导致程序员难以发现问题 var emp = { uname: "亮亮", age: 32 } // freeze: 冻结; 让对象中的属性无法被修改 Object.freeze(emp) // 书写了, 但是没有改掉, 然后不报错 -- 很难定位错误 emp.age = 100 console.log(emp) </script> </body> </html>
十、ES6
<!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>ES6 17:25</title> </head> <body> <!-- ES6:是JS最重大的一个版本升级 -- 在2015年6月 -- 在此版本中, 融入了大量的先进特性 和 语法糖, 让JS的编程发生重大变更 采用 ES6语法糖 JS代码 与 之前的代码有特别大的区别 ES6有两种含义 - 特指: 2015年6月出品的 JS版本 - 泛指: 15年6月以后出品的所有版本 文档: https://es6.ruanyifeng.com/ --> </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 17:34</title> </head> <body> <!-- ES6中提供了两种新的变量声明方式: let 和 const 原因: 全局变量污染 用 var 在脚本中声明变量, 会造成全局变量污染 解决办法: 用匿名函数自调用生成 局部作用域 来写代码 --> <script> // 用 let 和 const 声明的变量, JS引擎会自动存放在其他的作用域 // 新的作用域: 脚本作用域 - 用于存放全局的 用户自定义的变量 let aa = 10 const bb = 20 // let: 声明变量 -- 声明完毕后可以修改值 // const: 声明常量 - 声明时必须赋值, 以后不能修改 // 根据场景来选择: // 1. 声明亮亮老师的薪资 salary let salary = 20000 //除了没污染, var 和 let 作用一样 salary = 30000 console.log(salary); // 2. 声明亮亮老师的媳妇 wife // const方式, 更加安全可靠 -- 如果确定值后期不会变化, 应该用const更合适 const wife = '小梅' wife = '小翠' console.log(window); /// function show() { // 在函数中, var 和 let 有什么区别? // 没区别 // var: 在脚本中直接书写, 会污染全局; // 在函数中书写, 就在当前作用域中声明, 不存在全局污染的情况 // let 在函数中书写, 也是在函数作用域中声明 // 只有在脚本中直接书写, 才有区别 var a = 10 let b = 10 } // 由于匿名函数自调用: 书写较为繁琐, 学习成本高 -- 懒 // (function () { // var aa = 10 // })() </script> </body> </html>
总结
- 函数的触发方式
- call: 临时把函数放到对象里触发; 目的是修改其this指向
- bind: 把函数和 运行时所在对象 及 相关参数绑定在一起, 形成1个新的函数; 可以随时触发
- apply: 可以把数组打散成 1个1个 实参, 传递给函数
- 构造函数
- 对象是引用类型: 多个对象都可以指向同一个
共享的内存块
- 构造函数: 用于构建创造 对象的函数
- new关键词: 简化了3行代码
- var this = {}
- this.__proto__ = 构造函数.prototype
- return this
- 原型理论: 必须掌握的JS核心设计理念
- 来自ES6的class语法
- 比原生的 函数方式 更加简单的 构造函数写法
- 严格模式
- 开启之后, 可以辅助你写出更加健康的代码 -- 丰富的报错
- let - const