老样子。复制上来的图片都没了,想看原版可以移步对应资源下载(资源刚上传,还在审核中)
(免费) JS高级笔记https://download.csdn.net/download/m0_58355897/89102910
一些前提概念 一 什么是js高级 - js高级是对js基础语法的一个补充说明,本质上还是对ECMAScript语法的进阶与延伸
- 高级所要学习的语法相比而言没有js基础的多,但是会更加晦涩难懂,更加侧重的是理解
二 什么是面向对象编程 |
一 代码段和作用域
(一)代码段
1.什么是代码段
PS:代码块:{}之间的就是代码块
2.代码段特点
- 上面代码段定义的变量 下面的代码段可以使用
- 下面代码段定义的变量 上面的代码段不可以使用(因为代码从上向下执行)
- 代码段之间互不影响 一个代码段中的错误 不会影响到另一个代码段
- 一个html页面中,可以存在多个代码段
(二)作用域
1.作用域
a.分类
- 全局变量(直接变量名、function函数体外的包括if/for中的var变量名)的作用范围是全局
- 局部变量(function函数体中用var声明的变量、function函数的形参)的作用范围是局部
b.主要作用
隔离变量,不同作用域下同名变量不会有冲突。
2.作用域链
寻找变量的过程 叫作用域链 链式过程 (就近原则--从内向外地冒泡寻找) |
以下图的代码为例,看下作用域链上查找一个变量的查找规则:
- 首先,在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
- 然后,在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
- 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
二 预解析
(一)预解析概念
浏览器不会直接执行代码,而是加工处理后再执行,这个加工处理的过程我们称之为预解析
(二)预解析期间操作
1.提升操作
- 加var的变量仅声明提升
- 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.内存分区:堆区 栈区
- 引用数据类型 存放在堆区 堆空间与一个对应的地址 对应的地址仍存在栈区
- VO 变量对象 用来存放当前执行上下文中创建的数据
(二)执行上下文
- 在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 undefined(预解析)
|
1.执行上下文概念
执行上下文就是js代码执行环境的抽象概念理解
只要代码运行,就一定在执行上下文中
2.执行上下文过程
代码被浏览器解析后,形成执行上下文ECS 入栈 1)当全局代码执行 产生ECG(鹅蛋) 2)当函数执行的时候 产生ECfn(鸡蛋) 3)ECG 或者 ECfn 放在ECS中,ECS是执行上下文栈 出栈 4)当函数调用完毕之后,就出栈(内存释放) 5)当全局代码没有被引用,就出栈 |
3.画图分析举例
四 声明变量 var|let|const
(一)加var与不加var的区别
- 不管加不加var,创建的是全局变量,都会放在GO中,也就是说可以通过window.xx
- 加var的变量可能是全局变量, 也可能是局部变量, 通过是否写在函数内区分
- 局部变量不会挂载在window上
- 加var的变量在预解析的时候会提升, 不加var不会提升
- 加var的变量不能用delete()方法删除,不加var能
(二)let声明变量(ES6)
1.产生原因
之前使用var声明, 提升会打乱我们代码的执行顺序
2.特点
- let声明的变量不会提升(提升了但是没有初始化)
- let会和{} 形成块级作用域
- 使用let声明的变量不会挂载在window上
- 使用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.特点
- const声明的变量不会提升
- 和{}会形成块级作用域
- 不会挂载window上
- 声明的变量不能修改(常量)
- 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); |
五 高阶函数
(一)高阶函数定义
一个函数它的参数是函数或者它的返回值是函数,那么这个函数叫高阶函数
(二)常见的内置高阶函数
数组的一些方法是参数为函数的高阶函数:
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 |
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.特点
- 延长了变量的生命周期
- 扩大了变量的作用范围
- 保护变量
个人理解 需注意的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指向的是对象 或者是window 或者是其他事件源
|
2.分类
(二)默认绑定
默认绑定的this值:window对象
- 直接this
- 函数独立调用时里面的this
JavaScript //1.直接this console.log(this);//window
//2.当一个函数独立调用时,此时里面的this是默认绑定,指向是window function fn() { console.log(this); } fn() |
(三)隐式绑定
隐式绑定的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.概念
- 对于构造器来说 里面的this默认绑定的是window
- 当我们使用new的时候 里面的this指向发生了改变 指向是生成的实例化对象
2.new(运算符/操作符做了什么事情)
为什么要使用new呢? 因为new可以改变this指向,并且执行构造器 |
- 创建出了一个实例化对象(开辟了一个新的堆空间)
- 让构造器中this指向新的实例化对象(指向了新的堆空间)
- 执行了构造函数
- 返回的是一个堆空间的地址,把地址给了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指向 逐级向上查找
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 |
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
- 不写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... |
- 新增自己属性/方法时写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: 写成数组的时候 ,当元素是纯数字 可以不添加引号 其余都要添加 |
- 字符串
- 对象
JSON { "name": "小花", "hobby": [ "踢足球", "打篮球" ], "age": 18 } |
- 数组
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" 这个文件中写的代码都受严格模式的约束 |
严格模式时不允许:
- 不能使用不加var或者let的变量
- 形参的参数不能重名
- 在严格模式下,函数的独立调用不再默认绑定(即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 |