js高级语法

前置话题

代码观看地址

新老师的代码观看地址

语雀预习资料

预习资料

思维导图

在线思维导图

箭头函数

普通函数使用

image-20220513090946299

箭头函数使用

语法:let test = (参数) => {函数体}

// 箭头函数
        let func3 = ()=>{
            console.log('3');
        }
        func3()

const的说明

说明:

​ 1、const 声明的变量永远不能被修改,也称为常量

​ 2、规范程序员设计,需要明确哪些变量可以修改,哪些不能修改

​ 3、什么时候用const

1 多看老师的使用 
2 担心 先使用 let 

​ 4、有经验的使用:能使用const就不用let

const username = 'dwjia'
username= 'csc'//报错

箭头函数传参

箭头函数 传参

​ 如果只传递一个参数的话 小括号可以省略 :const func1 a =>{}

​ 如果不传递参数 或者 传递参数大于1个的话,小括号不能省略

		const func2 = a=>{
            console.log('a');
        }//只有一个参数,可以省略不写()
        const fun3 = (a,b)=>{
            console.log(a,b);
        }//两个参数,必须写()
        const fun4 = ()=>{
            console.log('我是不传参数');
        }//不传参,必须写
        func2('1')
        fun4('2')
        fun3('3','4')

箭头函数返回值

返回值个数

只有一个返回值:可以省略{},相当于省略了retuen,只写后面的语句

		const fn = (a)=>a+1 //只有一个返回值,可以省略{},这句相当于是return a+1
        let num = fn(4)
        console.log(num);

多个返回值:多个返回值则不能省略{}

const fn2 = (a)=>{
            let b =a + 1;
            let c = a + 2;
            return {b,c}
        } //多个返回值则不能省略{}
        console.log(fn2(12));

返回值对象

		// 返回数值
        const fn1 = ()=> 123
        // 返回字符串
        const fn2 = () => '字符串'
        // 返回布尔类型
        const fn3 = () => true
        // 返回会数组
        const fn4 = () => ['a','sca']
        // 返回对象
        const fn5 = () => {user:'123'} 

打印效果:image-20220513102931467

原因:

// 让箭头函数 你猜猜 我大括号是什么意思
      // 1 猜 对象的 符号
      // 2 猜 函数体结构 符号 (被识别为函数体,而不是对象)

解决方法:

​ 如果你的箭头函数 省略大括号的 情境下

​ 想要返回 一个对象 固定语法 添加一个小括号

      const fn6 = ()=>({user:'23'})
        console.log('fn6(): ', fn6());

​ 结果:image-20220513103241791

函数参数默认值

		const fn1 = (msg)=>{
            console.log(msg);
        }
        fn1('没问题') //传参
        fn1()//不传参

效果:image-20220513105531366

需求更新:返回一个数组的长度,如果没有传入数组,返回长度为0

const getLength = (arr = []) => console.log(arr.length);
        let arr1 = ['1', '2']
        getLength(arr1)
        getLength()

效果:image-20220513105831116

解构

解构数组

 		let arr = ['0','1','2']

        //普通区数组元素的方法
        const str1 = arr[0]
        const str1 = arr[1]
        console.log(str1,str2);

        //解构写法:只是一种更加方便获取数据的写法
			//数组解构关注顺序
        const [str1,str2] = arr //注意:这里获取数组使用[]
        console.log(str1,str2);

变量交换

 //解构的应用场景
            //数字交换
        let a = 100;
        let b = 200;
        [b,a] = [a,b];
        console.log(a,b);

解构对象

		// 对象解构
        const obj = {
            user:'我是鲁鲁修',
            country:'大不列颠'
        };

        const {user,country} = obj; //注意:这里获取对象使用{}
        console.log(user,country);

