菜狗学前端之JS高级笔记

老样子。复制上来的图片都没了,想看原版可以移步对应资源下载(资源刚上传,还在审核中)

(免费) JS高级笔记icon-default.png?t=N7T8https://download.csdn.net/download/m0_58355897/89102910

一些前提概念

一 什么是js高级

  •  js高级是对js基础语法的一个补充说明,本质上还是对ECMAScript语法的进阶与延伸
  • 高级所要学习的语法相比而言没有js基础的多,但是会更加晦涩难懂,更加侧重的是理解

        

二 什么是面向对象编程

  • 面向对象不是一门技术,而是一种解决问题的思维方式
  • 面向对象的本质是对面向过程的一种封装

一 代码段和作用域

(一)代码段

1.什么是代码段

  • 一个script就是一个代码段
  • 一个js文件也是一个代码段

PS:代码块:{}之间的就是代码块

2.代码段特点

  1. 上面代码段定义的变量 下面的代码段可以使用
  1. 下面代码段定义的变量 上面的代码段不可以使用(因为代码从上向下执行)
  1. 代码段之间互不影响 一个代码段中的错误 不会影响到另一个代码段
  1. 一个html页面中,可以存在多个代码段

(二)作用域

1.作用域

a.分类

  • 全局变量(直接变量名、function函数体外的包括if/for中的var变量名)的作用范围是全局
  • 局部变量(function函数体中用var声明的变量、function函数的形参)的作用范围是局部

b.主要作用

隔离变量,不同作用域下同名变量不会有冲突。

2.作用域链

寻找变量的过程 叫作用域链 链式过程

(就近原则--从内向外地冒泡寻找

以下图的代码为例,看下作用域链上查找一个变量的查找规则:

  1. 首先,在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
  1. 然后,在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
  1. 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常

二 预解析

(一)预解析概念

 浏览器不会直接执行代码,而是加工处理后再执行,这个加工处理的过程我们称之为预解析

(二)预解析期间操作

1.提升操作

  1. 加var的变量仅声明提升
  • (函数内部的局部变量,提升到函数体的最前面)
  • (函数外部的全局变量,提升到代码段最前面)
  1. function函数声明+赋值提升
  • (函数内部的子函数,提升到函数体的最前面)
  • (非函数内部的子函数,提升到代码段的最前面)

2.代码举例

JavaScript
        // 预解析前
        var a = 1;
        function fn() {
            console.log(a);//und
            var a = 10
        }
        fn()
        console.log(a);//1

JavaScript
        // 预解析后
        var a//全局变量
        function fn() {
            var a//局部变量
            console.log(a);//und
            a = 10
        }
        a = 1
        fn()
        console.log(a);//1

(三)提升的特殊情况

1.函数表达式的变量提升

 var gn = function () {
            console.log('函数');
        }

函数表达式:提升的是var声明的变量,而不是后面的function(){}

        // 如果碰到xxx is not a function

        // 思考自己调用的位置是变量还是函数

  • 预编译前

JavaScript
        gn()
        var gn = function () {
            console.log('函数');
        }

  • 预编译后

JavaScript
        var gn;
        gn() //und()  gn is not a function
        gn = function () {
            console.log('函数');
        }

2.for循环条件中var i变量的提升

for (var i = 0; i < 10; i++) {}

for循环:虽然不是函数 但里面var声明的变量依旧会提升

  • 预编译前

JavaScript
        console.log(i);//undefined
        for (var i = 0; i < 10; i++) {
            console.log(i);
        }

  • 预编译后

JavaScript
        var i;
        console.log(i);//undefined
        for (i = 0; i < 10; i++) {
            console.log(i);
        }

3.var声明的变量名和function声明的函数名冲突时的提升

当我们使用var声明的变量名和function声明的函数名 产生冲突的时候

函数的优先级高于var声明的变量 函数属于一等公民

总结:

声明冲突时无论先后函数优先(调用的皆是函数),直至变量赋值后同名函数才被同名变量覆盖(函数无法再被调用)

  • 预编译前

JavaScript
        console.log(value);
        var value = 111;
        function value() {
            console.log('我是一个函数');
        }
        console.log(value);

  • 预编译后

JavaScript
        var value;
        function value() {
            console.log('我是一个函数');
        }
        console.log(value); //函数体  //函数与变量同名,函数不会被仅声明的变量覆盖
        value = 111;//变量赋值
        console.log(value);//111  //函数被变量覆盖
        value()//报错 value已经不是函数

4.没有var声明直接不提升

报错:a is not defined

JavaScript
        console.log(a);//报错  Uncaught ReferenceError: a is not defined
        a = 100;
        console.log(a); //上面代码报错,代码停止执行

5.let变量声明提升但未初始化

报错:Cannot access 'a' before initialization 初始化

JavaScript
    console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization 初始化
    let a=1;
     console.log(a); //上面代码报错,代码停止执行

三 执行上下文

(一)内存的分区

1.数据类型

对内存来说,能够合理利用存储空间

  •  基本数据类型 Number String Boolean Null Undefined
  • 引用数据类型 Object Array Function

2.内存分区:堆区 栈区

  • 基本数据类型 存放在栈区(先进后出)
  • 引用数据类型 存放在堆区 堆空间与一个对应的地址 对应的地址仍存在栈区
  • GO 全局对象
  • VO 变量对象 用来存放当前执行上下文中创建的数据

(二)执行上下文

  • 当代码运行时,会产生一个对应的执行环境
  • 在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 undefined(预解析)
  • 代码从上往下开始执行,就叫做执行上下文。

1.执行上下文概念

执行上下文就是js代码执行环境的抽象概念理解

只要代码运行,就一定在执行上下文中

2.执行上下文过程

代码被浏览器解析后,形成执行上下文ECS

入栈

1)当全局代码执行 产生ECG(鹅蛋)

2)当函数执行的时候 产生ECfn(鸡蛋)

3)ECG 或者 ECfn 放在ECS中,ECS是执行上下文栈

出栈

4)当函数调用完毕之后,就出栈(内存释放)

