JS进阶学习(作用域、函数进阶、解构赋值、原型链)

文章目录

        • 1.面相对象编程介绍
        • 2.ES6中的类和对象
        • 3.类的继承
      • ES6中的类和对象
        • 三个注意点
    • 作用域
        • 局部作用域
        • 全局作用域
        • 作用域链
        • JS垃圾回收机制(GC)
          • JS垃圾回收机制—算法说明
        • 闭包
        • 变量提升
    • 函数进阶
      • 函数提升
      • 函数参数
        • 动态参数
        • 剩余参数(提倡使用)
        • 展开运算符
      • 箭头函数(重要)
        • 基本语法
        • 箭头函数参数
        • 箭头函数this
    • 解构赋值
        • 数组解构
        • 对象解构
        • 遍历数组forEach方法(重点)
      • 构造函数
        • 构造函数创建对象
        • 实例化执行过程
      • 实例成员&静态成员
    • 内置构造函数
      • Object
      • Array
        • 1.数组常见实例方法—核心方法
        • 2. reduce的执行过程
        • find使用方法
        • every使用方法
        • some使用方法
        • 伪数组转化为真数组
      • String
        • split
        • substring()
        • startsWith()
        • includes
    • 原型链
      • 构造函数
      • 原型
        • 原型
        • constructor属性
        • 对象原型
        • 原型继承
        • 原型链
      • 给数组扩展方法

这周主要学习了原型与原型链、原型属性问题,作用域与作用域链、闭包,对象创建模式、继承模式。以下是这周做的而学习笔记

1.面相对象编程介绍

两大编程思想

pop 面向过程

oop 面向对象

面向对象具有灵活、代码可复用,容易维护和开发的优点,更适合多人合作的大型软件项目。

面向对象的特性:

  • 封装性

  • 继承性

  • 多态性

2.ES6中的类和对象

面向对象 (抽象的,具体的)

在JS中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如,字符串,数组,数值,函数等。

对象是由属性和方法组成的:

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的行为,在对象中用方法来表示(常用动词)

抽象了对象的公共部分

创建类,必须使用new来对类进行实例化才能使用

类中有个很重要的函数constructor构造函数

//创建一个类
class Star{
    constructor(uname,age){
        this.uname=uname;
        this.age=age;
    }
      //在类中添加方法
    sing(song){
        // console.log("我会唱歌")
        console.log(this.uname+song)
    }
    //(1)在类里面的所有函数不需要写function
    //(2)多个函数方法之间不需要添加逗号分隔
}
//利用类创建对象,也称对类进行实例化
var zly=new Star("赵丽颖",31);
var zs=new Star("某爽",28);
console.log(zly);
console.log(zs);
// (1)通过class对象创建类,类名习惯性首字母大写
// (2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
// (3)constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
// (4)生成实例的new不能省略
// (5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function

3.类的继承

super不仅可以调用父类中的构造函数,也可以调用父类的普通函数

//继承中的属性或者方法的查找原则:就近原则

//1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的

//2.继承中,如果子类里面没有,就去查找父类里面有没有这个方法,如果有,就去执行父类中的这个方法

class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    sum() {
        console.log(this.x + this.y);
    }
}
class Son extends Father {
    constructor(x, y) {
        //super必须在子类this之前调用
        super(x, y)//调用父类中的构造函数
        this.x = x
        this.y = y     
    }
    sub(){
        console.log(this.x - this.y);
    }
}
var son = new Son(5,3);
son.sub();
son.sum();

ES6中的类和对象

三个注意点
  1. 在ES6中没有变量提升,所以必须先定义类,才能类实例化对象
  2. 类里面的共有的属性和方法一定要加this使用
  3. 类里面this的指向问题
  4. constructor中的this指向的是创建的实例对象,方法里面的this指向这个方法的调用者
 var that;
 var _that;