注意事项:

  • 取值名与对象属性名保持一致,否则无法识别

    		const obj = {
                user:'我是鲁鲁修',
                country:'大不列颠'
            };
    
    		const {a,b} = obj; 
            console.log(a,b);//undefined undefined
    
  • 解构的符号要与类型一致

    //解构数组arr
    const [str1,str2] = arr //注意使用[],使用数值和使用数组的都是[]
    //解构对象obj
    const {user,country} = obj; //注意使用{}
    

对象简写

对象属性简写

​ 原理还是赋值,只是写法更加简便了,所以属性要与变量保持一致

const username = 'cc';
        const skill = 'longLife'
        const obj= {
            username,//相当于username : username
            skill, //skill: skill
            // 书写一个没有定义的变量
            // height//执行报错:height is not defined
        }
        console.log(obj);

对象函数简写

​ 主要是省略了:function

		const username='cc'
        const obj = {
            username,
            // 以往写法:
            // say:function(){}
            
            //简写
            say(){
                console.log('方法');
            }
        }
        console.log(obj);

剩余运算符

数组筛选

		const arr = ['a','b','c','d','e']
        const[let1,let2,...list] = arr;
        console.log(list);

效果:image-20220513145722092

对象筛选

    	//对象筛选
        const obj = {
            a:1,
            b:2,
            c:3,
            d:4
        }
        const{a,b,c,...obj2} = obj
        console.log(obj2);//d:4

在函数的运用

//剩余运算符再函数中的运用
                //传入多个参数时
        calc()
        calc(1)
        calc(1,2)
        calc(1,2,3)

        function calc(...params){
            //params可以获取到所有 传递给calc的参数 封装到一个数组中
            console.log(params);//输出稳定,都是输出数组
            
        }

复制引用类型

就是复杂数据类型

// 由于复杂数据类型的复制时按地址传输,因此更改新旧对象都会引起两者的改变,因此引入了剩余运算符的操作
            //复制对象,双方改变不影响
        const obj={kl:546,jn:594,cf:7878,ser:4844}
        const newObj = {...obj}
        obj.kl = '我改的'
        console.log(newObj); //{kl:546,jn:594,cf:7878,ser:4844}
        
            //复制数组
        const arr = ['a','csc','gbg']
        const newArr = [...arr]
        arr.push('我增加的')
        console.log(newArr);//['a','csc','gbg']

数组方法

map

		const arr = ['a','b','c','d','e','f']
            //需求:将数组的值放入每一个li标签中
 // 普通做法
        const newLi = []
        arr.forEach(function(value){
            newLi.push(`<li>${value}</li>`)
        })
        console.log(newLi);
//map做法
        const newLi1 = arr.map(function(value,index){
            // return `<li>${index}-${value}</li>`
            return `<li>${value}</li>`
        })
        //简写做法
        //const newLi1 = arr.map((value,index)=>`<li>${value}</li>`)
        console.log(newLi1);

filter

		const arr1 = [1,2,3,4,5,6]
        const newArr0 = arr1.filter((value)=>{
            return value>1
        })
        const newArr1 = arr1.filter((value)=>value>1)
        console.log('newArr1:',newArr1);

every

类似于&&,全符合为真(检测一个不合格就弹出false,后面不再检测)

const names =['黄圣飞',
        '子聪',
        '王锦双',]
        const result = names.every((v)=>v.length==3)
        console.log(result);//true

注意:every对于空数组 也会返回true

		const arr=[]
        const result2 = arr.every((v)=>v.length==3)
        console.log('result2: ', result2);//true

some

一个符合就返回true(一旦发现符合的,就返回true,后面的不再查看)

const arr =['健康',
        '健康',
        '健康',
        '健康',
        '不健康']
        const result = arr.some((value)=>value==='不健康')
        if(result){
            console.log('有危险');
            
        }else{
            console.log('没事');
            
        }

findindex

语法:arr.findIndex((value) => {条件})

let index = arr.findIndex((value) => value.height === 60)
            // let index2 = arr.findindex(function(value,index){
            //     value.height===60
            // })
