1、面向对象编程介绍
-
1、面向过程编程 POP(Process-oriented programming)
-
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的一次调用就可以了
-
面向过程,就是按照我们分析好了的步骤,按照步骤解决问题
-
2、面向对象编程 OOP(Object oriented Programming)
-
面向对象是把事务分解成一个个对象,然后由对象之间分工与合作
-
面向对象是以对象功能来划分问题,而不是步骤
-
面向对象的特性
-
封装性
-
继承性
-
多态性
2、ES6 中的类和对象
-
1、对象
-
对象由属性和方法组成的
-
属性:事物的特征,在对象中用属性来表示(常用名词)
-
方法:事物的行为,在对象中用方法来表示(常用动词)
-
2、类 calss
-
类抽象了对象的公共部分,它泛指某一大类(class)
-
对象特指某一个,通过类实例化一个具体的对象
-
类 constructor 构造函数
// 创建类class 创建一个明星类 class Star { constructor(uname) { this.uname = uname; } } // 利用类创建对象 new new Star('刘德华')
-
注意
-
1、通过 class 关键字创建类,类名我们还是习惯定义首字母大写
-
2、类里面有个 constructor 函数,可以接收传递过来的参数,同事返回实例对象
-
3、constructor 函数只要 new 生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
-
4、生成实例 new 不能省略
-
5、最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加 function
-
3、类中添加方法
class Star { sing() { } } // 利用类创建对象 new new Star('刘德华')
-
1、我们类里面所有的函数不需要写 function
-
2、多个函数方法之间不需要添加逗号分隔
3、类的继承
-
extends 继承: 子类可以继承父类的属性和方法
class Father { // 父类 } class Son extends Father { // 子类继承父类 }
-
super 关键字
-
super 关键字用于访问和调用对象父类的函数。可以调用父类的构造函数,也可以调用父类的普通函数
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(x,y) // 调用了父类中的构造函数 } } const son = new Son(1,2); son.sum()
-
1、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
-
2、继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
// super 关键字调用父类普通函数 class Father { say() { return '我是爸爸'; } } class Son extends Father { say() { // console.log('我是儿子') console.log(super.say() + '的儿子') // super.say()就是调用父类的普通函数say() } } const son = new Son(); son.say();
-
继承中的属性或者方法查找原则:就近原则
class Person { constructor(surname) { this.surname = surname; } } class Student extends Person { // 子类继承父类 constructor(surname,firstname) { super(surname); // 调用父类的constructor(surname) this.firstname = firstname; // 定义子类独有的属性 } }
-
子类在构造函数中使用 super,必须放到 this 前面(必须先调用父类的构造方法,再使用子类构造方法)
-
注意点
-
1、在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
-
2、类里面的共有的属性和方法一定要加 this 使用
-
3、类里面的 this 指向问题
-
4、constructor 里面的 this 指向实例对象,方法里面的 this 指向这个方法的调用者
-
insertAdjacentHTML()可以直接吧字符串格式元素添加到父元素中
-
appendChild 不支持追加字符串的子元素,insertAdjacentHTML 支持追加字符串的元素
-
禁止选中文字:window.getSelection ? window.getSelection().removeAllRanges():document.selection.empty();
-
获取文字: window.getSelection ? window.getSelection():document.selection.createRange().text;
4、构造函数和原型
-
创建对象可以通过以下三种方式:
-
1、对象字面量 // let obj = {}
-
2、new Object() // let obj = new Object
-
3、自定义构造函数
// 自定义构造函数创建对象 function Star(uname,age) { this.uname = uname; this.age = age; this.sing = function() { } } let ldh = new Star('刘德华',18)
-
1、构造函数
-
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面
-
new 在执行时会做四件事情:
-
1、在内存中创建一个新的空对象
-
2、让 this 指向这个新的对象
-
3、执行构造函数里面的代码,给这个新对象添加属性和方法。
-
4、返回这个新对象(所以构造函数里面不需要 return)
// 构造函数中的属性和方法我们称为成员,成员可以添加 function Star(uname,age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我会唱歌') } let ldh = new Star('刘德华',18) // 1、实例成员就是构造函数内部通过this添加的成员 uname age sing 就是实例成员 // 实例成员只能通过实例化的对象来访问 console.log(ldh.uname) ldh.sing(); // console.log(Star.uname) // 不可以通过构造函数来访问实例成员 // 2、静态成员 在构造函数本身上添加的成员 sex 就是静态成员 Star.sex = '男' // 静态成员只能通过构造函数来访问 console.log(Star.sex) // console.log(ldh.sex) // 不能通过对象来访问 }
-
构造函数的问题
-
构造函数方法很好用,但是存在浪费内存的问题
-
一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
-
构造函数原型 prototype
-
构造函数通过原型分配的函数是所有对象所共享的
-
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象对象的所有属性和方法,都会被构造函数所拥有。
-
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
-
1、原型是什么?
-
一个对象,我们也成为 prototype 为原型对象
-
2、原型的作用是什么?
-
共享方法。
// 1、构造函数的问题 function Star(uname,age) { this.uname = uname; this.age = age; } Star.prototype.sing = function() { console.log('我会唱歌') } let ldh = new Star('刘德华',18) let zxy = new Star('张学友',18) console.log(ldh.sing === zxy.sing) // true ldh.sing(); zxy.sing(); // 2、 一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
-
对象原型
__proto__
-
对象都会有一个属性
__proto__
指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有__proto__
原型的存在 -
__proto__
对象原型和原型对象 prototype 是等价的 -
__proto__
对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype -
对象身上系统自己添加一个
__proto__
指向我们构造函数的原型对象 -
prototype 原型对象
-
__proto__
对象的原型 -
constructor 构造函数
-
对象原型(
__proto__
)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性,constructor 我们称为构造函数,因为它指回构造函数本身function Star(uname,age) { this.uname = uname; this.age = age; } // 很多情况下,我们需要手动的利用constructor这个属性指回 原来的构造函数 Star.prototype = { // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 constructor: Star, sing: function() {}, move: function() {} } let ldh = new Star('刘德华',18); let zxy = new Star('张学友',19) console.log(Star.prototype) console.log(ldh.__proto__) console.log(Star.prototype.constructor) console.log(ldh.__proto__.constructor)
-
原型链
-
this 指向
-
1、在构造函数中,里面 this 指向的是实例对象
-
2、原型对象函数里面的 this 指向的是实例对象
-
扩展内置对象
-
数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {}, 只能是 Array.prototype.xxx = function() {} 的方式
// 原型对象的应用 扩展内置对象方法 Array.prototype.sum = function() { let sum = 0; for(let i = 0; i < this.length; i++) { sum += this[i] } return sum; } let arr = [1,2,3] console.log(arr.sum()) console.log(Array.prototype)
5、继承
-
ES6 之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
-
call 方法
-
call() 调用这个函数,并且修改函数运行的 this 指向
-
fun.call(thisArg,arg1,arg2,...)
-
thisArg: 当前调用函数 this 的指向对象
-
arg1,arg2 : 传递的其他参数
// call 方法 function fn(x,y) { console.log('打印') console.log(this) console.log(x + y) } let o = { name: 'andy' } fn() // 基础调用 1、call() 可以调用函数 fn.call() // this执行windows 2、call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象 fn.call(o,1,2)
-
1、借用构造函数继承父类型属性
-
核心原理:通过 call()把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性
// 借用父构造函数继承属性 // 1、父构造函数 function Father(uname,age) { // this 指向付构造函数的对象实例 this.uname = uname; this.age = age; } Father.prototype.money = function() { console.log(l) } // 2、子构造函数 function Son(uname,age,score) { // this 指向子构造函数的对象实例 Father.call(this,uname,age) this.score = score; } // Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化 Son.prototype = new Father(); // 实例对象 // 如果你用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数 Son.prototype.constructor = Son; // 这个是子构造函数专门的方法 Son.prototype.exam = function() { console.log('孩子要考试') } const son = new Son('张三', 18,100) console.log(son) console.log(Father.prototype) console.log(Son.prototype.constructor)
-
2、借用原型对象继承方法
-
类的本质
-
ES6 之前通过构造函数+原型实现面向对象编程
-
1、构造函数有原型对象 prototype
-
2、构造函数原型对象 prototype 里面有 constructor 指向构造函数本身
-
3、构造函数可以通过原型对象添加方法
-
4、构造函数创建的实例对象有
__proto__
原型指向 构造函数的原型对象 -
Es6 通过类 实现面向对象编程
-
1、class 本质还是 function
-
2、类的所有方法都定义在类的 prototype 属性上
-
3、类创建的实例,里面也有
__proto__
指向类的 prototype 原型对象 -
4、所以 ES6 的类它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已
-
5、所以 ES6 的类其实就是语法糖
-
6、语法糖:语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便、那么这个方法就是语法糖
class Star { } console.log(typeof Star) // (1)、类有原型对象 prototype console.log(Star.prototype) // 类原型对象prototype 里面有constructor 指向构造函数本身 console.log(Star.prototype.constructor) // (3)类可以通过原型对象添加方法 Star.prototype.sing = function() { } let ldh = new Star(); console.log(ldh) // (4) 类创建的实例对象有`__proto__`原型指向 构造函数的原型对象 console.log(ldh.__proto__ === Star.prototype)
-
类的本质其实还是一个函数 我们也可以简单的认为 类就是构造函数的另外一种写法
6、ES5 中的新增方法
数组方法
-
迭代(遍历)方法:forEach()、map()、filter()、some()、every()
-
some() 方法用于检测数组中的元素是否满足指定条件,通俗点查找数组中是否有满足条件的元素
-
注意它返回值是布尔值,如果查找到这个元素,就返回 true,如果查找不到就返回 false
-
如果找到第一个满足条件的元素,则终止循环,不在继续查找
- forEach 和 some 的区别
- 在 forEach 里面 return 不会终止迭代
- 在 some 中遇到遇到 retur true 就是终止遍历 迭代效率更高
- 在 filter 里面 return 不会终止迭代
字符串方法
- trim()
对象方法
- Object.keys() 用来获取对象自身所有的属性
- 返回一个由属性名组成的数组
- 效果类似 for ... in
-
Object.defineProperty() 定义新属性或修改原有的属性
-
Object.defineProperty(obj,prop,descriptor)
-
obj:必需 目标对象
-
prop: 必需。 需定义或修改的属性的名字
-
descriptor: 必需 目标属性所拥有的特性
let obj = { id: 1, pname: '张三', price: 1999 } Object.defineProperty(obj,'num', { value: 100 }) Object.defineProperty(obj,'price', { value: 9.9, writable: false, // 如果值为false 不允许修改这个属性值 默认值也是 false enumerable: false, // 如果值为false 则不允许遍历 ,默认的值是false configurable: false // 如果值为false 则不允许删除这个属性 不允许在修改第三个参数里面的特性 默认为false })
函数进阶
1、函数的定义和调用
-
1、函数的定义方式
-
(1)、自定义函数(命名函数)
-
function fn() {}
-
(2)、函数表达式(匿名函数)
-
let fun = function() {};
-
(3)、利用 new Function('参数 1','参数 2','函数体')
-
let f = new Function(); // 不常用
-
Function 里面参数都必须是字符串格式
-
(4)、所有函数都是 Function 的实例(对象)
-
(5)、函数也属于对象
-
2、 函数的调用方式
-
1、普通函数
function fn() { } fn(); // 完整写法 window.fn(); fn.call()
-
2、对象的方法
let o = { // this指向的是对象 o sayHi: function() { } } o.sayHi()
-
3、构造函数
function Star() {} // this指向ldh这个实例对象 // 原型对象里面的this指向的也是ldh这个实例对象 Star.prototype.sing = function(){} vat ldh = new Star()
-
4、绑定事件函数
// this 指向函数的调用者 btn这个按钮对象 btn.onclick = function() {} // 点击了按钮就可以调用这个函数
-
5、定时器函数
// window.setInterval 完整写法 // this也是指向window setInterval(function() { },1000) // 这个函数是定时器自动1秒钟调用一次
-
6、立即执行函数
(function() { // this指向window })(); // 立即执行函数是自动调用
2、this
-
this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了 this 的指向不同
-
一般指向我们的调用者
-
调用方式:普通函数调用;this 指向:window
-
调用方式:构造函数调用;this 指向:实例对象 原型对象里面的方法也指向实例对象
-
调用方式:对象方法调用;this 指向:该方法所属对象
-
调用方式:事件绑定方法;this 指向:绑定事件对象
-
调用方式:定时器函数;this 指向: window
-
调用方式:立即执行函数;this 指向:window
-
1、改变函数内部 this 指向
-
常用的有 bind()、call()、apply()三种方法
-
(1)、call 方法
-
call()方法调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的 this 指向
let o = { name: 'andy' } function fn(a,b) { console.log(this) console.log(a+b) } fn.call(o,1,2) // call 第一个可以调用函数 第二个可以改变函数内的this指向 // call 的主要作用可以实现继承 function Father(uname,age,sex) { this.uname= uname; this.age = age; this.sex = sex; } function Son(uname,age,sex) { Father.call(this,uname,age,sex) } let son = new Son('刘德华',12,'男') console.log(son)
-
(2)、apply 方法
-
apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向
-
fun.apply(this.Arg,[argsArray])
let o = { name: 'andy' } function fn(arr) { console.log(this); console.log(arr) // 'pink' } fn.apply(o,['pink']) // 1、也是调用函数 第二个可以改变函数内部的this指向 // 2、但是他的参数必须是数组(伪数组) // 3、apply的主要应用 比如说我们可以利用apply借助于数学内置对象求最大值 // Math.max(); let arr = [1,66,88,5] // let max = Math.max.apply(null,arr); let max = Math.max.apply(Math,arr) let min = Math.min.apply(Math,arr) console.log(max,min)
- (3)、 bind 方法
-
bind()方法不会调用函数,但是能改变函数内部 this 指向
-
fn.bind(this.Arg,arg1,arg2,...)
-
返回由指定的 this 值和初始化参数改造的原函数拷贝
let o = { name: 'andy' } function fn() { console.log(this) } let f = fn.bind(o) f(); // 1、不会调用原来的函数 可以改变原来函数内部的this指向 // 2、返回的是原函数改变this之后产生的新函数 // 3、如果有的函数我们不需要立即调用 但是又想改变这个函数内部的this指向此时用bind
-
我们有一个按钮,当我们点击了之后,就禁用这个按钮,3 秒钟之后开启这个按钮
let btn = document.querySelector('button') btn.onclick = function() { this.disabled = true; // 这个this指向的是btn这个按钮 // let that = this; setTimeout(function() { // that.disabled = false; // 定时器函数里面的this 指向的是window; this.disabled = false; // 此时定时器函数里面的this指向的是 btn; }.bind(this),3000) // 这个this 指向的是btn这个对象 }
-
call apply bind 总结
-
相同点:都可以改变函数内部的 this 指向
-
区别点
-
1、call 和 apply 会调用函数,并且改变函数内部 this 指向
-
2、call 和 apply 传递的参数不一样,call 传递参数 aru1,aru2...形式,apply 必须数组形式[arg]
-
3、bind 不会调用函数,可以改变函数内部 this 指向
-
主要应用场景
-
1、call 经常做继承
-
2、apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
-
3、bind 不调用函数,但是还想改变 this 指向,比如改变定时器内部的 this 指向
3、严格模式
-
1、什么是严格模式
-
JavaScript 除了提供正常模式外,还提供了严格模式(strict mode).ES5 的严格模式是采用具有限制性 JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码
-
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
-
严格模式对正常的 JavaScript 语义做了一些更改:
-
1、消除了 JavaScript 语法的一些不合理,不严谨之处,减少了一些怪异行为
-
2、消除代码运行的一些不安全之处,保证代码运行的安全
-
3、提高编译器效率,增加运行速度
-
4、禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 JavaScript 做好铺垫。比如一些保留字如:class、enum、export、extends、import、super 不能做变量名
-
2、开启严格模式
-
严格模式可以应用到整个脚本或个别函数中,因此在使用时,我们可以将严格模式分为脚本开启严格模式和为函数开启严格模式两种情况
-
1、为脚本开启严格模式
-
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句'use strict'(或 "use strict")
<!--为整个脚本(script标签)开启严格模式--> <script> 'use strict' // 下面的js代码就会按照严格模式执行代码 </script> <script> (function() { 'use strict' })(); </script>
-
2、为函数开启严格模式
-
要给某个函数开启严格模式,需要把'use strict'(或 "use strict")声明放在函数体所有语句之前
<!--为某个函数开启严格模式--> <script> // 此时只是给fn函数开启严格模式 function fn() { 'use strict' // 下面的js代码就会按照严格模式执行代码 } function fun() { // 里面的还是按照普通模式执行 } </script>
-
3、严格模式中的变化
-
严格模式对 Javascript 的语法和行为,都做了一些改变
-
1、变量规定
-
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用 var/let 命令声明,然后再使用
-
严禁删除已经声明变量。例如 delete x; 语法是错误的
-
2、严格模式下 this 指向问题
-
以前在全局作用域函数中 this 指向 window 对象
-
严格模式下全局作用域中函数中的 this 是 undefined (*重点记)
-
以前构造函数时不加 new 也可以调用,当普通函数,this 指向全局对象
-
严格模式下,如果构造函数不加 new 调用,this 指向的是 undefined 如果给他赋值 则会报错
-
new 实例化的构造函数指向创建的对象实例
-
定时器 this 还是指向 window
-
事件、对象还是指向调用者
-
3、函数变化
-
函数不能有重名的参数
-
函数必须声明在顶层,新版本的 JavaScript 会引入"块级作用域"(ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码快内声明函数
4、高阶函数
-
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
<script> function fn(callback) { callback && callback(); } fn(function() {alert('hi')}) </script> <script> function fn() { return function() {} } fn(); </script>
-
此时 fn 就是一个高阶函数
-
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
5、闭包
1、变量作用域
- 变量根据作用域的不同分为两种:全局变量和局部变量
- 1、函数内部可以使用全局变量
- 2、函数外部不可以使用局部变量
- 3、当函数执行完毕,本作用域内的局部变量会销毁
2、闭包
-
闭包是函数
-
闭包(closure)指有权访问另一个函数作用域中变量的函数。---Javascrip 高级程序设计
// 闭包:我们fun这个函数作用域访问了另外一个函数fn里面的局部变量 num // 一个作用域可以访问另外一个函数的局部变量 // 我们fn外面的作用域可以访问fn内部的局部变量 function fn() { let num = 10; function fun() { console.log(num) } <!-- fun(); --> return fun } <!-- fn(); --> let f = fn(); f(); // 类似于 // let f = function fun() {console.log(num)}
-
简写
function fn() { let num = 10; return function() { console.log(num) } } let f = fn(); f();
-
闭包的主要作用:延伸了变量的作用范围
-
利用闭包的方式得到当前小 li 的索引号
// 1、我们可以利用动态添加属性的方式 let lis = document.querySelector('.nav').querySelectorAll('li') for(let i = 0; i < lis.length ; i++) { lis[i].index = i; list[i].onclick = function() { console.log(this.index) } } // 2、利用闭包的方式得到当前小li的索引号 for(let i = 0; i < lis.length ; i ++) { // 利用for循环创建了4个立即执行函数 // 立即执行函数也称为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量 (function(i) { //console.log(i) lis[i].onclick = function() { console.log(i) } })(i) }
-
循环中的定时器
-
闭包应用-3 秒钟之后,打印所有 li 元素的内容
let lis = document.querySelector('.nav').querySelectorAll('li') for(let i = 0; i < lis.length ; i++) { (function(i) { setTimeout(function() { console.log(lis[i].innerHTML) },1000) })(i) } }
-
闭包总结
-
1、闭包是什么?
-
闭包是一个函数(一个作用域可以访问另外一个函数的局部变量)
-
2、闭包的作用是什么?
-
延伸变量的作用范围
6、递归
1、什么是递归?
- 如果一个函数内部可以调用其本身,那么这个函数就是递归函数。
- 简单理解:函数内部自己调用自己,这个函数就是递归函数
- 递归函数的作用和循坏效果一样
- 由于递归很容易发生“栈溢出”错误(stack overflow) ,所以必须要加退出条件 return
2、利用递归求 1~n 的阶乘
// 利用递归函数求1~n的阶乘1*2*3*n
function fn(n) {
if(n === 1) {
return 1;
}
return n * fn(n-1)
}
console.log(fn(3)) // 6
console.log(fn(4)) // 24
// 详细思路,假如用户输入的是3
// return 3 * fn(2)
// return 3 * (2 * fn(1))
// return 3 * (2 * 1)
// return 3 * (2)
// return 6
3、利用递归函数求裴波那契数列(兔子序列)
// 利用递归函数求裴波那契数列(兔子序列)1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项 (n-1 n-2)就可以计算出n对应的序列值
function fb(n) {
if(n ===1 || n === 2) {
return 1;
}
return fb(n-1) + fb(n-2)
}
fb(5)
4、利用递归求:根据 id 返回对应的数据对象
let data = [
{
id: 1,
name: '家电',
goods: [
{
id: 11,
gname: '冰箱'
}, {
id: 12,
gname: '洗衣机'
}
]
},
{
id: 2,
gname: '服饰'
}
]
// 我们想要做输入id号,就可以返回的数据对象
// 1、利用forEach去遍历里面的每一个对象
function getID(json,id) {
let o = {}
json.forEach(function(item) => {
// console.log(item) // 2个数组元素
if(item.id === id) {
console.log(item)
o = item
// 2、我们想要得到里层的数据 11 12 可以利用递归函数
// 里面应该有goods这个数组并且数组的长度不为0
}else if(item.goods && item.goods.length > 0) {
o = getID(item.goods,id)
}
})
return 0;
}
getID(data,1)
getID(data,11)
浅拷贝和深拷贝
- 1、浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
- 2、深拷贝拷贝多层,每一级别的数据都会拷贝
- 3、Object.assign(target,...sources) es6 新增方法可以浅拷贝
-
浅拷贝
let obj = { id: 1, name: 'andy', msg: { age: 13}, } let o = {} for( let k in obj) { // k是属性名 obj[k]属性值 o[k] = obj[k] }
-
深拷贝
let obj = { id: 1, name: 'andy', msg: { age: 13}, color: ['pink', 'red'] } let o = {} // 封装函数 function deepCopy(newobj,oldobj) { for(let k in oldobj) { // 判断我们的属性值属于哪种数据类型 // 1、获取属性值 oldobj[k] let item = oldobj[k] // 2、判断这个值是否是数组 if(item instanceof Array) { newobj[k] = [] deepCopy(newobj[k],item) }else if(item instanceof Object) { // 3、判断这个值是否是对象 newobj[k] = {} deepCopy(newobj[k],item) }else { // 4、属于简单数据类型 newobj[k] = item; } } } deepCopy(o,obj)
正则表达式
1、什么是正则表达式
-
正则表达式(Regual Expression) 是用于匹配字符串中字符组合的模式,正则表达式也是对象
-
匹配 替换 提取
2、创建正则表达式
- 1、通过调用 RegExp 对象的构造函数创建
- let xxx = new RegExp(/表达式/)
- 2、通过字面量创建
- let xxx = /表达式/
3、测试正则表达式 test
- test()正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串
- regexobj.test(str)
- 1、regexobj 是写的正则表达式
- 2、str 我们要测试的文本
- 3、就是检测 str 文本是否符合我们写的正则表达式规范
4、正则表达式中的特殊字符
-
特殊字符也称为元字符
-
1、边界符
-
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
-
^
表示匹配行首的文本(以谁开始) -
$
表示匹配行尾的文本(以谁结束) -
2、字符类
-
[]
表示有一系列字符串可供选择,只有匹配其中一个就可以了 -
[-]
-
范围 -
[^]
如果中括号里面有^
表示取反的意思千万和我们边界符^
别混淆 -
3、量词符
-
量词符用来设定某个模式出现的次数
-
*
重复零次或更多次 -
+
重复一次或更多次 -
?
重复零次或一次 -
{n}
重复 n 次 -
{n,}
重复 n 次或更多次 -
{n,m}
重复 n 到 m 次
-
{6,16} 中间不要有空格
-
括号总结
-
中括号 字符结合 匹配中括号中的任意字符
-
let reg = /^[abc]$/
-
a 也可以 b 也可以 c 也可以 a || b || c
-
大括号 量词符 里面表示重复次数
-
let reg = /^abc{3}$/
// 它只是让 c 重复三次 abccc -
小括号 表示优先级
-
let reg = /^(abc){3}$/;
// 它是让 abc 重复三次 abcabcabc -
4、预定义类
-
预定义类指的是某些常见模式的简写方式
-
\d
匹配 0-9 之间的任一数字,相当于[0-9] -
\D
匹配所有 0-9 以外的字符,相当于[^0-9] -
\w
匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_] -
\W
除所有字母、数字和下划线以外的字符,相当于[^a-za-z0-9_] -
\s
匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f] -
\S
匹配非空格的字符,相当于 [^\t\r\n\v\f] -
正则里面的或者符号 |
-
手机号
/^1[3|4|5|7|8]\d{9}$/
-
手机号
/^1[3-9]\d{9}$/
-
QQ 号
/^[1-9]\d{4,}$/
-
指定元素之后的下一个兄弟元素 nextElementSibling
5、正则替换
-
replace
-
/表达式/[switch]
-
switch(也成为修饰符)按钮什么样的模式来匹配,有三种值
-
g: 全局匹配
-
i: 忽略大小写
-
gi: 全局匹配+忽略大小写
ES6
-
let
-
ES6 中新增的用于声明变量的关键字
-
不存在变量提升
-
暂时性死区
-
const
-
作用:声明常量,常量就是值(内存地址)不能变化的量。
-
具有块级作用域
-
声明常量时必须赋值
-
常量赋值后,值不能修改
-
let、const、var 的区别
-
1、使用 var 声明的变量、其作用域为该语句所在的函数内,且存在变量提升现象。
-
2、使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。
-
3、使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值。
-
var 函数级作用域 变量提升 值可更改
-
let 块级作用域 不存在变量提升 值可更改
-
const 块级作用域 不存在变量提升 值不可更改
箭头函数
-
箭头函数不绑定 this 关键字,箭头函数中的 this,指向的是函数定义位置的上下文 this
-
剩余参数
const sum = (...args) => { let total = 0; args.forEach(item => total += item; ) return total } sum(10,20) sum(10,20,30)
-
扩展运算符(展开语法)
-
扩展运算符可以将数组或者对象转为逗号分割的参数序列
let ary = [1,2,3] ...ary // 1,2,3 console.log(...ary) // 1 2 3 console.log(1 ,2, 3) // 1 2 3
-
扩展运算符可以应用于合并数组
// 方法1 let ary1 = [1,2,3]; let ary2 = [4,5,6]; let ary3 = [...ary1,...ary2]; // 方法2 ary.push(...ary2)
Array
-
构造函数方法:Array.from()
-
将类数组或者可遍历对象转换为真正的数组
-
find()
-
用于找出第一个符合条件的数组成员,如果没有找到返回 undefined
-
findIndex()
-
用于找出第一个符合条件的数组成员的位置,如果没有返回-1
-
includes()
-
表示某个数组是否包含给定的值,返回布尔值
String 的扩展方法
-
模板字符串
-
ES6 新增的创建字符串的方式,使用反引号定义。
let name = `zhangsan`;
-
模板字符串可以解析变量
-
${xxx}
-
模板字符串中可以换行
-
在模板字符串种可以调用函数
const sayHello = function () { return "哈哈哈 追不到我吧 我就是这么强大"; }; const greet = `${sayHello()} 哈哈哈哈`; console.log(greet); // 哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈
-
startsWith()
-
startsWith(): 表示参数字符串是否在原字符串的头部,返回布尔值
-
endsWith()
-
endsWith(): 表示参数字符串是否在原字符串的尾部,返回布尔值
const str = "Hello world!"; console.log(str.startsWith("H")); // true console.log(str.startsWith("Hello")); // true console.log(str.endsWith("!")); // true
-
repeat()
-
repeat 方法表示将原字符串重复 n 次,返回一个新字符串
Set 数据结构
- ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
- Set 本身是一个构造函数,用来生成 Set 数据结构
const s = new Set()
- size 查看 set 有几个属性(长度)
-
Set 函数可以接受一个数组作为参数,用来初始化
const set = new Set([1, 2, 3, 4, 4, 5]); console.log(set); // Set[1,2,3,4,5]
-
实例方法
-
add(value): 添加某个值,返回 Set 结构本身
-
delete(value): 删除某个值,返回一个布尔值,表示删除是否成功
-
has(value): 返回一个布尔值,表示该值是否为 Set 成员
-
clear(): 清楚所有成员,没有返回值
const s = new Set(); s.add(1).add(2).add(3); // 向set结构中添加值 s.delete(2); // 删除set结构中的2值 s.has(1); // 表示set结构中是否有1这个值 返回布尔值 s.clear(); // 清除set结构中的所有值
-
遍历用 forEach()