class Star{
    constructor(uname){
    that=this
    console.log(this)
        this.uname=uname;
        this.btn=document.querySelector("button");
        this.btn.onclick=this.sing;//不要加小括号,加上后就立马执行了
    }
    //在类中添加方法
    sing(){
        //这个sing方法里的this指向的是btn这个按钮,因为这个按钮调用了这个函数
        console.log(this)
        console.log(this.uname)
    }
    dance(){
        //这个dance里面的this 指向的是实例对象zly 因为zly调用了这个函数
        _that=this
        console.log(this)
    }
}
var zly=new Star("赵丽颖")
zly.dance();
console.log(that===zly);
console.log(_that===zly);

作用域

局部作用域
  • 函数作用域
  • 块作用域(for循环、if、while)

let、const声明的会产生块作用域,var不会产生块作用域

不同代码块之间的变量无法互相访问

推荐使用let和const

全局作用域

script标签内和JS文件就是全局变量

1.为 window对象动态添加的属性默认也是全局的,不推荐!

⒉.函数中未使用任何关键字声明的变量为全局变量,不推荐!!!

3.尽可能少的声明全局变量,防止全局变量被污染

作用域链

本质上是底层的变量查找机制

在函数被执行时,会优先在当前函数作用域中查找变量

如果当前作用域查找不到,则会依次逐级查找父级作用域直到全局作用域

总结:

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

2.相同作用域链中按着从小到大的规则查找变量

3.子作用域能够访问父作用域,父级作用域无法访问子级作用域

JS垃圾回收机制(GC)

内存的生命周期

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

说明:

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

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

JS垃圾回收机制—算法说明
  • 引用计数

存在一个致命问题:嵌套引用(循环引用)

如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏

  • 标记清除法

    核心思路:从根部扫描对象,能查找到的就是使用的,查找不到的就要回收

闭包

闭包=内层函数+外层函数的变量
JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第1张图片

闭包的作用:封闭数据,实现数据私有,外部也可以访问函数内的变量。

闭包很有用,它允许将函数与其他操作的某些数据(环境)关联起来

闭包了能会引起内存泄漏的问题

闭包的基本格式:

     function outer(){
       let i =10;
        function fn(){
            console.log(i);
        }
       return fn;
    }
   const fun= outer(); 
    fun()

//简约写法
     function outer(){
       let i =10;
        return function(){
            console.log(i);
        }
    }
   const fun= outer(); 
    fun()
//闭包的应用: 统计函数调用的次数
let i=0;
function fn(){
    i++
    console.log(`函数被调用${i}`)
}
//值会有被篡改的风险

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第2张图片

//采用闭包的形式  实现数据的私有
function count(){
    let i=0;
    function fn(){
        i++
        console.log(`函数被调用${i}`);
    }
    return fn
}
const fun=count()//全局,不会被回收

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第3张图片

变量提升

把所有var声明的提升到当前作用域的最前面

只提升声明,不提升赋值 undefined

不建议使用var声明,let、const不存在变量提升

函数进阶

知道函数参数默认值、动态参数,剩余参数的使用细节,提升函数应用的灵活度,知道箭头函数的语法及普通函数的差异

函数提升

  //1.会把所有函数声明提升到当前作用域的最前面
        //2.只提升函数声明,不提升函数调用
        fn()
        function fn(){
            console.log("函数提升");
        }
    //函数表达式必须先声明和赋值,才能调用  
//这样写是错误的。因为只提升函数声明,不提升函数赋值,会报错
  fun()
        var fun = function () {
            console.log("函数表达式");
        }

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第4张图片

总结:

  1. 函数提升能够使函数的声明调用更灵活
  2. 函数表达式不存在提升现象
  3. 函数提升出现在相同作用域中

函数参数

动态参数

切入点:arguments

        function getSum(){
            //arguments 动态参数 只存在于函数里面
            //是伪数组
            console.log(arguments);
        }
        getSum(2,3,4)

在这里插入图片描述

 function getSum(){
            let sum=0;
            for(let i=0;i<arguments.length;i++){
                sum+=arguments[i]
            }
            console.log(sum)
        }
        getSum(2,3,4)
        getSum(1,2,3,4)
        getSum(2,2,3,4)

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第5张图片

总结:

  1. arguments是一个伪数组,只存在于函数
  2. arguments的作用是动态获取函数的实参
  3. 通过for循环依次得到传递过来的实参