console.log(index);
arr.splice(index, 1)
console.log(arr)

includes

语法:arr.includes(想要检测的元素)

let arr = ['a', 'b', 'v'];
//let result = arr.includes(value => value === 'a')//错误写法,includes语法错误
let result = arr.includes('a')//里面直接写条件
console.log(result)//true

indexOf

没有返回-1

const arr = ['a', 'c', 'd']
const index = arr.indexOf('a')
console.log(index)
  • 类似于 findindex

    • findIndex可以用于复杂数据类型,比如数组里面的对象等,
  • indexOf可以用于简单的数组

  • 需求决定使用的方法

join

const arr = ['a', 'd', 'c', 'd']
        const arrJoin = arr.join('-')
        console.log(arrJoin)
            // 把数组渲染成一个标签, 使用的方法主要是map和join
            // const arr2 = ['<li>76</li>', '<li>5</li>', '<li>vf</li>', '<li>vv</li>']

        // 拼接字符串
        // arr = arr.map(value => `<li>${value}</li>`) //用map处理,这里报错了,说arr设置常量不能被更改
        let newArr = arr.map(value => `<li>${value}</li>`)
        newArr = newArr.join('')
        console.log(newArr)

常用:

  • 将数组渲染成标签,需要配合map和join实现
  • join比较常见的用法就是直接链接

set对象

使用

  • set是一个对象,且里面的数据不允许重复出现
  • 向对象里面增加数据,需要调用add方法
let set = new Set();
console.log(typeof set);

 // 2、存放数据 调用add方法
set.add(1);
set.add(2);
set.add(3);
set.add(3); //重复数据不添加
set.add(4);
console.log(set);

效果:image-20220515145200956

  • 将set对象转成数组
// 想要把set的数据遍历,使用forEach等等方法
        // 将set对象转成数组
        let arr = [...set]// 解构
        console.log(arr)

效果:image-20220515145227787

  • 将数组转成set对象
// 将数组转化为对象 直接将数组塞进去set对象就好
        const arrset = [45, 78, 6, 77, 88]
        let set2 = new Set(arrset)

作用

  • 根据他的特性,可以用于去重
    • 定义数组
    • 将数组转化为set对象
    • 再添加数据

面向对象

字面量

  • 优点:清楚直观,适合于小demo
  • 缺点:没有任何继承或着封装的特性,不利于后期维护,在大项目中不推荐使用
// 创建对象的方式一:字面量=》字面意思
        // 缺点: 不方便维护 - 修改
        const obj = {
            name: '八戒', //如果现在需要把name->nickname,则需要将下面3哥也全部改变,可能会出现改多改少出现程序错误的现象
            height: 160
        }
        const obj1 = {
            name: '八戒',
            height: 160
        }
        const obj2 = {
            name: '八戒',
            height: 160
        }
        const obj3 = {
            name: '八戒',
            height: 160
        }

工厂函数

  • 作用:实现了封装
// 创建对象的方式二:工厂函数
        // 接收两个参数值,再从函数中返回一个数组
        // 优点:便于维护,此时要修改属性名时只需要修改工厂函数属性的一个即可
        function person(name, height) {
            return {
                nickname: name,
                height //简写,全称是:height:height
            }
        }
        const obj4 = person('cac', 445)
        const obj5 = person('ccs', 455)
        const obj6 = person('vfv', 45)
        const obj7 = person('tery', 745)
        console.log(obj4, obj5, obj6, obj7)
  • 缺点:没有继承性

构造函数

  • 本质:就是一个函数
  • 作用:创建对象
  • 只要是被new出来的对象,统称为实例
  • 细节:
    • 首字母大写
    • 这样和内置的构造函数命名就统一了
let per = new Person(); //per是实例,1、先实例化对象
function Person() {

}//2、再使用
console.log(per)//返回空对象

效果:image-20220515162039866

// 非构造函数的写法( 普通函数)
        let person = Person()
        function Person() {}
        console.log(person) //undefined

