JavaScript进阶

JavaScript

内容记录的是JavaScript进阶笔记。

作用域

作用域(scope)规定了变量能够被访问的"范围",离开了这个范围变量便不能被访问,作用域可以堆叠成层次结构,子作用域可以访问父作用域,作用域分为:

  • 局部作用域
  • 全局作用域
  • 模块作用域

1.1 局部作用域

局部作用域分为函数作用域块作用域

函数作用域:

在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。

function sum(num) {
    // 形参num相当于局部变量
    const num = 10
}
sum(1);
console.log(num) // 外部无法使用函数内部变量

总结

  1. 函数内部生命的变量,在函数外部无法被访问
  2. 函数的形参也可以看做是函数内部的局部变量

块级作用域

在JavaScript中使用{}包裹的代码成为代码块,代码块内部声明的变量外部【有可能】无法被访问。

for (let i = 0; i < 10; i++) {
    // i 只能在改代码块中被访问
    console.log(i); // 正常
}
// 超出了 i 的作用域
console.log(i) // 报错

总结

  1. let/const声明会产生块作用域, var不会产生作用域
  2. 不同代码之间声明的变量无法互相访问
  3. 推荐使用let或const

1.2 全局作用域

<script>标签.js文件的【最外层】就是全局作用域,在此声明的变量在函数内部可以被访问。全局作用域中声明的变量,在任何其他作用域都可以访问

<script>
	// 全局作用域
	// 全局作用域下声明了num变量
    const num = 10
    function fn() {
        // 函数内部可以使用全局作用域的变量
        console.log(num);
    }
</script>