剩余参数(提倡使用)
     function getSum(...arr) {
            console.log(arr);
        }
		//arr可以随便起名
        getSum(2, 3)
        getSum(1, 2, 3)

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第6张图片

剩余参数允许我们将一个不定数量的参数表示为一个数组

  1. …是语法符号,至于最末函数形参之前,用于获取多余的实参,使用的时候不需要…
  2. 借助…获取的剩余实参,是个真数组(可以使用各种arr的方法)

剩余参数主要的使用场景:用于获取多余的实参

剩余参数和动态参数的区别:动态参数是伪数组,剩余参数是真数组

展开运算符
        const arr=[1,2,3]
        //展开运算符可以展开数组
        console.log(...arr);

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第7张图片

不会修改原数组

典型应用场景:求数组最大值(最小值)、合并数组

   const arr=[1,2,3]
        //展开运算符可以展开数组
        // console.log(...arr);
        console.log(Math.max(1,2,3));
        console.log(Math.max(...arr));
         //结果都为3
        //实质上...arr===1,2,3
   		//合并数组
		const arr1=[1,2,3]
        const arr2=[4,5,6]
        const arr=[...arr1,...arr2]
        console.log(arr);

总结:

1.展开运算符的主要作用

​ 可以把数组展开,可以利用求数组最大值以及合并数组等操作

2.展开运算符和剩余参数的区别

​ 展开运算符主要是数组展开

​ 剩余参数在函数内部使用

箭头函数(重要)

**目的:**引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。

使用场景:箭头函数更适用于那些本来需要匿名函数的地方

基本语法

箭头函数主要替代函数表达式

只有一个形参的时候,可以省略小括号

只有一行代码的时候,我们可以省略大括号

只有一行代码可以省略return

箭头函数可以直接返回一个对象

    // //普通函数
        // const fn = function () {
        //     console.log(1, 2, 3);
        // }
        // fn()
        // //箭头函数主要替代函数表达式
        // const fn = () => {
        //     console.log(1, 2, 3);
        // }
        // fn()
        // //箭头函数传递参数
        // const fn=(x)=>{
        //     console.log(x);
        // }
        // fn(1)
        // //只有一个形参的时候,可以省略小括号
        // const fn=x=>{
        //     console.log(x);
        // }
        // fn(1)
        // //只有一行代码的时候,我们可以省略大括号
        // const fn=x=>console.log(x);
        // fn(1)
        //只有一行代码可以省略return
        // const fn=x=>x+x;
        // console.log(fn(1));
        //箭头函数可以直接返回一个对象
        const fn = (uname) => ({ uname: uname })
        fn("赵丽颖")
        console.log(fn("赵丽颖"));

总结:

  1. 箭头函数属于表示式函数,因此不存在函数提升
  2. 箭头函数只有一个参数时可以省略圆括号()
  3. 箭头函数函数体只有一行代码时可以省略花括号{},并自动作为返回值被返回
  4. 加括号的函数体返回对象字面量表达式
箭头函数参数

普通函数有arguments动态参数

箭头函数没有arguments动态参数,但是有剩余参数…args

        //利用箭头函数来求和
        getSum = (...arr) => {
            let sum = 0;
            for (let i = 0; i < arr.length; i++) {
                sum += arr[i]
            }
            return sum;
        }
        console.log(getSum(1,2,3,4,5));

总结:箭头函数中没有arguments动态参数,可以使用剩余参数

箭头函数this

在箭头函数出现之前,没一个新函数根据他是如何被调用的来定义这个函数的this值。

        //this指向window
        console.log(this);
        //普通函数中this指向window(即函数的调用者)
        function fn(){
        console.log(this);
        }
        window.fn()
        //对象方法里的this指向对象
        const obj={
            name:'andy',
            sayHi:function (){
                console.log(this);
            }
        }
        obj.sayHi()

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

        const obj={
            name:'andy',
            sayHi:()=>{
                console.log(this);
            }
        }
        obj.sayHi()
  const obj ={
            uname:"pink老师",
            sayHi:function(){
                console.log(this);
                let i=10
                const count =()=>{
                    console.log(this);
                }
                count()
            }
        }
        obj.sayHi()