5)当全局代码没有被引用,就出栈

3.画图分析举例

四 声明变量 var|let|const

(一)加var与不加var的区别

  1. 不管加不加var,创建的是全局变量,都会放在GO中,也就是说可以通过window.xx
  1. 加var的变量可能是全局变量, 也可能是局部变量, 通过是否写在函数内区分
  1. 局部变量不会挂载在window上
  1. 加var的变量在预解析的时候会提升, 不加var不会提升
  1. 加var的变量不能用delete()方法删除,不加var能

(二)let声明变量(ES6)

1.产生原因

之前使用var声明, 提升会打乱我们代码的执行顺序

2.特点

  1. let声明的变量不会提升(提升了但是没有初始化)
  1. let会和{} 形成块级作用域
  1. 使用let声明的变量不会挂载在window上
  1. 使用let声明的变量不能重复声明

3.代码举例

JavaScript
        //情况1:
        console.log(a);//Cannot access 'a' before initialization
        let a = 1;

        //情况2:
        if (true) {
            let a = 1;
            console.log(a);//1  和{}形成块级作用域
        }
        console.log(a);//a is not defined

        //情况3:
        let a2 = 1;
        console.log(window);
        console.log(window.a2);

        //情况4:
        var a;
        var a;

        let a;
        let a;

(三)const声明常量(ES6)

1.特点

  1. const声明的变量不会提升
  1. 和{}会形成块级作用域
  1. 不会挂载window上
  1. 声明的变量不能修改(常量)
  1. const声明的变量必须赋值,只声明不赋值直接报错

2.代码举例

JavaScript
        //情况1:
        const a = 100;
        console.log(a);//100
        a = 110;
        console.log(a);//对常量变量赋值 Assignment to constant variable.

        //情况2:
        console.log(b);//Cannot access 'b' before initialization
        const b = 120;

        //情况3:
        var c;
        let c;//
        const c;//

        // 情况4:
        const a1 = 100
        console.log(window);

五 高阶函数

(一)高阶函数定义

一个函数它的参数是函数或者它的返回值是函数,那么这个函数叫高阶函数

(二)常见的内置高阶函数

数组的一些方法是参数为函数的高阶函数:

  • some() 一真全真
  • every() 一假全假

JavaScript
//some:
        let arr = [10, 20, 30, 40, 52];
        let flag = arr.some(item => item < 51);
        console.log(flag); //输出结果true
 
//every:
        let arr = [11, 21, 31, 42, 51];
        let flag = arr.every(item => item < 51);
        console.log(flag); //输出结果false

  • forEach()
  • map()
  • filter()
  • find()
  • findIndex()

JavaScript
        var arr = [1, 5, 2, 3]

        // 1.arr.forEach()
        // 作用:遍历数组  不能改变原数组
        // 注意:不能对空数组进行检测

        // 2.map
        // 作用:映射/加工   返回值是一个根据映射条件加工后的新数组,不能改变原数组
        // 语法:
        // arr.map(function (item, index, arr) {

        // })

        let res = arr.map(function (item, index, arr) {
            // item:当前元素的值
            // index:索引值
            // arr:当前数组(可选参数)
            return item + "hello"
        })
        console.log(arr);
        console.log(res);

        // 3.filter
        // 作用:过滤  把符合条件的元素放在新数组中,返回的新数组
        // return 后面是条件 条件为true的时候会返回到新数组当中
        let res2 = arr.filter(function (item, index) {
            // return item > 2
            // return 10  // 10-true
            return 0  // 0-false
        })
        console.log(res2);//[5,3]
        console.log(arr);

        // 4.find
        // 作用:查找 把符合条件的元素放在新数组中,返回的新数组
        // 如果数组中有对应的元素则返回元素,如果没有则返回undefined
        let res3 = arr.find(function (item, index) {
            // return item == 2
            return item == 0 //undefined
        })
        console.log(arr);
        console.log(res3);

        // 5.findIndex
        // 作用:查找数组中是否有某一个元素的索引
        // 如果数组中有对应的元素则返回元素的索引值,如果没有则返回-1
        let res4 = arr.findIndex(function (item, index) {
            // return item == 2
            // return item == 1 //0
            return 0 //-1

        })
        console.log(res4);

(三)自定义高阶函数(一般不用)

JavaScript
        // 对于高阶函数来说 ,实际上传递的是函数的堆地址
        function fn(func) {
            func()
        }
        function gn() {
            console.log('gn..');
        }
        fn(gn)

JavaScript
        // 需要封装一个函数
        // 三个参数 这个函数的作用是计算  加 减 乘
        function calc(num1, num2, func) {
            return func(num1, num2)

        }
        // 相加
        function add(a, b) {
            return a + b
        }
        // 相减
        function sub(a, b) {
            return a - b
        }
        // 相乘
        function mul(a, b) {
            return a * b
        }
        console.log(calc(3, 4, add));
        console.log(calc(30, 40, sub));

六 IIFE 立即执行函数表达式

立即调用函数表达式(immediately invoked function expression);

主要作用: 立即执行函数 

(一)两种不同写法

JavaScript
     (function fn1(形参) {
         console.log('fn1');
     })(实参)

JavaScript
    (function fn1(形参) {
        console.log(';fn1');
    }(实参))

(二)前面没加;之类会报错

可加: + - * / ! ~ ^

JavaScript
        ;(function fn1() {
            console.log(';fn1');
        }())
        +function fn1() {
            console.log('+fn1');
        }()
        -(function fn1() {
            console.log('-fn1');
        })()
        *(function fn1() {
            console.log('*fn1');
        })()
        /(function fn1() {
            console.log('/fn1');
        })()
        ~(function fn1() {
            console.log('~fn1');
        })()
        ^(function fn1() {
            console.log('^fn1');
        })()
        !(function fn1() {
            console.log('!fn1');
        })()

七 闭包

(一)概念和理解

函数执行后返回结果是一个内部函数,并被外部变量所引用,