总结

  1. window对象动态添加属性默认也是全局的,不推荐使用(其中当某个变量没有被声明变量声明时,该变量是挂载到window身上的
  2. 函数中为适应的任何关键字声明的变量为全局变量,不推荐
  3. 尽可能少的声明全局变量,防止全局变量污染

1.3 作用域链

嵌套关系的作用于串联起来形成了作用域链

作用:作用域链本质上是底层的变量查找机制(就近原则)

  • 在函数被执行时,会优先查找当前函数作用域中查找变量
  • 如果当前作用域查找不到则会逐级向上查找赋值作用域直到全局作用域
// 全局作用域
let a = 11
let b = 22
// 局部作用域
function f() {
    let a = 1
    // 局部作用域
    function g() {
        a = 2
        console.log(a) // 2
    }
   	g() // 调用g
}
f() // 调用f

总结

  1. 嵌套关系的作用域串联起来形成了作用域链
  2. 查找规则:就近原则
    • 当前作用于找不到,则会逐级查找父级作用域直到全局作用域
    • 都找不到则提示错误,这个变量没有被定义过
  3. 子作用域能够访问父作用域,父作用域无法访问子级作用域

1.4 垃圾回收机制

垃圾回收机制(Garbage Collection)简称GC

  • JS中内存的分配和回收都是自动完成的,内存在不适用的时候都会被垃圾回收器自动分配

内存的生命周期

JS环境中分配的内存,一般有如下生命周期:

  1. 内存分配:当我们声明、函数、对象的时候,系统会自动为他们分配内存
  2. 内存使用:即读写内存,也就是使用变量、函数登
  3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存

说明

  • 全局变量一般不会回收(关闭页面回收)
  • 一般情况下局部变量的值,不再使用,会被自动回收

内存泄漏:程序中分配的内存由于某种原因程序未释放无法释放叫做内存泄漏

原理

  • 引用计数法
    • 这是最初级的垃圾收集算法,此算法简化理解为“对象有没有其他对象引用到它”,如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收
let o {
    name: 'eason';
} // 创建一个对象,被o变量所引用,技术为1

let o2 = o // o2 变量是第二个对该对象的引用,计数为2

o = null // 该对象的原始引用 o 已经没有了,技术为1

o2 - null // 此时对象时所有引用都没有了,计数为 0 垃圾回收机制会回收该对象

缺点:循环引用

  • 标记清除法
    • 从2012年起,所有现代浏览器都使用了标记-清楚垃圾回收机制

1.5 闭包

闭包是什么?

闭包就是函数的嵌套,内层函数访问了外层函数的局部变量。

闭包有什么用?

实现变量的私有化,让外面的人无法修改内部的变量。

闭包会产生什么问题或者问题?

  1. 消耗内存,使用不当会造成内容泄露
  2. 本该被释放的变量,无法得到及时释放,会存在闭包空间中
function outer() {
    let name = 'eason';
    
    function inside() {
        return name;
    }
    
    return inside();
}

总结

  1. 用哪个关键字声明变量会有变量提升?var
  2. 变量提升是什么流程?
    • 先把var变量提升到当前作用域最前面
    • 只提升变量声明,不提升变量赋值
    • 然后依次执行代码

我们不建议使用var声明变量

函数进阶

1. 函数提升

函数提升与变量提升比较类似

// 调用函数
foo()
// 声明函数
function foo() {
    console.log('声明之前被调用...');
}
// 不存在提升变量
bar()

var bar = function(){
    console.log('函数表达式不存在提升现象...');
}

说明:

  1. 函数提升提升到当前作用于最前面
  2. 函数提升之提升声明,不提升调用
  3. 函数表达式不存在提升的现象
  4. 函数提升能够使用函数的声明调用更灵活

arguments

arguments就是一个伪数组,仅存在于function中,它的作用是装载着所有的实参,因为arguments是动态伪数组,数据变化带来的性能消耗较大,每次都会同步更新所有的参数,如果参数过多则会出现问题,在开启严格模式后就会彻底禁用。

现在开发中基本不用arguments了,可以使用可变参数完成代替

function fn() {
    console.log(arguments);
}

fn(1,2,3,4,5,6)
/*
Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]
0: 1	1: 2	2: 3	3: 4	4: 5	5: 6
callee: ƒ fn()
length: 6
Symbol(Symbol.iterator): ƒ values()
[[Prototype]]: Object
*/

2. 函数参数

剩余参数:允许我们将一个补丁数量的参数表示为一个数组

简单理解:用于获取多余的参数,并形成一个真数组

function sum(...other) {
    // 可以得到[1, 2, 3, 4]
    console.log(other)
}
sum(1, 2, 3, 4)

3. 展开运算符

**剩余参数:**函数参数使用,把多个元素收集起来生成一个真数组(凝聚)

**展开运算符:**将数组展开成各个元素(拆散)

const arr = [1, 5, 3, 8, 2];
console.log(...arr)// 1 5 3 8 2

4.箭头函数

  1. 当箭头函数只有一个参数时,可以省略参数的小括号其余个数不能省略(没有参数也需要写小括号)
  2. 当箭头函数的函数体只有一句代码时,可以省略函数体的大括号,这句代码就是返回值(可以不用写return
  3. 如果返回的是个对象,则需要把对象用小括号包裹
  4. 箭头函数里面没有arguments,但是有剩余参数

总结

  1. 箭头函数里面有this吗?
    • 箭头函数本身没有this,它只会沿用上一层作用域的this
  2. 我们如何选择用不用箭头函数呢?
    • 根据需求来选择是否需要
    • dom元素注册事件,需要用this,就不用箭头函数
    • 定时器里面需要用到this,就需要箭头函数

5.结构赋值

**解构赋值:**可以将数组中的或者对象属性取出,赋值给其他变量

**解构:**其实就是把一个事物的结构进行拆解

const obj = {
    name: 'eason',
    age: 21,
    address: '广州'
}

const arr = [1, 2, 3, 4, 5, 6]

const {name, age, address} = obj
const [a, b, c, d, e, f] = arr;

console.log(name, age, address) // 'eason', 21, '广州'
console.log(a, b, c, d, e, f) // 1, 2, 3, 4, 5, 6

6. 解构

**数组解构:**变量和值不匹配的情况

  1. 变量多值少的情况:

    const [a, b, c, d] = ['小米', '苹果', '华为']
    console.log(a); // 小米
    console.log(b); // 苹果
    console.log(c); // 华为
    console.log(d); // undefined
    
  2. 防止 undefined 传值,可以设置默认值

    let [a = '大亚亚', b, c, d = '小亚亚'] = ['龙志豪', '陈厚霖', '张泽文']
    console.log(a);
    console.log(b);
    console.log(c);
    console.log(d);
    
  3. 利用剩余参数解决变量少值多的情况

    let  [a, ...b] = ['龙志豪', '陈厚霖', '张泽文']
    console.log(a);
    console.log(b);
    
  4. 多种数组情况

    // 一维数组
    let arr1 = [1, 2, 3];
    // 二维数组
    let arr2 = [1, 2, 3, [4, 5, 6]]
    let [a, b, c, [d, e, f]] = arr2
    console.log(a);
    console.log(b);
    console.log(c);
    console.log(d);
    console.log(e);
    console.log(f);
    

对象解构

可以从一个对象中提取变量并同时修改新的变量名

格式:变量名:新变量名

// 普通对象
const user = {
    name: '小明',
    age: 18
}
// 把原来的name 变量重命名为 uname
const {name: uname, age} = user
console.log(uname); // 小明
console.log(age) // 18


// 多级对象解构
const pig = {
    name: '佩奇',
    family: {
        mother: '猪妈妈',
        father: '猪爸爸',
        sister: '乔治'
    },
    age: 6
}
let {name, age, family: {mother, father, sisiter}} = pig
console.log(name, age. mother, father, sister);

7. 构造函数

构造函数:一种特殊的函数,专门用于帮助程序员创建对象的

本质:构造函数本质就是普通函数,只有在使用new关键字调用它的时候,就被称为构造函数,定义构造函数和普通函数没有任何区别,只有在调用时才能决定它是否为构造函数、

特点:

  1. 调用构造函数会自动创建一个新对象
  2. 可以给当前创建的新对象添加属性
  3. 默认返回这个新对象,不需要写return
function Person(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
}

const person = new Person("eason", 21, "广州")

new实例化执行过程

  1. 创建新空对象
  2. 构造函数this指向新对象
  3. 执行构造函数代码
  4. 返回新对象

数组进阶

1. filter

  • 语法:array.filter(callback(element, index, array), thisValue);
  • 其中,callback为回调函数,它有三个参数:element表示数组中当前正在遍历的元素,index表示当前元素在数组中的索引,array表示原数组。thisValue为可选的参数,它表示在回调函数中使用的this指向。
  • 不会影响原数组,会返回一个符合判断条件的新数组
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
const newArr = arr.filter((item, index, arr) => {
    if(item >= 3) {
        return true;
    } else {
        return false;
    }
    console.log(index); // 0-7
})

console.log(newArr) "[3,4,5,6,7,8]"

2. reduce

  • 语法:array.reduce(callback(accumulator, currentValue, currentIndex, arr), initialValue)
  • callback为回调函数,它有四个参数:accumulator表示累加器,即上一次迭代后的累加结果;currentValue表示当前正在迭代的数组元素;currentIndex表示当前元素在数组中的索引;array表示原数组。initialValue为可选的参数,它表示累加器的初始值。
  • 应用场景:用于通过对数组元素的累加、累乘、字符串连接等操作,将数组元素转换为单个值。

注意:若第二个参数没有设置,则reduce函数会自动的以数组中的第一个参数为初始值,且只遍历arr.length - 1次。

const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15e

3. ervey

在JavaScript中,every()方法是用于数组的高阶函数之一。他接受一个回调函数作为参数,并对数组的每个元素调用该方法,返回一个布尔值。如果该数组的每个元素对应的对调函数都返回true,则every()方法返回true,否则返回false

例子:

const arr = [6, 7, 8, 9];
const result = arr.every(item => item > 5);
console.log(result); // true

4. some

在JavaScript中,some()方法也是数组的高阶函数之一,他接受一个回调函数作为参数,并对数组的每个元素调用该函数,返回一个布尔值。如果该数组的任一元素的对应的回调函数返回true,则some()方法返回true,否则返回false

const arr = [5, 9, 12, 15];
consta result = arr.some(item => item > 10);
console.log(result); // true

5. findIdenx

在JavaScript中,findIndex()方法是用于数组的高阶函数之一,他接受一个回调函数作为参数,并对数组的每个元素调用该函数,如果该函数返回true,则findIndex()方法返回该元素在数组中的索引,否则返回-1

const arr = [5, 9, 12, 15];
const result = arr.findIndex(item => item > 10);
console.log(index) // 2

构造函数

1. 编程思想

  • 面向过程编程

    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个一个的一次调用就可以了。

  • 面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。

2. 构造函数

  • 封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装
  • 把公共的属性和方法抽取封装到构造函数里面来实现数据的共享,这样创建的实例对象可以使用这些属性和方法了

构造函数:一种特殊的函数,专门用于帮助程序员创建对象的

本质:构造函数本质就是普通函数,只有在使用new关键字调用它的时候,就被称为构造函数,定义构造函数和普通函数没有任何区别,只有在调用时才能决定它是否为构造函数、

特点:

  1. 调用构造函数会自动创建一个新对象
  2. 可以给当前创建的新对象添加属性
  3. 默认返回这个新对象,不需要写return
function Person(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
}

const person = new Person("eason", 21, "广州")

new实例化执行过程

  1. 创建新空对象
  2. 构造函数this指向新对象
  3. 执行构造函数代码
  4. 返回新对象

3. 原型对象

问题引出:

前面我们学过的构造函数方法很好用,但是存在浪费内存的问题

// 构造函数
function Person(name, age) {
 this.name = name;
 this.age = age;
 this.sayHi = function() {
     console.log("hi!!");
 }
}

// 实例对象使用属性和方法
const person1 = new Person("1", 21);
const person2 = new Person("2", 21);

原因:sayHi是作为Person的一个构造参数,每次使用new一个实例对象都会在堆内存中开辟一个空间用来存储。

那我们是否有方法让它只创建一个呢?

实例对象可以直接访问原型对象中函数

执行过程:先找实例对象属性或者函数,找不到会再找原型对象中属性或函数(有点像作用域查找机制

  1. 原型对象是什么?
  • prototype,每个构造函数都有原型对象
  1. 原型对象的作用是什么?
  • 可以把公共的属性和方法,直接定义在prototype对象上
  • 实例对象可直接访问原型对象中属性和方法
  • 这些属性和方法不会多次在内存中创建,从而节约内存

4. this指向

构造函数和原型对象中的this都指向实例化对象

// 构造函数this指向、实例对象
function Person(name, age) {
 this.name = name;
}

// 原型对象this指向 实例对象
Person.prototype.sayHi = function () {
 console.log("hi~");
 console.log(this); // 指向实例对象 person
}

const person = new Person("1", 21)
person.sayHi();

5. 原型

对象都会有一个属性__proto__指向构造函数的prototype原型对象

之所以我们对象可以使用构造函数prototype原型对象的方法,就是因为对象有__proto__原型的存在

  1. prototype是什么?哪里来的?
    • 原型对象
    • 构造函数都自动有原型对象
  2. __proto__属性是什么?在哪里?指向谁?
    • 原型
    • 在实例对象中
    • 指向prototype原型对象,这样实例对象就可以访问原型对象里面的方法

6. 原型链

__proto__属性链状结构成为原型链

**作用:**原型链为对象成员查找机制提供一个方向,或者说一条路线

查找规则

  1. 当访问一个对象成员(属性/方法)时,首先查找这个对象自身有没有该成员(属性/方法)
  2. 如果没有就查找它的原型对象(也就是__proto__指向的prototype原型对象
  3. 如果还没有就查找原型对象的原型对象(Object的原型对象)
  4. 以此类推一直找到Object为止(null)
  5. 原型链就在于为对象成员查找机制提供一个方向,或者说一条路线

7. instanceof

语法:

实例对象instanceof构造函数

作用:

用来检测构造函数.prototype是否存在于实例对象原型链

8. 原型继承(组合式继承)

继承是面向对象编程的另一个特征。龙生龙、凤生凤、老鼠的儿子会打动描述的正是继承的含义,有些公共的属性和方法可以写到父级身上,子级通过继承也可以使用这些属性和方法。JavaScript中大多数是借助原型对象实现继承的特征

function Person() {
 this.eyes = 2;

}

Person.prototype.eat = function() {
 console.log("我可以吃饭...");
}

// 继承
function Man() {

}
Man.prototype = new Person();
Man.prototype.constructor = Man;

const m1 = new Man();

原型继承总结:

  1. 创建父级构造函数
  2. 将所有公共的方法放到父级的原型对象上
  3. 将子级构造函数的原型对象 指向 父级构造函 创建的实例对象

throw 抛异常

异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

function counter(x, y) {
    if(!x || !y) {
        throw new Error('参数不能为空!');
    }
    return  x + y;
}
counter();

总结:

  1. throw抛出异常信息,程序会终止执行
  2. throw后面跟的是错误提示信息
  3. Error对象配合throw使用,能够设置更详细的错误信息

try/catch

我们想要测试某些代码是否异常,可以通过try/catch捕获错误信息(浏览器提供的错误信息)try试试 catch拦住 finally最后

try {
    const p = document.querySelector('.p');
    p.style.color = 'red';
} catch(error) {
    console.log(error);
} finally {
    console.log('不管有没有错误都执行')
}

说明:

  1. 将预估可能发生错误的代码写在try代码中
  2. 如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息
  3. finally不管是否有错误,都会执行

持续更新…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值