//this指向都是obj

总结:

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

DOM事件回调函数不推荐使用箭头函数,特别是需要用到this的时候

事件回调函数使用箭头函数时,this为全局的window

解构赋值

解构的语法及分类,使用解构简洁语法快速为变量赋值

数组解构

数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法

  //基本语法:
        const arr=[100,60,80]
        //数组解构  赋值
        const [max,min,avg]=arr
        //就等价于如下赋值
        // const max =arr[0]
        // const max =arr[1]
        // const max =arr[2]
        console.log(max);
        console.log(min);
        console.log(avg);
 //典型应用   变量交互
       let a=1
       let b=2;//这里必须要加分号
        [b,a]=[a,b]
        console.log(a,b);

必须加分号的两种情况:

   //1.立即执行函数
        (function(){ })();
        (function(){ })();
        //2.使用数组的时候
        const str='pink';//如果这里不加分号,就会报错'pink'[1,2,3].map is not a function
        [1,2,3].map(function(item){
            console.log(item);
        })

总结:

1.数组解构赋值的作用是什么?

试将数组的单元值快速批量赋值给一系列变量的简洁语法

2.JS前面有哪两种情况是必须要加分号的

  • 立即执行函数
  • 数组解构

补充:

字符串拼接新思路:

利用map()和join()数组方法实现字符串拼接

map可以遍历数组处理数据,并且返回新的数组

map也称为映射。映射是个术语,指两个元素的 集之间元素相互“对应”的关系。

map重点在于有返回值(返回一个数组),forEach没有返回值

 const arr =['red','blue','pink'] 
        //数组map方法
        const newArr=arr.map(function(ele,index){
            console.log(ele);//数组元素
            console.log(index);//索引号
            return ele +"颜色"
        })
        console.log(newArr);
        

join方法

     const arr =['red','blue','pink'] 
        //数组map方法
        const newArr=arr.map(function(ele,index){
            console.log(ele);//数组元素
            console.log(index);//索引号
            return ele +"颜色"
        })
        console.log(newArr);
        //数组join方法
        //把数组中的而所有元素转换成一个字符串
        console.log(newArr.join());//小括号为空,用逗号分割   red颜色,blue颜色,pink颜色
        console.log(newArr.join(''));//小括号为空,无分割     red颜色blue颜色pink颜色
        console.log(newArr.join('|'));//小括号为空,用|分割   red颜色|blue颜色|pink颜色
    

数组解构的细节

  // const pc=['海尔','联想','小米','方正']
        // const [hr,lx,mi,fz]=['海尔','联想','小米','方正']
        // console.log(hr);
        // console.log(lx);
        // console.log(mi);
        // console.log(fz);
        // function getValue(){
        //     return [100,60]
        // }
        // const[max,min]= getValue()
        // console.log(max,min);
        //1.变量多,单元值少,undefined
        // const [a,b,c,d]=[1,2,3]
        // console.log(a);//1
        // console.log(b);//2
        // console.log(c);//3
        // console.log(d);//undefined
        //2.变量少,单元值多
        // const [a,b]=[1,2,3]
        // console.log(a);//1
        // console.log(b);//2
        //3.利用剩余参数 变量少,单元值多
        // const [a,b,...c]=[1,2,3,4]
        // console.log(a);//1
        // console.log(b);//2
        // console.log(c);//3,4
        //4.防止undefined传递,设个默认参数
        // const [a=0,b=0]=[1,2]
        // console.log(a);//1
        // console.log(b);//2
        //5.按需导入赋值
        // const [a,b,,d]=[1,2,3,4]
        // console.log(a);//1
        // console.log(b);//2
        // console.log(d);//4
        // //6.支持多维数组解构
        // const arr = [1, 2, [3, 4]]
        // console.log(arr[0]);
        // console.log(arr[1]);
        // console.log(arr[2]);
        // console.log(arr[2][0]);
        const [a, b, [c,d]] = [1, 2, [3, 4]]
        console.log(a);
        console.log(b);
        console.log(c);
        console.log(d);