如果内部函数持有被执行函数作用域的变量,即形成了闭包。

  • 一个本该被销毁的变量空间
  • 由于其内部对其引用, 导致无法被销毁, 形成闭包

(二)特点及缺点

1.特点

  1. 延长了变量的生命周期
  1. 扩大了变量的作用范围
  1. 保护变量

个人理解 需注意的tip:

可产生多个闭包,每个闭包各自独立

同一闭包下的变量固定,连续调用闭包函数其中的变量值会累积变化而非重置

2.缺点

  • 导致内存泄漏

(如果写了大量的闭包,产生了多个独立空间但是没有及时销毁,导致内存泄漏)

(三)代码举例

1.注意变量会累积变化,因为同一闭包中的变量被函数连续调用

JavaScript
        function fn1(a) {
            function gn() {
                console.log(++a);
            }
            return gn
        }

        //注意变量会累积变化,因为同一闭包中的变量被函数连续调用
        //除非重新函数赋值再调用才会重新开始,新闭包,新闭包中的变量
        let fn2 = fn1(1)//将a赋值为gnfunction gn() {console.log(++a);}  a=1
        fn2()//2 第一次调用gn()
        fn2()//3 第二次调用gn()
        
        let fn3 = fn1(1)//将a赋值为gnfunction gn() {console.log(++a);}  a=1
        fn3()//2 第一次调用gn()
        fn3()//3 第二次调用gn()

2.不管在哪里调用闭包函数,那么其中所需变量一定是闭包中的变量

(闭包中的i仅服务于y函数)

JavaScript
        var i = 0;
        function A() {
            var i = 10;
            function X() {
                console.log(i);
            }
            return X
        }
        var y = A();//此时 这个y实际上是函数x, 因为x函数内部的i使用的是定义出来的i, 所以形成闭包
        y()//10

        function B() {
            var i = 20;
            y()//不管在哪里调用y函数,那么这个i一定是闭包中的i(闭包中的i仅服务于y函数)
        }
        B()//10

八 this绑定

(一)this概念及this绑定分类

1.概念

  • this在不同的位置 指的是不用的东西
  • this指向的东西  和位置以及调用方式有关系
  • 常见的this指向的是对象 或者是window 或者是其他事件源

2.分类

  1. 默认绑定;
  1. 隐式绑定;
  1. 显式绑定;
  1. new绑定;

(二)默认绑定

默认绑定的this值:window对象

  1. 直接this
  1. 函数独立调用时里面的this

JavaScript
        //1.直接this
        console.log(this);//window

        //2.当一个函数独立调用时,此时里面的this是默认绑定,指向是window
        function fn() {
            console.log(this);
        }
        fn()

(三)隐式绑定

隐式绑定的this值:调用方法的对象

  • 隐式绑定 实际上就是对象打点调用身上的方法
  • 谁打点调用 this就指向谁

两种写法

JavaScript
        // 写法一:
        var obj = {
            name: "wang",
            fn: function () {
                // console.log('fn...');
                console.log(this); //obj
            }
        }
        obj.fn()//obj

JavaScript
        // 写法二:
        var obj = {
            name: "wang",
            // fn: fn
            // key和value的名字一样是,可以省略写一个
            fn
        }
        function fn() {
            console.log(this);
        }
        obj.fn()//obj

(四)显式绑定call() apply() bind()

显式绑定 主要是三个函数call() apply() bind()        对象身上的内置方法

  • 一次性

1.call() 改变this指向且调用函数

2.apply() 与call()类似 但传参写法不同加[]

  • 可永久

3.bind() 仅改变this指向但不调用 调用的话需要再()

PS:只有apply传参需要加[],call和bind方法不需要

JavaScript
        function fn(a, b) {
            this.a = a
            this.b = b
            console.log(this);
        }

//1.call() 作用:1.改变this指向   2.让函数执行
        let call = {
            name: 'call',
            a: 0,
            b: 0
        }
        fn.call(call, 1, 2)]//执行了fn函数,将函数中this指向了obj
        fn()//window  不是永久改变,只改变一次
        
//2.apply() 作用:1.改变this指向   2.让函数执行
        let apply = {
            name: 'apply',
            a: 0,
            b: 0
        }
        fn.apply(apply, [1, 2])//和call使用方式一样 传参方式不同
        fn()//window  不是永久改变,只改变一次
        
//3.bind() 作用:1.改变this指向
        let bind = {
            name: 'bind',
            a: 0,
            b: 0
        }
        let bind2 = fn.bind(bind, 1, 2)//会返回一个新的绑定好的this指向的函数
        bind2()//永久指向 bind2里面的this永久指向bind

(五)new绑定

1.概念

  • 通过new一个构造器(类),生成一个实例化对象
  • new一个类,就得到一个对象
  • 对于构造器来说 里面的this默认绑定的是window
  • 当我们使用new的时候 里面的this指向发生了改变 指向是生成的实例化对象

2.new(运算符/操作符做了什么事情)

为什么要使用new呢?

因为new可以改变this指向,并且执行构造器

  1. 创建出了一个实例化对象(开辟了一个新的堆空间)
  1. 让构造器中this指向新的实例化对象(指向了新的堆空间)
  1. 执行了构造函数
  1. 返回的是一个堆空间的地址,把地址给了car

3.代码举例

JavaScript
        // 1.构造器
        function Car(carName, carColor) {
            this.carName = carName
            this.carColor = carColor
            console.log(this);
        }

        Car('车', '绿色')//window--'车',"绿色"
        
        // 2.通过new构造器,得到一个实例化对象
        //也算是谁=new 构造器,this指向谁(类似于隐式绑定的谁调用this指向谁)
        let car = new Car('车', "蓝色")//car这个对象--'车',"蓝色"
        console.log(car);//car这个对象--'车',"蓝色"

(六)this绑定的优先级

  • this绑定优先级:new绑定>显示绑定>隐式绑定>默认绑定

(七)this指向特殊情况

1.内置函数的this指向

a.事件监听器中的this指向事件源