构造函数和this

  • 每一个构造函数中 都存在 有一个 魔鬼 this
  • 构造函数 默认情况下 就是返回了 this
  • this 等于你所new出来的实例 per
  • 只要给构造函数中的this 添加 属性或者方法,实例就能相对应的增加属性或方法
// 知识点1:
        let set = new Set()
        console.log(set) //这个内置的有好多属性,如果自定义的需要则自己添加
        let per = new Person();
        function Person() {
            this.name = '秦始皇'
            this.add = () => {}
                //add = function(){}
            this.clear = () => {}
        }
        console.log(per)

set内置对象:image-20220515171012749

Person对象:!image-20220515171107997

 // 知识点2:
        const pers = new Person();

        function Person() {}
        pers.username = 'baba'
            // 现在单独开一个
        const per2 = new Person()

        console.log(pers)
        console.log(per2) //不会做出更改

效果:image-20220515170835329

构造函数弊端

  • 我们自己的构造函数,方法无法共享
  • 指向堆的不同地址,如下一节所示
function Person() {
        this.say = function () {};
      }
const p1 = new Person();
const p2 = new Person();
console.log(p1 === p2); // 两个对象的比较 内存地址 false
console.log(p1.say === p2.say);// false 两个函数 存放在不同地址中
  • 自带的的构造函数,方法共享
  • 指向堆的同一个地址,如下一节所示
const s1 = new Set();
const s2 = new Set();
console.log(s1 === s2);// false
console.log(s1.add === s2.add); // true 

引用类型在队中存放数据

image-20220515192112389

构造函数方法指向同一个

解决方案:

  • 构造函数中 当我们定义方法的时候 一般都不会只在 构造函数中写死
  • 让我们方法 都指向外部 单独声明的方法 多个实例去共享方法!!!
         function say() {
            console.log('我是外面的');
        }

        function Person() {
            this.say = say //我把地址指向了外面的
        }
        const p1 = new Person()
        const p2 = new Person()
        console.log(p1 === p2);//false
        console.log(p1.say === p2.say); //true
		//指向外面后p1、p2的say都是指向同一个堆地址

总结:

1 值类型 是存在 栈空间 适合存放 固定大小的数据
2 引用类型(对象、数组、函数) 存在 堆空间  适合存放大小不固定的数据
3 new了两个对象的时候, 两个对象的内存地址 不相等 
4 我们希望 两个对象的方法 内存地址是相等!!! 
    1 在构造函数中 当我们定义方法的时候  一般都不会只在在 构造函数中写死 
      让我们方法 都指向外部 单独声明的方法  多个实例去共享方法!!! 

构造函数的基本使用

  • 构造函数的时候 属性 分成了两种

​ 1 函数类型的属性

​ 一般都是写在外部!!!

​ 2 非函数类型的属性

​ 一般是写在内部!!!

        const per = new Person(100);

        function Person(hairNum) {
            this.hairNum = hairNum;
            this.decrease = decrease;//为了可以共享,我们把函数类型的属性卸载了外面,然后使用“=”链接路径
        }
		//这个就是写在外面的函数
        function decrease() {
            this.hairNum = this.hairNum + 10;
            console.log('头发现存数量:', this.hairNum)
        }
        let button = document.querySelector('button')
        button.addEventListener('click', () => {
            per.decrease() //只需要每一次带点击实现增加即可
        })

  • 优点:对象构造时都可以使用这个函数
  • 缺点:由于函数名只能使用一次,会造成全局污染,你用过之后别人就不能再使用,甚至会出现覆盖问题

构造函数+原型

  • 使用背景:为了解决上面构造函数可能带来的全局污染的问题提出的结局方案

  • 什么是原型:image-20220515233915102

  • 原型有什么:image-20220515234227775内置的函数

使用:

在外部【new的对象名】.【prototype】.【要添加的对象属性函数】= function(){}

        function Person(name, height) {
            this.name = name;
            this.height = height
                // this.say = say//不像构造函数一样直接写在里面做链接,这里面只放非函数属性
        }
		//单独写在外面
        Person.prototype.say = () => {
            console.log('我是写在原型里的', this.name);
        }
        const per = new Person('黑白棋', 158)
        console.log(per);
        per.say()

再次了解prototype

  • 新语法:console.dir();

  • 作用:console.dir()方法可以在控制台显示指定javascript对象的属性。打印出该对象的所有属性和属性值.

  • 话题引入:获取标签对象

const div = document.querySelector('div')
const button = document.querySelector('button')
console.dir(div);
console.dir(button);

效果:

image-20220516103708191image-20220516103737455

再点开这个原型:

  • HTMLDivElement

image-20220516103955228

  • HTMLButtonElement

image-20220516104033008

共同点:他们都公用一个父类的原型

因此,我们也想做到父类的方法,我们子类要继承父类的方法,让他们有联系起来
因此以下提出几种方案:

原型来实现方法的继承

继承方法

使用方法:

  • 子类的写在原型上的函数 = 父类原型上的函数
  • WhitePerson.prototype.say = Person.prototype.say
  • 白人用了Person继承了say方法
// 父亲 Person ;say
            // 儿子:
            //     YellowPerson
            //     WhitePerson
        
        //老爹的方法
        function Person(){}
        Person.prototype.say = function(){
            console.log('我是老爹的');
            
        }
        // 儿子1
        function YellowPerson(){}
        // 继承老爹的方法
        YellowPerson.prototype.say = Person.prototype.say
        YellowPerson.prototype.jump = ()=>{
            console.log('我是儿子1自己的方法');
            
        }

        // 儿子2
        function WhitePerson(){}
        //我也要用老爹的方法
        WhitePerson.prototype.say = Person.prototype.say


        // new一个
        const yp = new YellowPerson()
        const wp = new WhitePerson()
        console.log(yp);
        console.log(wp);
        yp.say();
        wp.say();

        // 添加儿子自己的方法还是原来的方式添加即可
        yp.jump()
		console.log(yp)//第一个儿子

第一个儿子:image-20220516195819538

继承属性

  • 用什么继承:call的使用

  • 怎么继承:属性的继承 在 儿子的构造函数中 调用 父亲的构造函数

  • 使用方法父亲的构造函数.call(this,参数。。。)

//普通函数作为父亲
function Person(name,color,height,work){
            this.name = name
            this.color = color
            this.height = height
            this.work = work
        }
//构造函数继承父元素的属性
function YellowPerson(name,color,height,work,nickname){
    Person.call(this,name,color,height,work);
    this.nickname = nickname;
}
const person = new Person()
const yp = new YellowPerson('chjds','defew','565465','wwee','fefee')
console.log(yp);//打印结果如下所示

效果:image-20220516200259363

封装于继承案例

案例 : 目的 利用 面向对象的语法和思想 来结合 dom操作 解决实际的问题 

需求:
      1 定义父构造函数  
        HTMLElement  
         1 属性  dom 
         2 方法  appendTo 

      2 定义 一个儿子  HTMLDivElement
        1 会继承 父亲 属性和方法 
        2 儿子还有自己独立的功能  
            const  myDiv = new  HTMLDivElement("div中的文本")
            myDiv.appenTo("p")  ;  p标签中会多出有一个 文本为  “div中的文本”  的div
           儿子还会有自己的一个方法
           点击按钮  myDiv.scale(2);   缓慢放大两倍

      3 定义 另外一个儿子  HTMLImgElement 
        1 会继承 父亲 属性和方法  
        2 自己独立的功能
          const myImg = new HTMLImgElement(图片的地址)
          myImg.appendTo("a")  a标签中会多了一个 图片标签
          儿子还会有自己的一个方法
          点击某个按钮  myImg.borderRadius("50%")  缓慢变成 边框圆角 50% 效果 

      4 代码提示 
        1 先根据需求 把 代码的解构整理好   

        2 再去思考实现的逻辑 
       */