总结:

变量的数量大于单元值数量时,多余的变量将会被赋为undefined

变量的数量小于单元值的数量时,可以通过剩余参数…获取剩余单元值,但只能置于最末位

对象解构

对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法

    //  //对象解构
        const obj={
            uname:'pink老师',
            age:18
        }
        //解构的语法
        // const {uname,age}={ uname:'pink老师', age:18}
        //等价于const uname=obj.uname
        //要求属性名和变量名必须一致
        // console.log(username);
        // console.log(age);
        //1.对象解构的变量名 可以重新改名 旧变量名:新变量名
        // const{uname:username,age}={ uname:'pink老师', age:18}
        // console.log(username);
        // console.log(age);
        //2.解构数组对象
        const pig=[
            {
                uname:'佩奇',
                age:6
            }
        ]
        const [{uname,age}]=pig
        console.log(uname);
        console.log(age);对象解构
        const obj={
            uname:'pink老师',
            age:18
        }
        //解构的语法
        const {uname,age}={ uname:'pink老师', age:18}
        //等价于const uname=obj.uname
        //要求属性名和变量名必须一致
        console.log(uname);
        console.log(age);

多级对象解构

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

多级数组对象解构

 const pig=[
        {
            uname:'佩奇',
            family:{
                mother:'猪妈妈',
                father:'猪爸爸',
                brother:'乔治'
            },
            age:6
        }
        ]
        const [{uname,family:{mother,father,brother}}]=pig
        console.log(uname);
        console.log(mother);
        console.log(father);
        console.log(brother);
遍历数组forEach方法(重点)

forEach()方法用于调用数组的每个元素,并将元素传递给回调函数

主要使用场景:遍历数组的每个元素

        //forEach就是遍历 加强版的for循环
        const  arr=['red','green','pink']
        const result=arr.forEach(function(item,index){
            console.log(item);//数组元素 red green pink
            console.log(index);//索引号
        })
        console.log(result);//打印结果为undefined
```## 深入对象

### 创建对象的三种方式

**利用对象字面量创建对象**

```js
const o ={
            uname:'佩奇'
        }

利用系统给定的函数new object创建对象

 // const obj=new Object()
        // console.log(obj);
        // obj.name="pink老师"
        const obj=new Object({uname:"pink老师"})
        console.log(obj);

自定义构造函数创建(抽封一下)

     function Pig(name, age, gender) {
            this.name = name
            this.age = age
            this.gender = gender
        }
        //创建佩奇对象
        const Peppa=new Pig('佩奇',6,'女')
        const George=new Pig('佩奇',3,'男')
        const Mum=new Pig('猪妈妈',30,'女')
        const Dad=new Pig('猪爸爸',32,'男')
        console.log(Peppa);
        console.log(George);
        console.log(Mum);
        console.log(Dad);

构造函数

构造函数创建对象

构造函数是一种特殊的函数,主要用来初始化对象

构造函数在技术上是常规函数

不过有两个约定:

  1. 命名以大写字母开头
  2. 只能由“new”操作符来执行
 //创建一个猪 构造函数
        function Pig(uname,age){
            this.uname=uname  //第一个uname属性//第二个uname形参
            this.age=age
        }
        console.log( new Pig('佩奇',6));

说明:

  1. 使用new关键字调用函数的行为被称为实例化
  2. 实例化构造函数时没有参数时可以省略()
  3. 构造函数内部无需写return,返回值即为新创建的对象
  4. 构造函数内部的return返回的值无效,所以不要写return
  5. new object()和new Date()也是实例化构造函数

总结:

构造函数的作用是什么?怎么写?

  • 构造函数是用来快速创建多个对象的
  • 大写字母开头的函数

new关键字调用的函数的行为称为实例化

构造函数内部不需要写return,构造函数自动返回创建的新对象

实例化执行过程
  1. 创建新空对象
  2. 构造函数this指向新对象
  3. 执行构造函数代码,修改this,添加新的属性
  4. 返回新对象

实例成员&静态成员

通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)

说明:

  1. 为构造函数传入参数,创建结构相同但值不同的对象
  2. 构造函数创建的实例对象彼此独立互不影响