JavaScript
        // 1.事件监听器中的this指向事件源
        var divEle = document.querySelector('div')
        divEle.onclick = function () {
            console.log(this);//<div>this指向</div> 也就是<div>this指向</div>
        }

b.定时器里的this指向window(无论定时器放在哪都是这样)

JavaScript
        // 2.定时器里的this指向window(无论定时器放在哪都是这样)
        var obj = {
            fn: function () {
                console.log(this, '这是定时器外面的this');//obj 隐式绑定 Object
                setTimeout(function () {
                    console.log(this, '这是定时器里面的this');//Window
                }, 1000)
            }
        }
        obj.fn()

c.forEach里的this默认window;添加第二个参数时,指向它

JavaScript
        // 3.forEach
        // 默认Window
        // 添加第二个参数时,改变第一个参数的this
        var arr = [1, 2, 3]
        // 默认
        arr.forEach(function () {
            console.log(this);//window
        })
        // 添加第二个参数时,改变第一个参数的this
        arr.forEach(function (item) {
            if (item == 2) {
                this.age = 2
            }
            console.log(this);//Window --> { name: "张三",age:2}
        }, { name: '张三' })

2.箭头函数无this指向 逐级向上查找

  • 箭头函数需要逐级向上找this

JavaScript
        // 箭头函数中没有this,需要逐级向上找this
        console.log(this);//window

        var obj = {
            fn: function () {
                console.log(this);//obj
            },
            gn: () => {
                console.log(this);//window
            }
        }

        obj.fn()//obj
        obj.gn()//window

  • 箭头函数中没有this

JavaScript
        // 没有办法改变,因为箭头函数中没有this
        let fn = () => {
            console.log(this);//window
        }
        fn.call('malu')//window

九 创建对象的方式

        创建对象方式总结:

        1.使用字面量的方式直接创建对象

        2.使用new关键字:使用new关键字通过构造函数创建对象

        3.使用工厂模式:可根据参数返回不同的对象

        4.使用原型模式:使用一个原型模式创建新的对象

        5.使用Object.create方法

        6.使用class关键字:ES6提供的class关键字

        ps:遍历对象只能用 for(var key in arr){}  不能用for of哦

(一) 字面量方式

JavaScript
        let obj = {
            name: '名字',
            fn: function () {
                console.log('字面量创建对象');
            }
        }
        console.log(obj);

(二) 构造函数方式(首字母需要大写)

1.内置Object对象构造器

JavaScript
        //内置对象构造器
        var obj1 = new Object({ name: '名字', age: 18 })
        console.log(obj1);//Object age: 18 name: "名字"

2.自定义构造器/类

JavaScript
        // 自定义Student 构造器/类 注意:首字母需要大写
        function Student(sname, sage) {
            this.sname = sname
            this.sage = sage
        }
        // new 构造器 构建出来一个实例化对象
        let obj2 = new Student('学生', 18)
        console.log(obj2);//Student sage: 18 sname: "学生"

(三) 工厂函数方式

  • 在函数中声明局部对象变量,并进行属性赋值,最后返回该对象变量

JavaScript
        function Student(name, age) {
            var obj = {}
            obj.name = name
            obj.age = age
            obj.dohomework = function () {
                console.log('写作业..');
            }
            return obj
        }

        let stu1 = new Student('花花', 12)
        let stu2 = new Student('花', 18)
        console.log(stu1);
        console.log(stu2);
        stu1.dohomework()

十 构造器|原型对象|实例化对象相关

(一) 原型|原型模式|原型对象|原型链概念

  • 原型

一个可以被复制的类,通过复制原型,可以创建出来一个一摸一样的对象

也就是说 原型就是一个模板

  • 原型模式(一种设计模式)

原型模式指的是用于重复创建的对象,既可以重复创建又能保证性能

  • 原型对象

是抽象的 是一个内存中的空间

一个对象的诞生=构造器+原型对象(底层)

  • 原型链

当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。

(二) 构造器、原型对象、实例化对象及原型链

1.构造器、原型对象、实例化对象关系

  • 在构造器身上有一个prototype属性,指向构造器的原型对象(显示原型对象)
  • 在原型对象上有一个属性叫constructor,指向这个原型对象对应的构造器
  • 每一个生成的实例化对象都一个属性__proto__,指向对应的原型对象(隐式原型对象)

JavaScript
       function Student(name) {
            this.name = name
        }
        //打印构造器对象Student
        console.dir(Student);//f Student(name)

        // 通过prototype
        // 打印构造器对象Student的显式原型对象
        console.dir(Student.prototype)//Object
        console.dir(Student.prototype.constructor)//ƒ Student(name)
        console.dir(Student.prototype.prototype)//undefined
        
        // 比较两种方式获得的原型对象
        console.log(Student.prototype === stu1.__proto__);//true

PS:console.dir()打印对象

2.原型链(沿之向上寻找)

寻找对象的属性(方法)的机制,就是沿着__proto__形成的链式结构向上寻找

(自己身上没有此属性 -> 原型对象 -> Object -> null)

JavaScript
        let stu1 = new Student('张三')
        //打印实例化对象stu1
        console.dir(stu1)//Student
        
        // 原型链:寻找属性的机制,就是沿着__proto__形成的链式结构寻找(自己身上没有此属性 -> 原型对象 -> null)
        //1.打印实例化对象的隐式原型对象
        console.dir(stu1.__proto__.constructor)//ƒ Student(name)
        //2.Object的原型对象
        console.dir(stu1.__proto__.__proto__)//Object(原始真正的Object)
        //3.null
        console.dir(stu1.__proto__.__proto__.__proto__)//null

3.对象的私有属性和公有属性

  • 实例化对象自己身上的 叫私有属性
  • 原型对象身上 叫公有属性

JavaScript
        // 共有属性age
        Student.prototype.age = 18
        // 私有属性name
        let stu2 = new Student('李四')
        console.log(stu2.name);//李四
        console.log(stu2.age);//18

(三) 一些实例情况

1.__proto__改变--堆地址不同

2.prototype改变--旧原型对象依然是原型链一环

