作用域规定了变量能够被访问的范围,离开这个范围变量就不能被访问
作用域分为:
局部作用域分为函数作用域和块作用域。
函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法访问
总结:
块作用域:
在JavaScript中使用
{}
包裹的代码块内部声明的变量外部将有可能无法被访问
for(let i=0;i<10;i++){
// i 只能的该代码块中被访问
console.log(i)
}
// 超出了t的作用域 ,报错
console.log(i)
总结:
let
声明的变量,const
声明的常量,都会产生块级作用域var
声明的变量不会产生块级作用域let
和 const
在
<script>
标签 和.js
文件的最外层就是全局作用域,在此声明的变量在函数内部也可以访问,全局作用域下声明的变量,任何其他作用域都可以被访问
//全局作用域,下声明变量num
const num=10
function fn(){
//函数内部可以使用
console.log(num)
}
注意:
作用域链本质是底层的变量查找机制
//全局作用域
let a = 1
let b = 2
//局部作用域
function f() {
let a = 1
function g() {
a = 2
console.log(a) // 2
}
g()
}
f()
总结:
垃圾回收机制 (Garbage Collection) 简称 GC JS 中的内存的分配和回收都是自动完成的,,内存在不使用的时候会被垃圾回收器自动回收。 但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况 不再用到的内存,没有及时释放,就叫做内存泄漏
内存的生命周期 :
垃圾回收算法说明
所谓垃圾回收, 核心思想就是如何判断内存是否已经不再会被使用了, 如果是, 就视为垃圾, 释放掉 下面介绍两种常见的浏览器垃圾回收算法: 引用计数法 和 标记清除法
引用计数 IE采用的引用计数算法, 定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。 算法:
引用计数算法是个简单有效的算法。 但它却存在一个致命的问题:嵌套引用。 如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
标记清除法 现代的浏览器已经不再使用引用计数算法了。 现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。 核心:
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域 简单理解:闭包 = 内层函数 + 外层函数的变量
function outer() {
const a = 1
function f(){
console.log(a)
}
f()
}
outer
// 内层函数调用外层函数的变量,就是闭包
作用: 封闭数据,提供操作,外部也可以访问函数内部的变量
基本格式:
闭包应用:实现数据的私有 比如,我们要做个统计函数调用次数,函数调用一次,就++
普通写法
let count = 1
function fn() {
count++
console.log(count)
}
fn() //2
fn() //3
这个count 是个全局变量,很容易被修改
闭包写法:
function fn() {
let count = 1
return function fun() {
count++
console.log(count)
}
}
const result =fn()
result() //2
result() //3
实现了数据私有,无法直接修改count
闭包可能引起的问题:内存泄漏
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
console.log(str) //不报错 控制台显示undefined
var str = 'hello'
注意:
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
//求和函数 不管用户传入几个实参,都要把和求出来
function sum() {
let s = 0
for(let i = 0;i < arguments.length;i++){
s += arguments[i]
}
return s
}
sum(5,10) //15
sum(1,2,3) //6
剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
...
是语法符号,置于最末函数形参之前,用于获取多余的实参...
获取的剩余实参,是个真数组function sum(...other) {
let s = 0
for(let i = 0;i < other.length;i++){
s += other[i]
}
return s
}
sum(5,10) //15
sum(1,2,3) //6
区别:
展开运算符:...
展开运算符 (…), 将一个数组进行展开,不会修改原数组
const arr=[1,2,3,4,5]
console.log(...arr) //1 2 3 4 5
典型运用场景: 求数组最大值(最小值)、合并数组等
const arr=[1,2,3,6,4,5]
const arr2=[7,8,9]
//最大值
const max=Math.max(...arr)
//合拼
const arr3=[...arr,...arr2]
展开运算符 or 剩余参数 剩余参数:函数参数使用,得到真数组 展开运算符:数组中使用,数组展开
语法:
//普通函数
const fn = function (){
...
}
fn()
//1.箭头 无参数
const fn = () =>{
...
}
//2.只有一个参数
const fn = x=> {
return x
}
//3.函数体只有一行代码,可以写一行上,无需写 return 直接返回值
const fn = (x,y) => x + y //返回 x + y 的值
//加括号的函数体返回对象字面量表达式
const fn1 = uname =>({uname : uname})
console.log(fn1('叶湘伦')) //控制台:{uname:"叶湘伦"}
箭头函数参数:
arguments
动态参数arguments
动态参数,但是有 剩余参数 ..args
箭头函数 this: 在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的 this 值 箭头函数不会创建自己的 this ,它只会从自己的作用域链的上一层沿用 this 。
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此,DOM事件回调函数为了简便,还是不太推荐使用箭头函数
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
// 普通的数组
let arr = [1, 2, 3];
// 批量声明变量 a b c
// 同时将数组单元值 1 2 3 依次赋值给变量 a b c
let [a, b, c] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
基本语法:
=
左侧的 []
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量undefined
...
获取剩余单元值,但只能置于最末位undefined
时默认值才会生效对象解构
// 普通对象
const user = {
name: '小明',
age: 18
};
// 批量声明变量 name age
// 同时将数组单元值 小明 18 依次赋值给变量 name age
const {name, age} = user
console.log(name) // 小明
console.log(age) // 18
总结:
=
左侧的 {}
用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量undefined
undefined
时默认值才会生效forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
遍历数组.forEach(function (当前数组元素,索引号){
//函数体
})
注意:
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组
遍历数组.filter(function (当前数组元素,索引号){
return 筛选条件
})
//筛选数组大于30的元素
const arr=[10,26,62,61,56,12,36]
const re = arr.filter(function(item){
return item > 30
})
console.log(re) //[62,61,56,36]
返回值:返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组 参数:currentValue 必须写, index 可选 因为返回新数组,所以不会影响原数组
利用字面量创建
const obj = {
name:'叶湘伦',
age:18
}
利用 new Object 创建对象
const obj = new Object({
name:'叶湘伦',
age:18
})
利用构造函数创建
// 构造函数
function Obj(name,age){
this.name = name
this.age = age
}
//创建一个对象
const Stu = new Obj('叶湘伦',18)
注意:
说明:
实列化执行过程:
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员
// 构造函数
function Obj(name,age){
//构造函数内部的 this 就是实例对象
//实例对象中动态添加属性
this.name = name
this.age = age
//实例对象中动态添加方法
this.sayHi = function () {
console.log('hi')
}
}
//实例化 ,Stu 是实列对象
// Stu 实际就是构造函数内部的 this
const Stu = new Obj('叶湘伦',18)
//访问实例属性
console.log(Stu.name)
//调用实列方法
Stu.sayHi()
说明:
静态成员:
在 JavaScript 中底层函数本质上也是对象类型,因此允许直接为函数动态添加属性或方法,构造函数的属性和方法被称为静态成员。
// 构造函数
function Obj(name,age){
//实例成员
}
//静态属性
Obj.eyes = 2
Obj.arms = 2
//静态方法
Obj.walk = funtion () {
console.log('走路中')
//this 指向 person
console.log(this.eyes)
}
总结:
this
指向构造函数本身在 JavaScript 中最主要的数据类型有 6 种,分别是字符串、数值、布尔、undefined、null 和 对象,常见的对象类型数据包括数组和普通对象。其中字符串、数值、布尔、undefined、null 也被称为简单类型或基础类型,对象也被称为引用类型。
在 JavaScript 内置了一些构造函数,绝大部的数据处理都是基于这些构造函数实现的,JavaScript 基础阶段学习的 Date
就是内置的构造函数。
引用类型:
Object
:
三个常用静态方法(静态方法就是只有构造函数Object可以调用的)
Object.keys
静态方法获取对象中所有属性(键)
Object.values
静态方法获取对象中所有属性值
Object.assign
静态方法常用于对象拷贝
//Object.keys
const obj = {name:'叶湘伦',age:18}
const arr = Object.key(obj)
console.log(arr) // ['name','age'] 返回是一个数组
//Object.values
const arr2 =Object.values(obj)
console.log(arr2) // ['叶湘伦',18] 返回是一个数组
//Object.assign
const obj2 = {}
Object.assign(obj2,obj)
console.log(obj2) // {name:'叶湘伦',age:18}
//使用:经常使用的场景给对象添加属性
Object.assign(obj,{sex:'男'})
console.log(arr) // {name:'叶湘伦',age:18,sex:'男'}
Array
:
数组常见实例方法-核心方法
方法 | 作用 | 说明 |
---|---|---|
forEach | 遍历数组 | 不返回值,用于不改变值,经常用于查找打印输出值 |
filter | 过滤数组 | 筛选数组元素,并生成新数组 |
map | 迭代数组 | 返回新数组,新数组里面的元素是处理之后的值,经常用于处理数据 |
reduce | 累积器 | 返回函数累计处理的结果,经常用于求和等 |
总结:
Array
构造函数forEach
用于遍历数组,替代 for
循环 (重点)filter
过滤数组单元值,生成新数组(重点)map
迭代原数组,生成新数组(重点)join
数组元素拼接为字符串,返回字符串(重点)find
查找元素, 返回符合测试条件的第一个数组元素值,如果没有符合条件的则返回 undefined(重点)every
检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回 true,否则返回 false(重点)some
检测数组中的元素是否满足指定条件 如果数组中有元素满足条件返回 true,否则返回 falseconcat
合并两个数组,返回生成新数组sort
对原数组单元值排序splice
删除或替换原数组单元reverse
反转数组findIndex
查找元素的索引值String
:
总结:
length
用来获取字符串的度长(重点)split('分隔符')
用来将字符串拆分成数组(重点)substring(需要截取的第一个字符的索引[,结束的索引号])
用于字符串截取(重点)startsWith(检测字符串[, 检测位置索引号])
检测是否以某字符开头(重点)includes(搜索的字符串[, 检测位置索引号])
判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false(重点)toUpperCase
用于将字母转换成大写toLowerCase
用于将就转换成小写indexOf
检测是否包含某字符endsWith
检测是否以某字符结尾replace
用于替换字符串,支持正则匹配match
用于查找字符串,支持正则匹配注:String 也可以当做普通函数使用,这时它的作用是强制转换成字符串数据类型。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象
作用:
constructor 属性: 指向该原型对象的构造函数
对象原型:
对象都会有一个属性 __proto__
指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__
原型的存在
总结:
prototype
是什么?哪里来的?constructor
属性在哪里?作用干啥的?prototype
原型和对象原型__proto__
里面都有__proto__
属性在哪里?指向谁?prototype
原型继承
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。
//人类
const People = {
head:1,
eyes:2,
leys:2,
say:function () {}
}
//男人
function Man(){
}
//公共属性和方法给原型
Man.prototype = People
//如果我们给男人添加了一个吸烟的方法,发现女人自动也添加这个方法
//男人和女人都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
Man.prototype.smoking = function () {}
//女人
function Woamn() {
//独有方法
this.body =function () {}
}
// 继承写法完善 ,解决上面问题
//男人和女人不要使用同一个对象,但是不同对象里面包含相同的属性和方法
//答案:构造函数
//new 每次都会创建一个新的对象
function People() = {
head:1,
eyes:2,
leys:2,
say:function () {}
}
//男人
function Man(){
}
//公共属性和方法
Man.prototype =new People()
原型链:
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
原型链-查找规则:
__proto__
指向的 prototype
原型对象)Object
的原型对象)Object
为止(null)__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线拷贝的是地址 常见方法:
Object.assgin()
展开运算符 {...obj}
拷贝对象Array.prototype.concat()
或者 [...arr]
总结:
拷贝的是对象,不是地址
常见方法:
总结:
throw
抛出异常信息,程序也会终止执行throw
后面跟的是错误提示信息Error
对象配合 throw
使用,能够设置更详细的错误信息总结:
try...catch
用于捕获错误信息try
代码段中try
代码段中出现错误后,会执行 catch
代码段,并截获到错误信息finally
不管是否有错误,都会执行debugger :类似浏览器调试打断点
普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】
普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined
箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !
call()
方法调用函数,同时指定被调用函数中 this 的值
fn.call(thisArg,arg1,arg2,...)
说明:
thisArg
:在 fn 函数运行时指定的 this
值arg1,arg2
:传递的其他参数apply()
调用函数,同时指定被调用函数中 this 的值
fn.apply(thisArg,[argArray])
说明:
thisArg
:在fn函数运行时指定的 this 值argsArray
:传递的值,必须包含在数组里面apply
主要跟数组有关系,比如使用 Math.max()
求数组的最大值bind()
不会调用函数。但是能改变函数内部this 指向
fn.bind(thisArg,arg1,arg2,...)
说明:
thisArg
:在 fn 函数运行时指定的 this
值arg1,arg2
:传递的其他参数总结:
call
和 apply
会调用函数, 并且改变函数内部 this 指向call
和 apply
传递的参数不一样, call
传递参数 aru1, aru2..
形式 apply
必须数组形式[arg]
bind
不会调用函数, 可以改变函数内部 this 指向节流:
就是指连续触发事件但是在 n 秒中只执行一次函数,比如可以利用节流实现 1s之内 只能触发一次鼠标移动事件
防抖:
指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
使用场景