静态成员

构造函数的属性和方法被称为静态成员(静态属性和静态方法)

说明:

  • 静态成员只能构造函数来访问
  • 静态方法中的this指向构造函数

比如Date.now( ) Math.PI Math.random( )

总结:

1.实例成员(属性和方法)写在谁身上?

实例对象的属性和方法即为实例成员

实例对象相互独立,实例成员当前实例对象使用

2.静态成员(属性和方法)写在谁身上?

构造函数的属性和方法被称为静态成员

静态成员只能通过构造函数来访问

内置构造函数

JS中最主要的数据类型有6种

基本数据类型:字符串、数值、布尔、undefined、null

引用类型:对象

   const str ="pink"
    //js 底层完成,把简单数据类型包装成了引用数据类型
        //const str =new String ('pink')
        console.log(str.length);
        const num=12
        console.log(num.toFixed(2));//num.toFixed()是保留几位小数

其实字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型

JS中几乎所有的数据都可以基于构造函数创建

内置构造函数分为引用类型和包装类型

引用类型

Object、Array、RrgExp、Date等

包装类型

String、Number、Boolean等

    // const str ="pink"
        //js 底层完成,把简单数据类型包装成了引用数据类型
        //const str =new String ('pink')
        console.log(str.length);
        const num=12
        console.log(num.toFixed(2));//num.toFixed()是保留几位小数

Object

是内置的构造函数,用于创建普通对象

      const o={uname:'pink', age:18}
//1.获得所有的属性名
console.log(Object.keys(o));//返回数组['uname','age']
//获取所有的属性值
console.log(Object.values(o));//['pink',18]
//拷贝对象
const oo={}
Object.assign(oo,o)
console.log(oo);
//经常使用的场景:给对象添加属性
Object.assign(o,{gender:"女"})
console.log(o);

Array

是内置的构造函数,用于创建数组

创建数组建议使用字面量创建,不用array构造函数创建

1.数组常见实例方法—核心方法
方法 作用 说明
forEach 遍历数组 不返回数组,经常用于查找遍历数组元素
filter 过滤数组 返回新数组,返回的是筛选满足条件的数据组元素
map 迭代数组 返回新数组,返回的是处理之后的数组元素,想要使用返回的新数组
reduce 累计器 返回累计处理的结果,经常用于求和等
//数组reduce方法
//arr.reduce(function(上一次值,当前值){},初始值)
const arr =[1,5,6,8]
//1.没有初始值
// const total=arr.reduce(function(prev,current){
//     return prev+current
// })
// console.log(total);
//2.有初始值
const result=arr.reduce(function(prev,current){
    return prev+current
},10)
console.log(result);
//3.箭头函数的写法
 const total=arr.reduce((prev,current)=>prev+current,10)
 console.log(total);
2. reduce的执行过程
  1. 如果没有起始值,则上一次值以数组的第一个元素的值
  2. 每一次循环,把返回值给作为下一次循环的上一次值
  3. 如果有起始值,则起始值作为上一次值

没有起始值,循环次数比数组元素个数少一次

有起始值,循环次数等于数组元素个数

find使用方法
    // const  arr=['red','blue','green']
    //     const result=arr.find(function(item){
    //         return item==='blue'
    //     })//会将blue作为返回值返回
    //     console.log(result);
    //find最常使用的场景
    const  arr=[
        {
            name:'小米',
            price:1999
        },
        {
            name:'华为',
            price:3999
        }
    ]
    //找小米这个对象,并返回这个对象
    // arr.find(function(item){
    //     // console.log(item);
    //     // console.log(item.name);
    //     return item.name==='小米'
    // })
    const mi=arr.find(item=>item.name==="小米")
    console.log(mi);
every使用方法

every()方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。返回一个布尔值

  //every()每一个是否都符合条件,如果都符合返回true,否则返回false
        const  arr=[10,20,30]
        const flag=arr.every(item=>item>=20)
        console.log(flag);
some使用方法