3.实例化对象调用VS原型对象调用

PS:undefined类型转number类型是NaN,而NaN+任何数都为NaN

4.实例化对象没有某属性沿原型链向上查找

十一 继承

(一) 继承相关概念

继承,可以让多个构造函数产生关联,便于管理和复用

所谓的继承 就是子类继承父类的属性和方法

举例:教师类 教导主任 老师

父类的范围大一些 子类的范围小一些 子类的属性多一点

不使用继承时:

JavaScript
        //学生类
        function Student(name, age) {
            this.name = name
            this.age = age
        }

        // 班长类
        function Mointer(name, age, m_id) {
            //重复写,冗余
            this.name = name
            this.age = age
            this.m_id = m_id
        }

(二)私有属性继承--call继承

  • 优点 可以继承私有属性
  • 缺点 没有继承公有属性

            // 核心代码--改变this指向并执行函数

            Father.call(this, name, age)

JavaScript
        function Father(name, age) {
            //私有属性
            this.name = name
            this.age = age
        }

        function Son(name, age, father) {
            // 核心代码--改变this指向并执行函数
            Father.call(this, name, age)
            // 当我们 new Son的时候
            // 此时当前位置的this指向的是新的实例化对象
            this.father = father
        }

        let son = new Son('小花', 18, '爹')
        console.log(son);//Son age: 18 father: "爹" name: "小花"

(三)公有属性继承

1.原型继承

原型继承 对于私有属性来说 没什么意义,对于公有属性来说 可以继承

        // 核心代码--原型继承

        Son.prototype = new Father()

        Son.prototype.constructor = Son//可写可不写 没大影响

沿原型链查找.prototype.prototype.say

JavaScript
       function Father(name, age) {
            this.name = name
            this.age = age
        }

        Father.prototype.say = function () {
            console.log('say...');
        }

        function Son(name, age, father) {
            // this.name = name
            // this.age = age
            this.father = father
        }

        // 核心代码--原型继承
        Son.prototype = new Father()
        Son.prototype.constructor = Son

        let son=new Son('小花',18,'爹')
        console.log(son);//Son father: "爹"
        son.say()//say... 沿原型链查找

2.Object.create() 创建一个对象,可指定对象的原型对象->能用于继承公有属性

 Object 是一个内置构造器 有一个方法create  Object.create

作用:创建一个对象,并且可以指定对象的原型对象

//let res = Object.create(obj)

//核心代码 Son.prototype = Object.create(Father.prototype)

可以指定对象的原型对象

JavaScript
        let obj = {
            name: '张三',
            say:function(){
                console.log('say...');
            }
        }

        // 想把谁当成原型对象,在参数里面就写谁
        let res = Object.create(obj)
        console.log(res);//Object prototype name: '张三' say:function()
        res.say()//say... 沿原型链查找

        // 可以定义一个没有原型对象的对象
        let res2 = Object.create(null)
        console.log(res2);//Object no properties

用于公有属性继承:沿原型链查找.prototype.prototype.say

JavaScript
        function Father(name, age) {
            this.name = name
            this.age = age
        }
        //公有方法
        Father.prototype.say = function () {
            console.log('say...');
        }

        function Son(name, age, father) {
            this.father = father
        }
        //通过Object.create继承公有属性
        Son.prototype = Object.create(Father.prototype)
        let son = new Son('小花', 18, '爹')
        console.log(son);//Object father: "爹" prototype Prototype say:function()
        son.say()//say... 沿原型链查找

(四)公+私有属性继承

1.组合继承=call+原型继承

        function Son(name, age, father) {
            // call继承私有属性
            Father.call(this, name, age)
            this.father = father
        }
        // 原型继承公有方法
        Son.prototype = new Father()

沿原型链查找.prototype.prototype.say

JavaScript
        function Father(name, age) {
            // 私有属性
            this.name = name
            this.age = age
        }

        //在父类的原型身上添加了一个公有方法
        Father.prototype.say = function () {
            console.log('say...');
        }

        function Son(name, age, father) {
            // call继承私有属性
            Father.call(this, name, age)
            this.father = father
        }
        // 原型继承公有方法
        Son.prototype = new Father()
        // Son.prototype.constructor = Son

        let son = new Son('小花', 18, '爹')
        console.log(son);//私有属性
        son.say()//公有方法

2.寄生继承=call+Object.create()

        function Son(name, age, father) {
            // call继承私有属性
            Father.call(this, name, age)
            this.father = father
        }

        //通过Object.create继承公有属性
        Son.prototype = Object.create(Father.prototype)

沿原型链查找.prototype.prototype.say

JavaScript
        function Father(name, age) {
            //私有属性
            this.name = name
            this.age = age
        }
        //公有方法
        Father.prototype.say = function () {
            console.log('say...');
        }

        function Son(name, age, father) {
            // call继承私有属性
            Father.call(this, name, age)
            this.father = father
        }
        //通过Object.create继承公有属性
        Son.prototype = Object.create(Father.prototype)
        // Son.prototype.constructor = Son
        let son = new Son('小花', 18, '爹')
        console.log(son);//私有属性
        son.say()//公有方法

十二 ES6中声明类及继承

(一)声明类class

class Xxx{

constructor(xx,xx,...){

// 这里的属性是会挂载在实例化对象上

// 私有属性/方法

this.xx=xx

this.xx=function(){}

...

}

// 在constructor外面写,会挂载在原型对象上

           // 公有属性/方法

xxx=xxx//这里属性仍绑定到实例化对象身上,私有属性

xxx(){}

}

JavaScript
        class Student {
            // 在class声明的这个类中,有一个函数叫constructor
            constructor(name, age) {
                // 这里的属性是会挂载在实例化对象上
                // 私有属性/方法
                this.name = name
                this.age = age
                this.work = function () {
                    console.log('work...');
                }
            }
            // 在constructor外面写,just方法会挂载在原型对象上
            // 私有属性
            a = 1
            // 公有方法
            say() {
                console.log('say...');
            }
        }
        let stu1 = new Student('张三', 18)
        console.log(stu1);
        stu1.work()//work...
        stu1.say()//say...

