怎么创建网站相册,怎么查域名注册商,如何将自己做网站放上网,越南做购物网站JavaScript 进阶 - 第1天 学习作用域、变量提升、闭包等语言特征#xff0c;加深对 JavaScript 的理解#xff0c;掌握变量赋值、函数声明的简洁语法#xff0c;降低代码的冗余度。 理解作用域对程序执行的影响能够分析程序执行的作用域范围理解闭包本质#xff0c;利用闭包…
JavaScript 进阶 - 第1天 学习作用域、变量提升、闭包等语言特征加深对 JavaScript 的理解掌握变量赋值、函数声明的简洁语法降低代码的冗余度。 理解作用域对程序执行的影响能够分析程序执行的作用域范围理解闭包本质利用闭包创建隔离作用域了解什么变量提升及函数提升掌握箭头函数、解析剩余参数等简洁语法
作用域 了解作用域对程序执行的影响及作用域链的查找机制使用闭包函数创建隔离作用域避免全局变量污染。 作用域scope规定了变量能够被访问的“范围”离开了这个“范围”变量便不能被访问作用域分为全局作用域和局部作用域。
局部作用域
局部作用域分为函数作用域和块作用域。
函数作用域
在函数内部声明的变量只能在函数内部被访问外部无法直接访问。
script// 声明 counter 函数function counter(x, y) {// 函数内部声明的变量const s x yconsole.log(s) // 18}// 设用 counter 函数counter(10, 8)// 访问变量 sconsole.log(s)// 报错
/script总结
函数内部声明的变量在函数外部无法被访问函数的参数也是函数内部的局部变量不同函数内部声明的变量无法互相访问函数执行完毕后函数内部的变量实际被清空了
块作用域
在 JavaScript 中使用 {} 包裹的代码称为代码块代码块内部声明的变量外部将【有可能】无法被访问。
script{// age 只能在该代码块中被访问let age 18;console.log(age); // 正常}// 超出了 age 的作用域console.log(age) // 报错let flag true;if(flag) {// str 只能在该代码块中被访问let str hello world!console.log(str); // 正常}// 超出了 age 的作用域console.log(str); // 报错for(let t 1; t 6; t) {// t 只能在该代码块中被访问console.log(t); // 正常}// 超出了 t 的作用域console.log(t); // 报错
/scriptJavaScript 中除了变量外还有常量常量与变量本质的区别是【常量必须要有值且不允许被重新赋值】常量值为对象时其属性和方法允许重新赋值。
script// 必须要有值const version 1.0.0;// 不能重新赋值// version 1.0.1;// 常量值为对象类型const user {name: 小明,age: 18}// 不能重新赋值user {};// 属性和方法允许被修改user.name 小小明;user.gender 男;
/script总结
let 声明的变量会产生块作用域var 不会产生块作用域const 声明的常量也会产生块作用域不同代码块之间的变量无法互相访问推荐使用 let 或 const
注开发中 let 和 const 经常不加区分的使用如果担心某个值会不小被修改时则只能使用 const 声明成常量。
全局作用域
script 标签和 .js 文件的【最外层】就是所谓的全局作用域在此声明的变量在函数内部也可以被访问。
script// 此处是全局function sayHi() {// 此处为局部}// 此处为全局
/script全局作用域中声明的变量任何其它作用域都可以被访问如下代码所示
script// 全局变量 nameconst name 小明// 函数作用域中访问全局function sayHi() {// 此处为局部console.log(你好 name)}// 全局变量 flag 和 xconst flag truelet x 10// 块作用域中访问全局if(flag) {let y 5console.log(x y) // x 是全局的}
/script总结
为 window 对象动态添加的属性默认也是全局的不推荐函数中未使用任何关键字声明的变量为全局变量不推荐尽可能少的声明全局变量防止全局变量被污染
JavaScript 中的作用域是程序被执行时的底层机制了解这一机制有助于规范代码书写习惯避免因作用域导致的语法错误。
作用域链
在解释什么是作用域链前先来看一段代码
script// 全局作用域let a 1let b 2// 局部作用域function f() {let c// 局部作用域function g() {let d yo}}
/script函数内部允许创建新的函数f 函数内部创建的新函数 g会产生新的函数作用域由此可知作用域产生了嵌套的关系。
如下图所示父子关系的作用域关联在一起形成了链状的结构作用域链的名字也由此而来。
作用域链本质上是底层的变量查找机制在函数被执行时会优先查找当前函数作用域中查找变量如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域如下代码所示
script// 全局作用域let a 1let b 2// 局部作用域function f() {let c// let a 10;console.log(a) // 1 或 10console.log(d) // 报错// 局部作用域function g() {let d yo// let b 20;console.log(b) // 2 或 20}// 调用 g 函数g()}console.log(c) // 报错console.log(d) // 报错f();
/script总结
嵌套关系的作用域串联起来形成了作用域链相同作用域链中按着从小到大的规则查找变量子作用域能够访问父作用域父级作用域无法访问子级作用域
闭包
闭包是一种比较特殊和函数使用闭包能够访问函数作用域中的变量。从代码形式上看闭包是一个做为返回值的函数如下代码所示
bodyscript// 1. 闭包 : 内层函数 外层函数变量// function outer() {// const a 1// function f() {// console.log(a)// }// f()// }// outer()// 2. 闭包的应用 实现数据的私有。统计函数的调用次数// let count 1// function fn() {// count// console.log(函数被调用${count}次)// }// 3. 闭包的写法 统计函数的调用次数function outer() {let count 1function fn() {countconsole.log(函数被调用${count}次)}return fn}const re outer()// const re function fn() {// count// console.log(函数被调用${count}次)// }re()re()// const fn function() { } 函数表达式// 4. 闭包存在的问题 可能会造成内存泄漏/script
/body总结
1.怎么理解闭包
闭包 内层函数 外层函数的变量
2.闭包的作用
封闭数据实现数据私有外部也可以访问函数内部的变量闭包很有用因为它允许将函数与其所操作的某些数据环境关联起来
3.闭包可能引起的问题
内存泄漏
变量提升
变量提升是 JavaScript 中比较“奇怪”的现象它允许在变量声明之前即被访问
script// 访问变量 strconsole.log(str world!);// 声明变量 strvar str hello ;
/script总结
变量在未声明即被访问时会报语法错误变量在声明之前即被访问变量的值为 undefinedlet 声明的变量不存在变量提升推荐使用 let变量提升出现在相同作用域当中实际开发中推荐先声明再访问变量
注关于变量提升的原理分析会涉及较为复杂的词法分析等知识而开发中使用 let 可以轻松规避变量的提升因此在此不做过多的探讨有兴趣可查阅资料。
函数 知道函数参数默认值、动态参数、剩余参数的使用细节提升函数应用的灵活度知道箭头函数的语法及与普通函数的差异。 函数提升
函数提升与变量提升比较类似是指函数在声明之前即可被调用。
script// 调用函数foo()// 声明函数function foo() {console.log(声明之前即被调用...)}// 不存在提升现象bar() // 错误var bar function () {console.log(函数表达式不存在提升现象...)}
/script总结
函数提升能够使函数的声明调用更灵活函数表达式不存在提升的现象函数提升出现在相同作用域当中
函数参数
函数参数的使用细节能够提升函数应用的灵活度。
默认值
script// 设置参数默认值function sayHi(name小明, age18) {document.write(p大家好我叫${name}我今年${age}岁了。/p);}// 调用函数sayHi();sayHi(小红);sayHi(小刚, 21);
/script总结
声明函数时为形参赋值即为参数的默认值如果参数未自定义默认值时参数的默认值为 undefined调用函数时没有传入对应实参时参数的默认值被当做实参传入
动态参数
arguments 是函数内部内置的伪数组变量它包含了调用函数时传入的所有实参。
script// 求生函数计算所有参数的和function sum() {// console.log(arguments)let s 0for(let i 0; i arguments.length; i) {s arguments[i]}console.log(s)}// 调用求和函数sum(5, 10)// 两个参数sum(1, 2, 4) // 两个参数
/script总结
arguments 是一个伪数组arguments 的作用是动态获取函数的实参
剩余参数
scriptfunction config(baseURL, ...other) {console.log(baseURL) // 得到 http://baidu.comconsole.log(other) // other 得到 [get, json]}// 调用函数config(http://baidu.com, get, json);
/script总结
... 是语法符号置于最末函数形参之前用于获取多余的实参借助 ... 获取的剩余实参是个真数组
箭头函数
箭头函数是一种声明函数的简洁语法它与普通函数并无本质的区别差异性更多体现在语法格式上。
bodyscript// const fn function () {// console.log(123)// }// 1. 箭头函数 基本语法// const fn () {// console.log(123)// }// fn()// const fn (x) {// console.log(x)// }// fn(1)// 2. 只有一个形参的时候可以省略小括号// const fn x {// console.log(x)// }// fn(1)// // 3. 只有一行代码的时候我们可以省略大括号// const fn x console.log(x)// fn(1)// 4. 只有一行代码的时候可以省略return// const fn x x x// console.log(fn(1))// 5. 箭头函数可以直接返回一个对象// const fn (uname) ({ uname: uname })// console.log(fn(刘德华))/script
/body总结
箭头函数属于表达式函数因此不存在函数提升箭头函数只有一个参数时可以省略圆括号 ()箭头函数函数体只有一行代码时可以省略花括号 {}并自动做为返回值被返回
箭头函数参数
箭头函数中没有 arguments只能使用 ... 动态获取实参
bodyscript// 1. 利用箭头函数来求和const getSum (...arr) {let sum 0for (let i 0; i arr.length; i) {sum arr[i]}return sum}const result getSum(2, 3, 4)console.log(result) // 9/script箭头函数 this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。 script// 以前this的指向 谁调用的这个函数this 就指向谁// console.log(this) // window// // 普通函数// function fn() {// console.log(this) // window// }// window.fn()// // 对象方法里面的this// const obj {// name: andy,// sayHi: function () {// console.log(this) // obj// }// }// obj.sayHi()// 2. 箭头函数的this 是上一层作用域的this 指向// const fn () {// console.log(this) // window// }// fn()// 对象方法箭头函数 this// const obj {// uname: pink老师,// sayHi: () {// console.log(this) // this 指向谁 window// }// }// obj.sayHi()const obj {uname: pink老师,sayHi: function () {console.log(this) // objlet i 10const count () {console.log(this) // obj }count()}}obj.sayHi()/script解构赋值 知道解构的语法及分类使用解构简洁语法快速为变量赋值。 解构赋值是一种快速为变量赋值的简洁语法本质上仍然是为变量赋值分为数组解构、对象解构两大类型。
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法如下代码所示
script// 普通的数组let arr [1, 2, 3]// 批量声明变量 a b c // 同时将数组单元值 1 2 3 依次赋值给变量 a b clet [a, b, c] arrconsole.log(a); // 1console.log(b); // 2console.log(c); // 3
/script总结
赋值运算符 左侧的 [] 用于批量声明变量右侧数组的单元值将被赋值给左侧的变量变量的顺序对应数组单元值的位置依次进行赋值操作变量的数量大于单元值数量时多余的变量将被赋值为 undefined变量的数量小于单元值数量时可以通过 ... 获取剩余单元值但只能置于最末位允许初始化变量的默认值且只有单元值为 undefined 时默认值才会生效
注支持多维解构赋值比较复杂后续有应用需求时再进一步分析
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法如下代码所示
script// 普通对象const user {name: 小明,age: 18};// 批量声明变量 name age// 同时将数组单元值 小明 18 依次赋值给变量 name ageconst {name, age} userconsole.log(name) // 小明console.log(age) // 18
/script总结
赋值运算符 左侧的 {} 用于批量声明变量右侧对象的属性值将被赋值给左侧的变量对象属性的值将被赋值给与属性名相同的变量对象中找不到与变量名一致的属性时变量值为 undefined允许初始化变量的默认值属性不存在或单元值为 undefined 时默认值才会生效
注支持多维解构赋值
bodyscript// 1. 这是后台传递过来的数据const msg {code: 200,msg: 获取新闻列表成功,data: [{id: 1,title: 5G商用自己三大运用商收入下降,count: 58},{id: 2,title: 国际媒体头条速览,count: 56},{id: 3,title: 乌克兰和俄罗斯持续冲突,count: 1669},]}// 需求1 请将以上msg对象 采用对象解构的方式 只选出 data 方面后面使用渲染页面// const { data } msg// console.log(data)// 需求2 上面msg是后台传递过来的数据我们需要把data选出当做参数传递给 函数// const { data } msg// msg 虽然很多属性但是我们利用解构只要 data值function render({ data }) {// const { data } arr// 我们只要 data 数据// 内部处理console.log(data)}render(msg)// 需求3 为了防止msg里面的data名字混淆要求渲染函数里面的数据名改为 myDatafunction render({ data: myData }) {// 要求将 获取过来的 data数据 更名为 myData// 内部处理console.log(myData)}render(msg)/script综合案例
forEach遍历数组
forEach() 方法用于调用数组的每个元素并将元素传递给回调函数 注意 1.forEach 主要是遍历数组 2.参数当前数组元素是必须要写的 索引号可选。 bodyscript// forEach 就是遍历 加强版的for循环 适合于遍历数组对象const arr [red, green, pink]const result arr.forEach(function (item, index) {console.log(item) // 数组元素 red green pinkconsole.log(index) // 索引号})// console.log(result)/script
/bodyfilter筛选数组
filter() 方法创建一个新的数组新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景 筛选数组符合条件的元素并返回筛选之后元素的新数组
bodyscriptconst arr [10, 20, 30]// const newArr arr.filter(function (item, index) {// // console.log(item)// // console.log(index)// return item 20// })// 返回的符合条件的新数组const newArr arr.filter(item item 20)console.log(newArr)/script
/body