some()方法测试一个数组内是否含有元素能通过某个指定函数的测试。返回一个布尔值

    //some()至少有一个是否符合条件,如果有返回true,否则返回false
        const  arr=[10,20,30]
        const flag=arr.some(item=>item>=20)
        console.log(flag);

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第8张图片


        const spec={size:'40cm*40cm',color:'黑色'}
        //1.所有的属性值获取过来
        console.log(Object.values(spec));
        //2.转换为字符串 join('/')把数组根据分隔符转化为字符串
       console.log( Object.values(spec).join('/'));
       document.querySelector('div').innerHTML= Object.values(spec).join('/')

伪数组转化为真数组

静态方法 Array.from( )

<body>
    <ul>
        <li>1li>
        <li>2li>
        <li>3li>
    ul>
    <script>
        const lis =document.querySelectorAll('ul li')
        console.log(lis);//获取过来的是伪数组NodeList(还有arguments获取的也是伪数组)
   // lis.pop();//伪数组无法使用pop删除方法
   const liss=Array.from(lis)
   console.log(liss);
   liss.pop()//真数组可以使用
   console.log(liss);
    script>
body>

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第9张图片

String

split
        //把字符串转化为数组,与join超级相似,根据分隔符将其分割成数组
        const str ='pink ,red'
        const arr=str.split(',')
        console.log(arr);
        const str1 ='2022-4-8'
        const arr1=str1.split('-')
        console.log(arr1);

效果如下:
JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第10张图片

substring()
   //字符串截取 substring(开始的索引号[,结束的索引号])
        const str='再也不用做核酸了'
        console.log(str.substring(5))//如果省略了结束索引号,默认取到最后
        console.log(str.substring(5,6))//截取的部分不包括结束索引
startsWith()
//startswith()用来判断当前字符串是否以另外一个给定的字符串开头,并根据判断结果返回true或false
        var str="To be, or not to be, that is the question."
        alert(str.startsWith("To be"));         //true
        alert(str.startsWith("not to be"));     //false
        alert(str.startsWith("not to be",10));  //true  加空格
includes
//includes 判断某个字符是否包含在一个字符串内
        var str="To be, or not to be, that is the question."
        console.log(str.includes('To be'));         //true
        console.log(str.includes('question'));      //true
        console.log(str.includes('nonexistent'));   //false
        console.log(str.includes('To be',1));       //false
        console.log(str.includes('To BE'));         //false
        console.log(str.includes('To b'));          //true

原型链

JS面向函数编程

构造函数

封装是面向对象思想中比较重要的一部分,JS面向对象可以通过构造函数实现封装。

同样的将变量和函数组合到了一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。

总结:

  • 构造函数体现了面向对象的封装特性
  • 构造函数实例创建的对象彼此独立、互不影响

之前我们学习的构造函数很好用,但存在浪费内存的问题

原型

原型
  • 构造函数通过原型分配的函数是所有对象所共享的
  • JS规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
  • 这个对象可以挂载函数,对象实例化不会多次原型上函数,节约内存
  • 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
  • 构造函数和原型对象中的this都指向实例化对象
  • 总结:

原型是一个对象,我们也称prototype为原型对象

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第11张图片

原型的作用:

  • 共享方法
  • 可以把那些不变的方法,直接定义在prototype对象上

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

constructor属性

每个原型对象里面都有一个constructor属性

使用场景:

如果有多个对象的方法,我们可以给原型对象采取对象形式赋值

但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不在指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数

       //constructor   构造函数
        //该属性指向该原型对象的构造函数,简单理解,就是指向我的爸爸,我是有爸爸的孩子
        function Star(){
        }
        // const ldh=new Star()
        // console.log(Star.prototype);
        // console.log(Star.prototype.constructor===Star);
        // Star.prototype.sing=function(){
        //     console.log('我会唱歌');
        // }
        // Star.prototype.dance=function(){
        //     console.log('我会跳舞');
        // }     //这样写显得繁琐
        console.log(Star.prototype); 
        Star.prototype={  
            //从新指回创造这个原型对象的构造函数 
            constructor:Star,
            sing:function(){
                console.log('我会唱歌');
            },
            dance:function(){
                console.log('我会跳舞');
            },
        }
        console.log(Star.prototype); 

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第12张图片