静态结构:

<button>div放大两倍</button>
<input type="button" value="设置图片缓慢边框圆角效果" />
<div class="box"></div>

类的基本使用

es5

  • 1 构造函数 来创建对象

  • 2 对象 两种信息

    • 1 属性
    • 2 方法

es6

  • 优点:es6 新 简洁 class 面向对象

初级实现代码:

一点拓展:js中的原型(用来解决下面普通函数为什么有prototype的问题)

es5:
function Person(){
    //增加属性需要使用到this
    this.name = '13';
    this.height = 150;
}
//可以看出,书写对象的方法需要单独拿出来,写在原型里面
Person.prototype.say(){
    console.log('我是大美铝')
}
const p2 = new Person();

es6:
 class Person{
            name= '123';
            height = 150;
            say(){
                console.log('我是大美铝');
            }//写在了原型里面
        }
        const person = new Person();
        console.log(person);

  • 效果:image-20220516200931035

class方式实现继承

  • 核心语句:extends、super
对于子类来说 
      1 如果你写了  extends 而且 还写 constructor 
        那么在我们的 constructor 必须要调用super 固定语法!! 
class Person {
            constructor(name,height){
                this.name = name;
                this.height = height;
            }
            fly(){
                console.log('我飞');
            }
        }
        class YellowPerson extends Person{
            constructor(name,height,nickname){
                super(name,height);// 调用父亲的构造器 给儿子 添加属性 
                this.nickname = nickname;
            }
        }
        const person = new Person('123',150,"hh");
        console.log(person);

      2 为什么 之前只写  extends 不用写super-没有写 constructor
  
	class Person {
        name = '父亲';
        say() {
          console.log(this.name, '父亲的say方法');
        }
      }

      // 直接就实现了 继承父亲的属性和方法 !!!!
      class YellowPerson extends Person {}
      // 儿子实例 拥有 父亲 name属性和父亲的 say方法
      const yp = new YellowPerson();
      console.log(yp);
      

  • 效果:image-20220516203721531

检测数据类型

  • 基本的数据类型 typeof

  • 引用数据类 intanceof

  • 检测 你是不是 你爸爸亲生!! 检测 这个实例是不是被某个构造函数 new 出来

class Person {}
      class SuperPerson{}
      const p1 = new Person();
      const s1 = new SuperPerson();

      console.log(p1 instanceof Person);//  true
      console.log(p1 instanceof SuperPerson); // false 

this指向

全局函数的this的指向

  • 常态:一般来说 this的指向 判断依据 谁调用了 函数 this 指向谁

  • 其实 当我们定义了全局的函数的时候,默认是挂载到了window 全局变量上

  • 全局函数 其实就是 window的一个属性而已 只不过 平时写代码的时候 可以省略这个window

//通常采用缩写形式 
    function abc(){
            console.log(this);
        }
    abc()//window

//展开形式
        window.abc = function(){
            console.log(this);
        }
        window.abc()//window

小结
      1 当我们定义全局的函数的时候 本质是给window添加了一个 属性(函数类型)
      2 当我们调用这个函数 的时候  abc()  本质  window.abc() -- window 可以被省略而已  

箭头函数没有this

  • 箭头函数没有内部的this
  • 当你的

修改箭头函数的指向

用于封装复杂代码,一般的业务用不到,但是面试常问

  • bind
  • call
  • apply

原型继承

优点

缺点:

prototype是一个对象,是一个复杂数据类型,直接写等于的话,两个原型指向同一个地址,会出现:儿子改,父亲改变

原型链

  • 只是一种代码的组织形式而已

  • 可以通过原型对象prototype将多个对象关联起来 父子结构 看div的dom元素原型

posted @   莐亓  阅读(276)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示