(二)继承类extends

父类Student

JavaScript
        class Student {
            constructor(name, age) {
                // 私有属性/方法
                this.name = name
                this.age = age
                this.work = function () {
                    console.log('work...');
                }
            }
            // 私有属性
            a = 1
            // 公有方法
            say() {
                console.log('say...')
            }
        }

子类Montor

  1. 不写constructor(xx){super(xx)}时也继承父类

JavaScript
        class Montor extends Student {
            // 1.不写constructor时也继承父类
            // 子类私有属性
            b = 2
            // 子类公有方法
            talk() {
                console.log('talk...')
        }

        let mon = new Monitor('班长', 20, 1)
        console.log(mon);
        mon.work()//work...
        mon.say()//say...
        mon.talk()//talk...

  1. 新增自己属性/方法时写constructor须用super()继承父类

粉-继承父类;绿-子类新增

JavaScript
        class Monitor extends Student {
            // 2.新增自己属性/方法时写constructor用super()
             constructor(name, age, id) {
                //继承父类
                super(name, age)
                //新增子类私有属性
                this.id = id
            }
            // 子类私有属性
            b = 2
            // 子类公有方法
            talk() {
                console.log('talk...')
            }
        }

        let mon = new Monitor('班长', 20, 1)
        console.log(mon);
        mon.work()//work...
        mon.say()//say...
        mon.talk()//talk...

十三 JSON的数据格式及转换

(一)JSON及其数据格式

JSON   作为前端和后端的数据交互的一种格式

JSON数据格式:

        格式1: "hello"  需要使用双引号包裹起来

        格式2: 写成对象的时候 属性名必须添加双引号 如果嵌套对象的时候 嵌套对象也需要使用双引号包裹

        格式3: 写成数组的时候 ,当元素是纯数字 可以不添加引号 其余都要添加

  1. 字符串

JSON
"hello"

  1. 对象

JSON
{
    "name": "小花",
    "hobby": [
        "踢足球",
        "打篮球"
    ],
    "age": 18
}

  1. 数组

JSON
[
    "小花",
    18,
    {
        "name": "小花花"
    },
    [
        1,
        2
    ]
]

(二)JSON的序列化和反序列化

 JSON的序列化 JSON.stringify()--对象变字符串

 JSON的反序列化 JSON.parse()--字符串变对象

JavaScript
        let obj = {
            name: "小花",
            age: 18,
            score: [1, 2, 3]
        }
        // JSON的序列化 JSON.stringify()
        console.log(JSON.stringify(obj));//{"name":"小花","age":18,"score":[1,2,3]}
        // JSON的反序列化 JSON.parse()
        console.log(JSON.parse(JSON.stringify(obj)));//Object age: 18...

十四 JS中的错误类型及处理

(一)JS中的错误类型

总结5种错误情况:

        1.xx is not defined  未声明直接使用

        2.let 不能再初始化之前使用 Cannot access 'a' before initialization

        3.xx is not a function   当你调用函数的时候()前面不是一个函数

        4.const声明的变量不能被改变 Assignment to constant variable

        5.超出最大执行栈  Maximum call stack size exceeded

JavaScript
        // 1.未声明直接使用
        // Uncaught ReferenceError: a is not defined
        console.log(a);

        // 2.let在初始化之前使用
        // Uncaught ReferenceError: Cannot access 'b' before initialization
        console.log(b);
        let b

        // 3.const不能二次赋值
        // Uncaught TypeError: Assignment to constant variable.
        const c = 10
        c = 11

        // 4.调用函数的时候()前面不是一个函数
        // Uncaught TypeError: fn is not a function
        let fn=1
        fn()

        // 5.超出最大执行栈(如陷入死循环)
        // Uncaught RangeError: Maximum call stack size exceeded
        function f1() {
            f2()
        }
        function f2() {
            f1()
        }
        f1()

(二)try catch处理错误

处理错误:(为了使错误不阻塞后面的代码执行)

try catch 捕获错误 try和catch须连用

        1.默认错误try{}catch(err){console.log(err)}

        2.自定义错误类型try{throw xxx}catch(err){console.log(err)}

1.抛出错误并捕获

JavaScript
        // 抛出默认错误
        try {
            console.log(a);//会产生错误的代码
        } catch (err) {
            console.log(err);//ReferenceError: a is not defined 以字符串的方式抛出
        }
         console.log("try-catch处理异常后报错不影响后面代码执行");//try-catch处理错误后报错不影响后面代码执行

2.抛出自定义错误(throw)并捕获

JavaScript
        try {
            let age = "hhh"
            if (typeof age != "number") {
                // throw 抛出自定义错误
                throw "请输入数字"
            }
        } catch (err) {
            console.log(err);//请输入数字
        }
         console.log("try-catch处理异常后报错不影响后面代码执行");//try-catch处理错误后报错不影响后面代码执行

十五 其他(严格模式等)

(一)用class封装数据存储的工具

JavaScript
class Cache {
    constructor(sort) {
        this.storage = (sort === localStorage) ? localStorage : sessionStorage
    }
    // 增|改
    setItem(key, value) {
        if (value) {
            this.storage.setItem(key, JSON.stringify(value));
        }
    }
    // 查
    getItem(key) {
        let value = this.storage.getItem(key);
        if (value) {
            return JSON.parse(value);
        } else {
            return "仓库中没有";
        }
    }
    // 删
    removeItem(key) {
        if (key) {
            this.storage.removeItem(key);
        }
    }
    // 清空所有
    clear() {
        this.storage.clear()
    }
}

let localCache = new Cache(localStorage)
let sessionCache = new Cache(sessionStorage)

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./cache.js"></script>
</head>

<body>
    <script>
        localCache.setItem("stu", { name: "小花", age: 18 })
        localCache.setItem("stu2", { name: "小花", age: 18 })
        console.log(localCache.getItem("stu"));
        localCache.removeItem("stu2")
        // localCache.clear()

        sessionCache.setItem("stu", { name: "小花", age: 18 })
        sessionCache.setItem("stu2", { name: "小花", age: 18 })
        console.log(sessionCache.getItem("stu"));
        sessionCache.removeItem("stu2")
        // sessionCache.clear()
    </script>
</body>

</html>

(二)with和eval和严格模式

1.with--扩展一个语句的作用域链

with语句 作用:扩展一个语句的作用域链

  • 优点:可以让代码更加简洁(不需要打点查找)   
  • 缺点:破坏了作用域

PS:当传入复杂对象时,相较连续多次打点,with可以使效率提高  

JavaScript
        //不使用with
        var obj1 = {
            name1: "张三",
            obj2: {
                name2: "李四"
            }
        }
        console.log(obj1.obj2.name2);
        console.log(obj1.obj2.name2);
        console.log(obj1.obj2.name2);

        //使用with
        with (obj1.obj2) {
            var newName = name2
        }
        console.log(newName);
        console.log(newName);
        console.log(newName);

2.eval--字符串转js

eval-比较特殊的函数 可以把字符串当成js代码去执行

作用:是一种接受字符串作为参数,并且可以将接受的字符参数转变成js的表达式且立即执行函数

注意:eval的参数是一个字符串

JavaScript
        var str='var msg="哈哈哈";console.log(msg)'
        console.log(str);//var msg="哈哈哈";console.log(msg)
        eval(str)//哈哈哈

3.严格模式--消除js中某些不合理或者不严谨的地方

严格模式:消除js中某些不合理或者不严谨的地方

作用:提高js的程序运行效率 为以后的js版本做铺垫

在一个js文件中开启"use strict" 这个文件中写的代码都受严格模式的约束

严格模式时不允许:

  1. 不能使用不加var或者let的变量
  1. 形参的参数不能重名
  1. 在严格模式下,函数的独立调用不再默认绑定(即this不再指向window)

JavaScript
        'use strict'
        // 1.不能使用不加var或者let的变量
        // b=1;
        var b = 1
        let c = 2
        console.log(b);
        console.log(c);

        // 2.形参的参数不能重名
        // function fn(a, a, a) {
        //     console.log(a, a, a);
        // }
        // fn(1, 2, 3)//Duplicate parameter name not allowed in this context

        // 3.在严格模式下,函数的独立调用不再默认绑定(即this不再指向window)
        console.log(this);//window

        setTimeout(() => {
            console.log(this);//window
        }, 100)

        function gn() {
            console.log(this);//und
        }
        gn()

(三)柯里化函数和组合函数

1.柯里化函数

只传递给函数一部分参数来调用它, 让它返回一个函数去处理剩余的参数, 这个过程,就是柯里化。

PS:说白了就是分开参数连续调用新函数

HTML
    <script>
        //没有经过柯里化函数处理
        function add(a, b, c) {
            return a + b + c
        }
        console.log(add(1, 2, 3));//7
    </script>

    <script>
        // 只传递给函数一部分参数来调用它, 让它返回一个函数去处理剩余的参数, 这个过程,就是柯里化。
        // 柯里化函数:说白了就是分开参数连续调用新函数
        function fn1(a) {
            return function (b) {
                // b++
                return function (c) {
                    // c+=b
                    return a + b + c
                }
            }
        }
        console.log(fn1(1)(2)(3));//6
        // console.log(fn(1)(2)(3)); //10
    </script>

    <script>
        // 简化版柯里化函数
        let fn2 = a => b => c => a + b + c
        console.log(fn1(1)(2)(3));//6
    </script>

2.组合函数

组合函数:其实就是先执行内参数函数获得返回值作为参数后再执行外函数

即先执行参数函数,再执行外函数

JavaScript
        function fn1(a) {
            console.log(a);
        }
        function fn2(b) {
            return ++b
        }
        //先执行fn2(1)得到返回值2,然后执行fn1(2)
        fn1(fn2(1))//2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高效稳定转换!PW2205芯片轻松实现12V/24V转5V/3.3V 5A输出

在电子设备蓬勃发展的今天&#xff0c;高效稳定的电源转换技术成为了推动行业进步的关键。PW2205平芯微芯片推出的高效同步降压DC-DC转换器&#xff0c;以其出色的性能和广泛的应用领域&#xff0c;成为了市场上的热门选择。 PW2205转换器凭借其卓越的性能&#xff0c;为各类电…

AI智能滤镜解决方案,全新的视觉创作体验

一张精美的图片&#xff0c;一段引人入胜的视频&#xff0c;往往能够瞬间抓住观众的眼球&#xff0c;为企业带来不可估量的商业价值。然而&#xff0c;如何快速、高效地制作出高质量的视觉内容&#xff0c;一直是困扰众多企业的难题。美摄科技凭借其领先的AI智能滤镜解决方案&a…

电脑实时监控软件分享|五个好用的实时屏幕监控软件

电脑实时监控软件是一种专门设计用于实时监控和记录电脑用户操作行为、系统状态以及网络活动的软件工具。 这类软件主要服务于企业、教育机构、家庭或个人用户&#xff0c;用于确保网络安全、提升工作效率、监督员工行为、保护儿童在线安全、防止数据泄露等多种目的。 针对企业…

面试(01)————JVM篇,最大白话的一集,常见概念的讲解以及GC监控调优等等

一、JDK体系结构图 二、JVM整体架构 三、JVM组成 3.1、JVM内存区域的执行底层原理 ​编辑 3.1.1、程序计数器 3.1.2、堆栈关系的发现 3.1.3、方法去和堆的关系 3.1.4、堆&#xff08;重点&#xff09; 3.1.4.1、可达性分析算法 3.1、内存泄漏测试以及堆区的GC监控 3.…

电脑硬盘分区表的两种格式:MBR 和 GPT

电脑硬盘分区表的两种格式&#xff1a;MBR 和 GPT 段子手168 2024-4-5 电脑硬盘分区表有两种格式&#xff1a;MBR 和 GPT&#xff1a; 一、MBR 分区表 1.MBR 是主引导记录 (Master Boot Record) 的英文缩写 在传统&#xff08;Legacy&#xff09;硬盘分区模式中&#xff0c…

零基础入门NLP - 新闻文本分类比赛方案分享 nano- Rank1

nano- 康一帅 简介 环境 Tensorflow 1.14.0Keras 2.3.1bert4keras 0.8.4 文件说明 EDA&#xff1a;用于探索性数据分析。data_utils&#xff1a;用于预训练语料的构建。pretraining&#xff1a;用于Bert的预训练。train&#xff1a;用于新闻文本分类模型的训练。pred&a…

环形链表 II - LeetCode 热题 26

大家好&#xff01;我是曾续缘&#x1f61b; 今天是《LeetCode 热题 100》系列 发车第 26 天 链表第 5 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 环形链表 II 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xf…

每日OJ题_两个数组dp④_力扣44. 通配符匹配

目录 力扣44. 通配符匹配 解析代码 力扣44. 通配符匹配 44. 通配符匹配 难度 困难 给你一个输入字符串 (s) 和一个字符模式 (p) &#xff0c;请你实现一个支持 ? 和 * 匹配规则的通配符匹配&#xff1a; ? 可以匹配任何单个字符。* 可以匹配任意字符序列&#xff08;包…

全面了解网络性能监测:从哪些方面进行监测?

目录 摘要 引言 CPU内存监控 磁盘监控 网络监控 GPU监控 帧率监控 总结 摘要 本文介绍了网络性能监测的重要性&#xff0c;并详细介绍了一款名为克魔助手的应用开发工具&#xff0c;该工具提供了丰富的性能监控功能&#xff0c;包括CPU、内存、磁盘、网络等指标的实时…

[C语言]——柔性数组

目录 一.柔性数组的特点 二.柔性数组的使用 三.柔性数组的优势 C99中&#xff0c;结构体中的最后⼀个元素允许是未知大小的数组&#xff0c;这就叫做『柔性数组』成员。 typedef struct st_type //typedef可以不写 { int i;int a[0];//柔性数组成员 }type_a; 有些编译器会…

B. Burning Midnight Oil Codeforces Round 112 (Div. 2)

题目链接&#xff1a; Problem - 165B - Codeforceshttps://codeforces.com/problemset/problem/165/B 题目大意&#xff1a; 最后写了至少n个&#xff0c;每次衰减k倍&#xff08;/k&#xff09;&#xff0c;问最初的v最小为多少。 思路&方法&#xff1a; 二分答案。 AC代…

2024零废弃日青山少年可持续工坊走进南湖社区

“零废弃”不代表完全不产生任何垃圾&#xff0c;而是一种“尽可能避免产生废弃”的生活态度&#xff0c;每一个人都可以从零开始&#xff0c;从日常的随手小事开始&#xff0c;珍惜每一件物品、珍视每一种情绪&#xff0c;为生活腾出更大的空间。 2024零废弃日&#xff0c;北…

JS 表单验证

点击注册的时候&#xff0c;渲染出来&#xff0c;验证码是自动获取出来的 html&#xff1a; <div class"div1">用户名<input type"text" id"yhm"><span id"span1"></span><br>密码<input type"…

《Git版本控制管理》笔记

第三章 起步 git --version查看版本号git --help查看帮助文档裸双破折号分离参数 git diff -w master origin – tools/Makefile将当前目录或任何目录转化为Git版本库 git init 初始化之后项目目录中&#xff0c;有名为.git的文件git status 查看git状态git commit 提供日志消…

贪心算法|135.分发糖果

力扣题目链接 class Solution { public:int candy(vector<int>& ratings) {vector<int> candyVec(ratings.size(), 1);// 从前向后for (int i 1; i < ratings.size(); i) {if (ratings[i] > ratings[i - 1]) candyVec[i] candyVec[i - 1] 1;}// 从后…

格式化字符串漏洞原理及其利用(附带pwn例题讲解)

写在前面&#xff1a; 本篇博客为本人原创&#xff0c;但非首发&#xff0c;首发在先知社区 原文链接&#xff1a; https://xz.aliyun.com/t/14253?time__1311mqmx9QiQi%3D0%3DDQoDsNOfptD8nDCFdNNK4D&alichlgrefhttps%3A%2F%2Fxz.aliyun.com%2Fu%2F74789各位师傅有兴趣…

JQuery(三)---【使用JQuery动态设置浏览器窗口尺寸、JQuery的遍历】

零.前言 JQuery(一)---【JQuery简介、安装、初步使用、各种事件】-CSDN博客 JQuery(二)---【使用JQuery对HTML、CSS进行操作】-CSDN博客 一.JQuery动态设置浏览器窗口尺寸大小 1.1width()和height()方法 width()&#xff1a;设置或者返回元素的宽度(不包括内边距、边框或外…

React - 你知道在React组件的哪个阶段发送Ajax最合适吗

难度级别:中级及以上 提问概率:65% 如果求职者被问到了这个问题,那么只是单纯的回答在哪个阶段发送Ajax请求恐怕是不够全面的。最好是先详细描述React组件都有哪些生命周期,最后再回过头来点题作答,为什么应该在这个阶段发送Ajax请求。那…

智慧校园预付费水电表控制系统

在智慧校园建设中&#xff0c;预付费水电表控制系统成为了不可或缺的一部分&#xff0c;它采用了先进的信息技术手段确保校园水电资源的高效管理和使用。这种系统通过智能化、信息化的手段&#xff0c;不仅优化了能源管理&#xff0c;还大幅提升了校园管理的现代化水平。本文将…

FPGA(Verilog)实现uart传输协议传输数据(含仿真)

目录 实现功能&#xff1a; 1.接收uart串行数据&#xff0c;输出并行数据(1byte)。 2.输入并行数据(1byte)&#xff0c;输出uart串行数据。 3.完成uart传输的1次环回。 uart协议的1帧数据传输 模块封装-port设置 Verilog代码实现 1.uart接收模块:接收串行数据,输出并行数…