作用:指向该原型对象的构造函数

对象原型

对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。

 function Star(){ }
        const ldh=new Star()
        //对象原型__proto__ 指向该构造函数的原型对象
        console.log(ldh.__proto__);
        console.log(ldh.__proto__===Star.prototype);  //true
        //对象原型里面有constructor指向构造函数Star
        console.log(ldh.__proto__.constructor);

注意:

  • __proto__是JS非标准属性
  • [[prototype]]和__proto__意义相同
  • 用来表明当前实例对象指向哪个原型对象prototype
  • __proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第13张图片

总结:

1.prototype是什么?从哪里来?

  • 原型(原型对象)
  • 构造函数都自动有原型

2.constructor属性在哪里?作用干啥的?

  • prototype原型和对象原型__proto__里面都有
  • 都指向创建的实例对象/原型的构造函数

3.__proto__属性在哪里?指向谁?

  • 在实例对象里面
  • 指向原型prototype
原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript中大多是借助原型对象实现继承的特性

原型继承中出现的问题:

都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZOMRaKJ-1681011543044)(C:\Users\86184\Desktop\4.3-4.14\原型继承出现的问题.png)]

解决问题的思路:让Woman和Man指向的对象不同,内容依然相同

解决问题的方法:构造函数 new出来的对象 结构一样 但对象不一样

   // //男人,女人都继承了人这个特性
        // //继续抽取
        // const Person = {
        //     eyes: 2,
        //     head: 1
        // }

        //构造函数 new出来的对象 结构一样 但对象不一样
        function Person(){
            this.eyes=2
            this.head=1
        }
        //女人 构造函数  想要继承Person
        function Woman() {
            // this.eyes = 2
            // this.head = 1
        }
        //Women 通过原型来继承Person
        // Woman.prototype = Person
        Woman.prototype =new Person()
        //指回原来的函数
        Woman.prototype.constructor = Woman
        //给女人添加一个方法  生孩子
        Woman.prototype.baby=function(){
            console.log('宝贝');
        }//这样添加之后,男女都有baby,麻烦大了
        //出现该问题的原因
        //男人和女人都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
        //解决问题的思路:让Woman和Man指向的对象不同,内容依然相同
        //解决问题的方法:构造函数 new出来的对象 结构一样 但对象不一样 
        const red = new Woman()
        console.log(red);
        console.log(Woman.prototype);//指回了原来的函数,既继承了属性,又有了constructor
        // console.log(red.eyes);

        //男人 构造函数   想要继承Person
        function Man() {
            // this.eyes = 2
            // this.head = 1
        }
        //Man通过原型继承Person
        Man.prototype=new Person() 
        //指回原来的函数
        Man.prototype.constructor=Man 
        const pink = new Man()
        console.log(pink);
        console.log(Man.prototype);

原型链

基于原型对象的继承是的不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们就爱那个原型对象的链状结构称为原型链

JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第14张图片
JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第15张图片
JS进阶学习(作用域、函数进阶、解构赋值、原型链)_第16张图片

在这里粘贴一个博客,觉得讲的挺好的

(37条消息) JavaScript完整原型链图解_js原型链图示_旦旦boom的博客-CSDN博客

给数组扩展方法

 //自己定义 数组扩展方法 求和  和  最大值
        //1. 我们定义的这个方法,任何一个数组实例对象都可以使用
        //2. 自定义的方法写(挂载)到 数组.prototype身上

        //求最大值
        // const arr=[1,2,3]
        // Array.prototype.max=function(){
        //     //展开运算符
        //     return Math.max(...this)
        // }
        // console.log(arr.max());

        //求最小值
        // const arr=[1,2,3]
        // Array.prototype.min=function(){
        //     return Math.min(...this)
        // }
        // console.log(arr.min());

        
        //求和 方法
        const arr=[1,2,3]
        Array.prototype.sum=function(){
           return this.reduce((prev,item)=>prev+item,0)
        }
        console.log(arr.sum());

你可能感兴趣的:(前端,javascript,学习,开发语言)