【JavaScript】JS高级语法
变量与函数,作用域,定义变量,作用域链,闭包,预解析,let提升,函数参数,箭头函数,解构赋值,数组解构,对象解构,对象,字面量创建对象,构造函数,自定义构造函数,静态成员和实例成员,数据类型,引用类型,Object,Array,RegExp,包装类型,String,Number,Boolean,原型对象,原型链,this指向,普通函数,严格模式,箭头函数,改变函数的this指向,call,app
目录
1.变量与函数
1.1作用域
作用域:规定了变量使用的范围,离开了这个范围变量就不能再被访问
分类:全局作用域和局部作用域 ===> 变量可以分为全局变量和局部变量
局部作用域:
- 函数作用域:函数内部声明的变量,只能在函数内部被访问,外部无法直接访问
- 块级作用域:JS中使用{ }包裹的代码称为代码块,代码块内部声明的变量外部有可能无法访问
局部变量:在局部作用域中声明的变量,只能在当前函数内部使用
- 函数每次执行都会开辟新的内存空间,当函数执行完毕之后,就会释放这块空间,并且销毁里面的变量(局部变量)
- 形参相当于局部变量
全局作用域:
- 在script标签中局部作用域外的区域
全局变量:在全局作用域中声明的变量,在任何地方都能使用
📖Note:
- 不建议定义大量的全局变量,防止造成变量污染
1.2定义变量
关键字:let,var,const
let和var的区别:
- let声明的变量会产生块作用域,var声明的变量不具有块级作用域
- var可以重新定义声明的变量,let不可以
- var声明的变量相当于window添加属性,let直接就是变量
let和var的同:
- var和let声明的变量都可以只定义不赋值,这个变量就是undefined
const:声明的变量值不能改变,称为常量,const声明的常量必须初始化赋值,其他用法和let相同
📖Note:
- 常量名通常大写
- 一般固定不变的值使用const定义为常量
1.3作用域链
作用域链:底层的变量查找机制,当访问一个变量时,先在当前作用域内查找是否有这个变量的声明,没有找到则去上级作用域查找,依次查找父级作用域直到全局作用域,这个查找的过程会形成一个链状结构
📖Note:
- 子作用域可以访问父作用域,父作用域不可以访问子作用域
1.4闭包
闭包:一种比较特殊的函数,使用闭包能够访问函数作用域中的变量,从代码形式上看闭包是一个作为返回值的函数
- 闭包本质仍是函数,只不过是从函数内部返回
- 闭包能够创建外部可访问的隔离作用域,避免全局变量污染,能够使变量的作用范围延伸
- 过度使用闭包可能造成内存泄漏
- 回调函数也能访问函数内部的局部变量
1.5预解析
预解析:在代码执行之前,先要预解析代码,预解析变量和函数
- 变量:带有声明的变量,JS会把声明的变量提升到当前作用域的最前面,只声明不赋值
- 函数:代表函数名的变量,JS会把带有名字的函数提升到当前作用域的最前面,只定义不调用
变量解析:
- 变量在未声明时就被访问会报语法错误
- 变量在声明之前就被访问,变量的值为undefined
- 变量提升出现在相同作用域中
- 实际开发中推荐先声明再访问变量
函数解析:
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域中
1.6let提升
上面代码报错原因:初始化之前不能访问n
let声明的变量存在变量提升,但是let定义的变量没有初始化之前不允许被使用
死区范围内(变量声明赋值之前的区域)不允许访问let定义的变量
1.7函数参数
函数参数
- 声明函数时为形参赋值即为参数的默认值
- 如果形参未定义默认值时,参数的默认值为undefined
- 调用函数时没有传入对应参数时,参数的默认值会被当成实参传入
动态参数:
arguments:函数内部内置的伪数组变量,包含函数调用时传入的所有实参
📖Note:
- 参数固定写形参,不固定使用arguements
剩余参数
- 剩余值写法:...
- ...是语法符号,置于函数最末形参前,用于获取多余的实参
- 借助...获取剩余的实参
1.8箭头函数
语法:
- let fn = (形参列表) => {函数体}
调用:
- fn(实参列表)
📖Note:
- 如果参数只要一个,那么可以省略小括号
- 如果函数体只有一行,可以省略大括号,大括号省略会自动返回结果
箭头函数的应用:回调函数
箭头函数使用注意点
- 箭头函数使基于函数表达式延伸出来的,不存在预解析,必须先定义再调用
- 箭头函数中,不存在arguements对象,动态参数使用剩余参数接收
- 箭头函数中不存在this,箭头函数中的this指向上级作用域的this
在对象的fun方法中,this本来应该指向事件源window,但是回调函数是箭头函数,箭头函数中的this指向了函数的调用者obj
2.解构赋值
解构赋值:一种快速为变量赋值的简洁语法,本质上仍是为变量赋值,
分类:数组解构,对象解构
3.1数组解构
数组解构:将数组的单元值快速批量赋值给一系列变量的简洁语法
非一一对应的情况:
1.变量多,值少
2.变量少,值多
3.按需取值
4.剩余值法
5.复杂情况
3.2对象解构
对象解构:把属性名当作变量名
命名冲突:修改属性名字
对象嵌套对象
3.对象
对象:类中某个具体的实例,属性和方法的集合体
4.1字面量创建对象
4.2构造函数
构造函数:专门用于创建对象的函数,如果一个函数使用new关键字,那么这个函数就是构造函数
4.2.1自定义构造函数
构造函数中的this指向实例化对象
instanceof: 判断一个对象是否是另外一个构造函数的实例化对象
语法:对象 instanceof 构造函数
constructor :指回构造函数本身
4.2.2静态成员和实例成员
静态成员(静态属性/方法):把在构造函数身上直接添加的成员
- 静态成员只能由构造函数访问
实例成员:构造函数内部为实例化对象准备的成员
- 实例成员只能由实例化对象访问
4.数据类型
JS中主要的数据类型:字符串,数值,布尔,undefined,null,对象
字符串,数值,布尔,undefined,null称为简单类型或基础类型;对象也称为引用类型,常见的对象类型包括数组和普通对象
JS中内置了一些构造函数,用于创建对应类型的对象
数据传递:
- 值传递:会把数据复制一份传递(简单类型)
- 引用传递:会把数据地址复制一份传递(引用类型)
4.1引用类型
4.1.1Object
Object是内置的构造函数,用于创建普通对象
常见实例方法:
- assign()方法:静态方法创建新的对象
- keys()方法:静态方法获取对象的所有键 以数组形式返回
- value()方法:获取对象中所有的属性值
4.1.2Array
Array是内置的构造函数,用于创建数组对象
数组实际上是new Array的实例化对象
常见实例方法:
- forEach():遍历数组,替代for循环
- filter():过滤数组单元值,生成新数组
- map():迭代原数组,生成新数组
- join():数组单元素拼接成字符串
- concat():合并两个数组,生成新数组
- sort():对原数组单元值排序
- splice():删除或替换原数组单元
- indexOf():检索数组单元值
- reverse():反转数组
- from():伪数组转成数组
indexOf():查找某个元素在数组中首次出现的索引位置,找不到返回-1
lastindexOf():查找某个元素在数组中尾次出现的索引位置,找不到返回-1
静态方法:
数组的使用:
4.1.3RegExp
创建正则对象
4.2包装类型
JS中,字符串,数值,布尔具有对象的使用特征,如属性和方法
字符串,数值,布尔类型数据是JS底层使用Object构造函数‘包装’来的,被称为包装类型
4.2.1String
内置的构造函数。用于创建字符串
区别:
- subString()自动识别索引的大小
相同:
- 如果只有一个参数,从这个索引位置截取直到字符串结束
4.2.2Number
内置的构造函数,用于创建数字类型
4.2.3Boolean
JS中一切皆对象
引用类型和包装类型都包含两个公共的方法
- toString:以字符串形式表示对象
- valueOf:获取原始值,很少主动调用该方法
📖Note:
- null和undefined除外
5.封装
构造函数
- 构造函数体现了面向对象的封装特性
- 构造函数实例创建的对象彼此独立,互不影响
- 命名空间式的封装无法保证数据的独立性
5.1 原型对象
原型对象:每一个构造函数都有一个名为prototype的属性,prototype指向一个对象数据类型,称为构造函数的原型对象,每个原型对象都具有constructor属性代表该原型对象对应的构造函数
作用:共享方法,节省内存
当实例对象找不到成员时,就会在原型对象中查找成员
实例化对象可以使用原型对象中的方法
6.继承
继承是面向对象的另一个特征,通过继承可以进一步提升代码的封装程度,JS中大多是借助原型对象实现继承特性
继承之后prototype的属性constructor属性被覆盖
添加constructor属性
问题:Teacher.prototype = obj是引用传值,Teacher.prototype和Student.prototype共用同一个obj的空间,Teacher.prototype.constructor = Teacher的修改也会导致Student的原型对象中的constructor属性指向Teacher
5.2原型链
原型链:由原型对象构成的一个链状结构
作用:提供了成员查找的机制
数组是Array的实例对象,构造函数Array()也有原型对象
<script>
let str = '15375635243273819198387443534234945342648'
// 用对象保存字符及字符出现次数
let obj = {
}
// 统计次数
// 如果对象中有这个键(字符),给值加1
// 没有,则添加键,并给值加一
for(let i = 0; i < str.length; i++) {
// 在obj中查找是否有str[i]属性
// 注意:不能使用.访问 obj.str[i] 是访问obj中确实存在的属性
// 存在str[i]属性 返回属性值 隐式类型转换为布尔值
if(obj[str[i]]) {
obj[str[i]]++
} else {
obj[str[i]] = 1
}
}
// 求出现最多次数的字符
let max = 1
let count = obj['1']
for(let key in obj) {
if(count < obj[key]) {
count = obj[key]
max = key
}
}
console.log(max, count)
</script>
7.this指向
7.1普通函数
普通函数的调用方式决定了this的值,谁调用函数this指向谁
构造函数 :this指向实例化对象
方法:this指向调用者
事件处理函数:this指向事件源
定时器:this指向调用者
自调用函数(立即调用函数):this指向调用者
普通函数没有明确调用者时this的值为window,严格模式下没有调用者时this的值为undefined
7.2严格模式
语法:"use strict"
放在作用域的开头,才能生效
放到全局作用域的开头,代表全局都是严格模式
严格模式下:
- 变量必须先定义再使用
- 普通函数中的this指向undefined
- 函数的形参不能重名
7.3箭头函数
箭头函数中的this不受调用方式的影响,实际上,箭头函数中并不存在this,箭头函数中访问的this是箭头函数所在作用域的this变量
📖Note:
- 事件处理函数中一般不使用箭头函数,this的指向不明确
7.3改变函数的this指向
7.3.1call
call方法调用函数,同时指定函数中的this值
语法:函数.call(this, arg1, arg2......)
📖Note:
- 使用call方法调用函数时,第一个参数为this指定的值
- call方法的其余参数会依次自动传入函数作为函数的参数
7.3.2apply
语法:函数.apply(this, [ ])
第二个参数是一个数组,是传入的参数
7.3.3bind
语法:函数.bind(this, arg1, arg2......)
bind方法不会调用函数,而是创建了一个指定了this值的新函数
8.class关键字(类)
8.1封装
class是ES6中新增的关键字,专门用于创建类,类可被用于实现逻辑的封装
constructor(构造函数,构造方法,构造器):创建类时在类的内部有一个特定的方法constructor,该方法会在类被实例化时自动被调用,常用于处理一些初始化操作
8.2继承
子类有自己的成员时,继承过程中,子类中constructor必须调用super函数
类的本质:构造函数
- ES6之前,使用构造函数创建对象
- ES6之后,使用关键字class创建对象
9.拷贝
9.1浅拷贝
复杂对象类型 ===> 简单数据类型
Object.assign(obj1, obj2)
浅拷贝
9.2深拷贝
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)