一、面向对象

面向对象是一种注重结果的思维方式

面向过程:注重的是过程
面向对象:注重的是结果—本质是面向过程的封装

1.面向对象编程

别人写好的对象 : 好处: 效率高 坏处: 维护不便
自己写的对象 : 好处:维护方便 坏处:效率低

  1. <div>div1</div>
  2. <div>div2</div>
  3. <div>div3</div>
  4. <p>我是p1</p>
  5. <p>我是p2</p>
  6. <p>我是p3</p>
  7. <p>我是p4</p>
  8. <script>
  9. let zxkObj = {
  10. setColor:function (arr,color){
  11. for(let i = 0;i<arr.length;i++){
  12. arr[i].style.color = color
  13. }
  14. },
  15. setBorder:function (arr,border){
  16. for(let i = 0;i<arr.length;i++){
  17. arr[i].style.border = border
  18. }
  19. }
  20. }
  21. //1.获取元素
  22. let divList = document.querySelectorAll('div')
  23. let pList = document.querySelectorAll('p')
  24. //2.循环遍历元素
  25. zxkObj.setColor(divList,'red')
  26. zxkObj.setBorder(divList,'5px solid yellow')
  27. zxkObj.setColor(pList,'green')
  28. zxkObj.setBorder(pList,'5px solid pink')
  29. </script>

2.内置对象api

内置对象api : js作者写好的对象的一些方法 内置对象 : js作者提前写好的对象,直接拿来使用即可 api : 是预先定义的函数

2.1数组对象api

  1. <script>
  2. /*数组增删改查
  3. push() : 在数组末尾 新增元素
  4. unshift() : 在数组开头 新增元素
  5. pop() : 在数组末尾 删除元素
  6. shift() : 在数开头 删除元素
  7. */
  8. let arr = [20, 40, 60, 30, 50, 70, 100]
  9. console.log(arr)
  10. //1.arr.join('分隔符') 拼接成字符串
  11. let str = arr.join('|')
  12. console.log(str)
  13. //2.arr.reverse() 翻转数组
  14. arr.reverse()
  15. console.log(arr)
  16. //3.arr.sort() 数组排序 回调函数
  17. arr.sort(function (a, b) {
  18. //return a - b //小到大[20, 30, 40, 50, 60, 70, 100]
  19. return b - a //大到小 [100, 70, 60, 50, 40, 30, 20]
  20. })
  21. console.log(arr)
  22. </script>

2.1字符串对象api

  1. <script>
  2. let str = '再也没有留恋的斜阳'
  3. //1.字符串也有下标
  4. console.log(str[str.length - 1])
  5. /* 2.str.indexOf() : 返回某个指定字符首字符的下标位置
  6. 应用:检查字符串中是否有某个字符
  7. 返回值
  8. 如果有,则返回对应字符的首字符的下标
  9. 如果没有则返回 -1
  10. */
  11. console.log(str.indexOf('斜阳')) //7
  12. console.log(str.indexOf('你好')) //-1
  13. /*
  14. 3.str.split('分隔符') 切割字符串 返回的一定是数组
  15. */
  16. let str1 = '黄雅兰&🐖'
  17. console.log(str1.split('&'))
  18. //4.str.substr(其实下标,截取长度) 截取部分字符串
  19. console.log(str1.substr(1, 2))
  20. //5.大小写转换
  21. console.log('abcdefgHIJKLMN'.toLocaleLowerCase()) //转小写abcdefghijklmn
  22. console.log('abcdefgHIJKLMN'.toLocaleUpperCase()) //转大写ABCDEFGHIJKLMN
  23. console.log('沉默是金'.toLocaleUpperCase()) //汉字返回的是本身 沉默是金
  24. </script>

3.原型对象

1.面向对象 : 是一种注重结果的思维方式
2.面向对象与面向过程关系 : 面向对象基于面向过程的封装
面向对象:注重结果
面向过程:过程
3.构造函数创建对象(new关键字四个流程)
a.创建一个空对象
b.this指向这个对象
c.对象赋值
d.返回这个对象
4.原型对象 : 函数在声明的时候,系统会自动创建与之对应的对象。 原型对象
作用:解决资源浪费+变量污染
使用:
构造函数.prototype
实例对象可以直接用原型成员
5.构造函数、原型对象、实例对象三角关系
1. prototype : 属于构造函数,指向原型对象
作用:解决资源浪费+变量污染
2.proto : 属于实例对象,指向原型对象
作用: 可以让实例对象访问原型中的成员
3.constructor: 属于原型对象,指向构造函数
* 作用: 可以让实例对象 知道自己被哪一个构造函数创建的

  1. //1.构造函数
  2. function Person(name,age){
  3. this.name = name
  4. this.age = age
  5. }
  6. //2.原型对象
  7. Person.prototype.eat = function(){
  8. console.log('我要吃东西')
  9. }
  10. console.log( Person.prototype.constructor )//Person
  11. //3.实例对象
  12. let p1 = new Person('ikun',30)
  13. console.log( p1 )
  14. //查看p1的构造函数
  15. console.log( p1.constructor )//p1.__proto__.constructor
  16. /*
  17. __proto__ : 属于实例对象,指向原型对象
  18. __proto__属性不是web标准属性,在浏览器看不见。
  19. 但是代码可以用。 实际开发中一定要省略__proto__.
  20. */
  21. p1.eat()//底层 p1.__proto__.eat()
  22. //检测构造函数、原型对象、实例对象三者之间关系: 两行代码
  23. console.log( p1.__proto__.constructor === Person )//true (你爹是你爹)
  24. console.log( p1.__proto__ === Person.prototype )//true (你妈是你妈)

3.1工厂函数

用于创建多个对象的函数

  1. <script>
  2. /* 解决代码冗余方案:封装函数
  3. 工厂函数 : 用于创建多个对象的函数
  4. */
  5. function createPerson(name,age){
  6. //1.声明对象
  7. let p = {}
  8. //2.对象赋值
  9. p.name = name
  10. p.age = age
  11. //3.返回值
  12. return p
  13. }
  14. let p1 = createPerson('ikun',30)
  15. let p2 = createPerson('班长',20)
  16. let p3 = createPerson('班主任',40)
  17. console.log(p1,p2,p3)
  18. </script>

3.2构造函数

js中,所有的对象都是由构造函数创建创建

{}:new Object 内置对象函数
自定义构造函数
new关键字工作原理
1.创建空对象
2.this指向这个创建的空对象
3.给对象赋值
4.返回这个对象
5.构造函数首字母大写;提醒不要忘记new关键字
6.如果构造函数主动写return
如果返回的是值类型 ,无效
如果返回的是引用数据类型,会覆盖new创建的对象

  1. <script>
  2. function Iphone(price, color) {
  3. //1.创建一个空对象
  4. //2.this指向空对象
  5. //3.给对象新增属性
  6. this.price = price
  7. this.color = color
  8. //4.返回这个对象 return this
  9. }
  10. let phone = new Iphone(8888, 'red')
  11. let phone2 = new Iphone(666, 'black')
  12. //5.构造函数首字母大写;提醒不要忘记new关键字
  13. //6.如果构造函数主动写return
  14. // 如果返回的是值类型 ,无效
  15. // 如果返回的是引用数据类型,会覆盖new创建的对象
  16. console.log(phone, phone2)
  17. </script>

3.3静态成员与实例成员

静态成员:属于函数自己的成员
实例成员:属于实例对象的成员
4

4.面向对象三大特征

封装 继承 多态

1.封装 :把代码放到对象中的方法
2.继承 :一个对象有另一个对象的所有的成员
继承的三种方式:
混入式:遍历父对象的成员,添加给子对象
应用场景:适用于单个对象继承
弊端:没继承一次,就要执行一次循环
替换原型:父对象 作为子对象构造函数原型
应用场景:给内置构造函数添加方法
弊端:会覆盖原先的原型
混合式:混入+替换原型.遍历父对象的成员,添加到子对象的构造函数
3.多态 : 一个对象在不同情况下的不同状态

4.1混入式继承

遍历父对象的成员,添加给子对象

  1. let father = {
  2. house : {
  3. address:'武汉天地',
  4. price : 10000000
  5. },
  6. car:{
  7. brand:'劳斯莱斯幻影',
  8. price:8000000
  9. }
  10. }
  11. //子对象
  12. let son = {
  13. name:'爽哥',
  14. age:20
  15. }
  16. /*
  17. 混入式继承 : 遍历父对象成员,添加给子对象
  18. 特点: 单个对象继承
  19. */
  20. for(let key in father){
  21. son[key] = father[key]
  22. }
  23. console.log( son )

4.2替换原型

替换原型:父对象 作为子对象构造函数原型

  1. //父对象
  2. let father = {
  3. house : {
  4. address:'武汉天地',
  5. price : 10000000
  6. },
  7. car:{
  8. brand:'劳斯莱斯幻影',
  9. price:8000000
  10. }
  11. }
  12. //子对象构造函数
  13. function Son(name,age){
  14. this.name = name
  15. this.age = age
  16. }
  17. //把父对象 赋值给子对象的原型
  18. Son.prototype = father
  19. let son1 = new Son('爽哥',20)
  20. let son2 = new Son('ikun',30)
  21. console.log(son1,son2)

4.3混合式继承

混入+替换原型.遍历父对象的成员,添加到子对象的构造函数原型

  1. //父对象
  2. let father = {
  3. house : {
  4. address:'武汉天地',
  5. price : 10000000
  6. },
  7. car:{
  8. brand:'劳斯莱斯幻影',
  9. price:8000000
  10. }
  11. }
  12. //子对象构造函数
  13. function Son(name,age){
  14. this.name = name
  15. this.age = age
  16. }
  17. //遍历父对象成员
  18. for(let key in father){
  19. //添加到子对象的构造函数原型里
  20. Person.prototype[key] = father[key]
  21. }
  22. let zi = new Person('刘德华', 17)

5.原型链

5.1原型链

1.原型链:每一个实例对象都有自己的原型,原型也是对象,也有自己的原型,以此类推,
形成链式结构,称之为原型链
2.原型链访问规则:就近原则
3.原型链的作用:继承
image.png

  1. //构造函数 : 创建对象
  2. function Person(name,age){
  3. this.name = name
  4. this.age = age
  5. }
  6. //原型对象添加方法
  7. Person.prototype.eat = function(){
  8. console.log('吃东西')
  9. }
  10. //给原型对象添加属性
  11. Person.prototype.type = '哺乳动物'
  12. Person.prototype.name = '中国人'
  13. //实例对象
  14. let p1 = new Person('陈爽',20)
  15. let p2 = new Person('ikun',30)
  16. console.log(p1,p2)
  17. /* 请说出下列代码运行结果 */
  18. console.log( p1.name )//陈爽 p1自己有name
  19. console.log( p1.type )//哺乳动物 p1没有type,但是p1的原型有
  20. p1.eat()//'吃东西' p1自己没有eat,但是原型有
  21. // p1.learn()//报错
  22. //为什么p1没有toString,p1的原型也没有toString,但是不报错
  23. //原因: p1的原型的原型有toString()
  24. p1.toString()
  25. //查看p1的原型的原型
  26. console.log( p1.__proto__.__proto__.constructor )//Object 内置构造函数
  27. console.log( p1.__proto__.__proto__ === Object.prototype )//true
  28. //查看p1的原型的原型的原型
  29. console.log( p1.__proto__.__proto__.__proto__ )//null

5.2内置对象原型链

  1. //数组实例对象
  2. let arr = [10,20,30]// new Array(10,20,30)
  3. //1.查看数组的原型 : 2行代码
  4. console.log( arr.__proto__.constructor )//Array
  5. console.log( arr.__proto__ === Array.prototype )//true
  6. //2.查看数组的原型的原型
  7. console.log( arr.__proto__.__proto__.constructor )//Object
  8. console.log( arr.__proto__.__proto__ === Object.prototype )//true
  9. //字符串实例
  10. let str = 'abc'// new String('abc')
  11. //查看str的原型
  12. console.log( str.__proto__.constructor )//String
  13. console.log( str.__proto__ === String.prototype )//true\
  14. //2.查看数组的原型的原型
  15. console.log( str.__proto__.__proto__.constructor )//Object
  16. console.log( str.__proto__.__proto__ === Object.prototype )//true

5.3DOM对象原型链

image.png

5.4 instanceof运算符作用

  1. <script>
  2. /*
  3. instanceof关键字(运算符) : 检测 构造函数的原型 在不在 实例对象的原型链中
  4. 实例对象 instanceof 构造函数
  5. 在:true
  6. 不在:false
  7. instanceof运算符作用 : 限制函数的参数数据类型
  8. */
  9. //数组实例对象
  10. // arr->Array.prototype->Object.prototype->null
  11. let arr = [10,20,30]
  12. console.log( arr instanceof Array )//true
  13. console.log( arr instanceof Object )//true
  14. console.log( arr instanceof String )//false
  15. //报错: 参数不是节点
  16. //instanceof关键字一般用在函数中,用于限制参数的数据类型
  17. //例如 appendChild()参数只能是node节点,如果你传了其他类型就会报错。底层就会用到instanceof来检测参数数据类型
  18. let h1 = document.createElement('h1')
  19. h1.innerText = '我是h1标签'
  20. document.body.appendChild(h1)
  21. </script>

5.5 ES6类继承

  1. <script>
  2. /*
  3. 1.class关键字 : 声明类函数 (构造函数)
  4. * 语法:
  5. class 类函数名{
  6. //构造函数
  7. constructor(){
  8. }
  9. //原型方法
  10. eat(){
  11. }
  12. }
  13. * 特点: class类函数必须要用new关键字,否则代码报错
  14. 2.extends关键字 : 类继承
  15. * 语法:
  16. class 子类函数 extends 父类函数{
  17. }
  18. 3. super关键字 : 子类调用父类方法
  19. * 应用场景: 写在子类的constructor方法中
  20. * 语法 :
  21. class 子类函数 extends 父类函数{
  22. //子类构造函数
  23. constructor(name,age,score){
  24. super(name,age)
  25. this.score = score
  26. }
  27. //子类原型方法
  28. learn(){
  29. }
  30. }
  31. */
  32. //父类函数:Person
  33. class Person{
  34. //构造函数
  35. constructor(name,age){
  36. this.name = name
  37. this.age = age
  38. }
  39. //原型方法
  40. sayHi(){
  41. console.log('i love you')
  42. }
  43. }
  44. //子类函数:teacher
  45. class Teacher extends Person{
  46. //构造函数
  47. constructor(name,age,className){
  48. //父类方法
  49. super(name,age)
  50. this.className = className
  51. }
  52. //子类原型
  53. learn(){
  54. console.log('一日为师终身为友')
  55. }
  56. }
  57. let t1 = new Teacher('ikun',30,'71期')
  58. console.log(t1)
  59. </script>

5.6 函数补充

  1. /*
  2. 1.函数中的关键字: arguments
  3. 作用:arguments:获取函数所有的实参(伪数组)
  4. 场景: 例如 arr.push() 底层就是用arguments获取实参
  5. 2.rest参数(剩余参数) : ...形参名
  6. 作用:获取所有的剩余参数(真数组)
  7. 场景: 绝大多数情况下,剩余参数可以替代arguments
  8. */
  9. function fn(...b){
  10. console.log(b)//获取所有的剩余参数
  11. console.log( arguments )//获取函数所有实参
  12. }
  13. fn(10,20,30,40,50)
  14. /* 函数默认参数
  15. 1.函数传参本质是:实参给形参赋值
  16. 2.函数形参和实参数量可以不一致,但是会按照顺序一一赋值
  17. 3.函数默认参数:
  18. ES5 : 逻辑 或 中断
  19. ES6 : 形参 = 默认值
  20. */
  21. function fn(a=10,b=20){
  22. //找真 : 左边为真,返回左边的值。反之返回右边的值
  23. // a = a || 10
  24. // b = b || 20
  25. console.log(a)//5
  26. console.log(b)//20
  27. }
  28. fn(5)//底层 fn(5,undefined)

二、函数进阶

1.this三种指向

  1. /*
  2. 1.this环境对象 : 谁‘调用’我,我就指向谁 (相当于中文‘我’,谁说出这个字,这个字就代表谁)
  3. * 重点: 与函数声明无关,取决于函数调用
  4. * (1)普通函数调用: 函数名() -> this指向window
  5. * (2)对象方法调用: 对象名.方法名() -> this指向这个对象
  6. * (3)构造函数调用: new 函数名() -> this指向new创建的实例对象
  7. 2.分析this指向心得 : this指向取决于函数调用,而函数有三种调用方式。所以this指向也会有三种情况
  8. (1)优先用排除法分析, 如果有new关键字,则指向new创建的对象。 否则不是window就是对象
  9. (2)如何判断this是window还是对象,就看调用是: 函数名() 还是 对象名.方法名()
  10. */
  11. function fn(){
  12. console.log( this )
  13. }
  14. //1.普通函数
  15. fn()//window
  16. let obj = {
  17. name:'ikun',
  18. age:30,
  19. eat:fn,
  20. sayHi:function(){
  21. function fn1(){
  22. //这里this指向window, this指向取决于调用
  23. console.log( this )//
  24. }
  25. fn1()
  26. }
  27. }
  28. obj.sayHi()
  29. //2.对象方法
  30. obj.eat()
  31. //3.构造函数
  32. new fn()

2.函数上下文调用

1.默认情况下,this指向是无法修改的。
普通函数:函数名() this指向window
对象方法: 对象名.方法名() this指向对象
构造函数: new 函数名() this指向new创建的对象
2.上下文调用 : 修改函数中this指向
(1)函数名.call(修改的this,参数1,参数2,….)
(2)函数名.apply(修改的this, 数组/伪数组 )
(3)函数名.bind(修改this)
bind不会立即执行函数,而是得到一个修改this之后的新函数(一次修改,终生受用)
细节: 如果你在bind后面传了函数参数,那么参数也会绑定。之后传参无效
3.经典面试题:请说说call、apply、bind有什么异同点
相同点:都是修改函数中this指向
不同点:
3.1 传参方式不同 : call是一一传参,apply是数组/伪数组传参
3.2 执行机制不同 : call、apply会立即执行函数, bind不会立即执行函数,会返回一个新的函数

2.1 call上下文调用

函数名.call(修改的this,参数1,参数2,….)

  1. function fn(a,b){
  2. console.log( this )
  3. console.log(a+b)
  4. }
  5. fn(1,2)//this指向window
  6. //函数名.call(修改的this,参数1,参数2,....)
  7. fn.call({name:'张三'},10,20)
  8. //--------应用场景:检测数据类型
  9. // 修改this,检测数据的数据类型
  10. console.log(Object.prototype.toString.call(123)) //[object Number]
  11. console.log(Object.prototype.toString.call('123')) //[object String]
  12. console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
  13. console.log(Object.prototype.toString.call(null)) //[object Null]
  14. console.log(Object.prototype.toString.call(false)) //[object False]
  15. console.log(Object.prototype.toString.call(function () {})) //[object Function]
  16. console.log(Object.prototype.toString.call([1, 2, 3])) //[object Array]
  17. console.log(Object.prototype.toString.call(obj)) //[object Object]
  18. console.log(Object.prototype.toString.call(Array.prototype)) //[object Window]
  19. console.log(Object.prototype.toString()) //[object 原型obj]
  20. console.log(this)
  21. console.log('-- -- -- -- -- -- - -- -- -- -- -- -- -- -- -- -- -- -- --')

2.2 apply上下文调用

函数名.apply(修改的this, 数组/伪数组 )

  1. function fn(a,b){
  2. console.log(this)
  3. console.log(a+b)
  4. }
  5. //函数名.apply(修改的this, 数组/伪数组 )
  6. //apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
  7. fn.apply({name:'李四'},[30,40])
  8. //应用场景---------伪数组转真数组
  9. /*
  10. 伪数组: 有数组三要素(下标、元素、长度),但是不能使用数组的方法
  11. 伪数组 本质是对象
  12. 伪数组不能使用数组方法: 伪数组的原型指向对象的原型,而不是Array的原型
  13. */
  14. let obj = {
  15. 0:20,
  16. 1:50,
  17. 2:88,
  18. 3:66,
  19. length:4
  20. }
  21. console.log( obj )
  22. //需求:伪数组转成真数组
  23. //ES5 : 上下文
  24. let newArr = []
  25. //函数名.apply(修改的this, 数组/伪数组 )
  26. //第一个参数: newArr 本来this就是newArr,这里面不需要修改this.(相当于this不变)
  27. //第二个参数: obj. 借助apply特点:自动遍历伪数组/数组,逐一传参(自己省去了for循环)
  28. // newArr.push(obj[0],obj[1],obj[2],obj[3])
  29. newArr.push.apply(newArr,obj)
  30. console.log( newArr )
  31. //ES6(推荐) : Array.from(伪数组)
  32. let arr = Array.from(obj)
  33. console.log(arr)
  34. //应用场景--------数组求最大值
  35. //求数组最大值
  36. let arr = [10,20,50,60,88,30]
  37. //1.排序法 : 从大到小排序,取0下标
  38. // arr.sort(function(a,b){
  39. // return b-a
  40. // })
  41. // console.log(arr[0])//数组从大到小排序,第一个元素就是最大值
  42. //2.擂台法
  43. // let max1 = arr[0]
  44. // for(let i = 1;i<arr.length;i++){
  45. // if( arr[i]>max1){
  46. // max1 = arr[i]
  47. // }
  48. // }
  49. // console.log( max1 )
  50. //3.ES5 : Math.max.apply(Math,数组名)
  51. //函数名.apply(修改的this, 数组/伪数组 )
  52. //第一个参数 Math : this本来就是Math,这里也不需要修改this (传Math相当于this不变)
  53. //第二个参数 arr : 借助apply特点。 自动遍历数组/伪数组。 逐一传参
  54. let max2 = Math.max.apply(Math,arr)
  55. console.log(max2)
  56. //4.ES6(推荐) :
  57. let max3 = Math.max(...arr)
  58. console.log(max3)

2.3 bind上下文调用

函数名.bind(修改this)
bind不会立即执行函数,而是得到一个修改this之后的新函数(一次修改,终生受用)
细节: 如果你在bind后面传了函数参数,那么参数也会绑定。之后传参无效

  1. function fn(a,b){
  2. console.log(this)
  3. console.log(a+b)
  4. }
  5. fn(1,2)//普通函数 this->window
  6. //(1)函数名.call(修改的this,参数1,参数2,....)
  7. fn.call({name:'张三'},10,20)
  8. //(2)函数名.apply(修改的this, 数组/伪数组 )
  9. //apply传参的时候会自动的遍历这个数组,然后按照循序逐一传参 a = [30,40][0] b=[30,40][1]
  10. fn.apply({name:'李四'},[30,40])
  11. //(3)函数名.bind(修改this)
  12. //bind不会立即调用函数,而是得到一个修改this之后的新函数
  13. let newFn = fn.bind({name:'王五'})
  14. newFn(25,66)//此时this指向{name:'王五'}
  15. //应用场景 --------------定时器中的this
  16. //bind场景主要是修改 '不需要立即执行的函数'
  17. // 事件处理函数、定时器函数
  18. //1.定时器中的this一定是window
  19. let fn = function(){
  20. console.log( this )
  21. }
  22. //使用bind修改this
  23. let newFn = fn.bind({name:'666'})
  24. // fn() : 调用函数, 运行结果是函数返回值
  25. // fn : 变量取值, 取出fn中存储的堆地址
  26. setTimeout( newFn ,3000)
  27. //下面这个写法和上面写法是一样的 : 函数是数据类型,也可以像其他数据一样直接使用语法
  28. setTimeout(function(){
  29. console.log(this)
  30. }.bind({name:'干饭'}),3000)

上下文注意点

1.上下文模式: 修改this指向
2.注意点: this一定是指向对象(引用类型)
a.如果修改的this是 : string,number,boolean则编译器会自动转成对应的对象类型。
基本包装类型 : new String() new Number() new Boolean()
b.如果修改的this是: undefined和null,则修改无效

  1. function fn(){
  2. console.log( this )
  3. }
  4. fn.call('123')// new String('123')
  5. fn.call(123)//new Number(123)
  6. fn.call(true)//new Boolean(123)
  7. fn.call(undefined)//window
  8. fn.call(null)//window
  9. fn.call()//window

3.闭包—一个访问其他函数中局部变量的函数

作用:解决变量污染
总结:形成闭包要有两个条件,缺一不可。 (1)函数 (2)访问其他函数内部变量

  1. //获取元素
  2. let input = document.querySelector('input')
  3. let button = document.querySelector('button')
  4. button.onclick = function(){
  5. //有一个变量存储搜索结果
  6. let str = '6666'
  7. //模拟网络请求:2秒钟之后获取搜索结果
  8. setTimeout(function(){
  9. alert(`搜索结束,本次搜索结果是${str}`)
  10. },2000)
  11. }

4.递归函数—函数内部调用自己

注意点:需要满足条件(if)才会递归,否则会导致死循环 递归函数的功能类似于循环

递归函数应用场景 :
2.1 浅拷贝与深拷贝
浅拷贝: 拷贝的是地址, 修改拷贝后的数据对原数据有影响
深拷贝:拷贝的是数据, 修改拷贝后的数据对原数据没有影响
2.2 遍历dom树

4.1递归场景:深拷贝与浅拷贝

浅拷贝

  1. //声明一个对象
  2. //拷贝: 把对象中存储的数据 拷贝一份赋值给其他对象
  3. let obj = {
  4. name:'ikun',
  5. age:30,
  6. hobby:['讲课','学生','学习'],
  7. friend:{
  8. name:'朋友',
  9. sex:'男'
  10. }
  11. }
  12. //浅拷贝: 拷贝的是地址
  13. let obj1 = obj
  14. //由于浅拷贝,拷贝的是地址。 所以修改拷贝后的数据,原来的数据也会变化
  15. obj1.hobby = '美食'
  16. console.log(obj1,obj)

深拷贝一JSON转换

  1. let obj = {
  2. name:'ikun',
  3. age:30,
  4. hobby:['讲课','学生','学习'],
  5. friend:{
  6. name:'朋友',
  7. sex:'男'
  8. }
  9. }
  10. //(1)先把js对象转成json格式字符串 : JSON.stringify(js对象)
  11. //json在把js转成json格式字符串的时候,底层会自动帮你深拷贝
  12. // let json = JSON.stringify(obj)
  13. // console.log( json )
  14. //(2)再把刚才的json字符串,转成js对象 : JSON.parse( json格式 )
  15. // let js = JSON.parse( json )
  16. // js.hobby = '学习'
  17. // console.log( js,obj )
  18. //以上两个流程,可以简写成一行代码
  19. let newObj = JSON.parse( JSON.stringify( obj ) )
  20. newObj.friend = '71期'
  21. console.log(newObj,obj)

深拷贝二

  1. let obj = {
  2. name:'ikun',
  3. age:30,
  4. hobby:['讲课','学生','学习'],
  5. friend:{
  6. name:'朋友',
  7. sex:'男'
  8. }
  9. }
  10. /*
  11. (1)遍历obj,把所有的属性添加给newObj
  12. (2)如果obj[key]是引用类型(数组、对象),则不能直接拷贝地址
  13. (2.1)数组:给newObj声明一个空数组,然后遍历obj[key],把里面元素添加给newObj[key]
  14. (2.2)对象:给newObj声明一个空对象,然后遍历obj[key],把里面元素添加给newObj[key]
  15. (3)如果obj[key]不是引用类型,则直接赋值。结束递归
  16. */
  17. function kaobei(newObj,obj){
  18. for(let key in obj){
  19. //判断 obj[key] 是不是数组类型
  20. if( obj[key] instanceof Array ){
  21. //声明一个空数组,然后继续拷贝数组里面的数据
  22. newObj[key] = []
  23. kaobei(newObj[key],obj[key])
  24. }else if(obj[key] instanceof Object){
  25. //声明一个空对象,然后继续拷贝数组里面的数据
  26. newObj[key] = {}
  27. kaobei(newObj[key],obj[key])
  28. }else{
  29. newObj[key] = obj[key]
  30. }
  31. }
  32. }
  33. //调用深拷贝函数
  34. let newObj = {}
  35. kaobei(newObj,obj)
  36. //深拷贝:修改拷贝的数据,对原数据没有影响
  37. newObj.hobby[0] = '111'
  38. console.log( newObj,obj)

4.2遍历DOM树

  1. //arr:数据 father:父盒子
  2. function addElement(arr,father){
  3. //遍历数组,生成div>p添加到父元素中
  4. for(let i = 0;i<arr.length;i++){
  5. //(1)创建空标签
  6. let div = document.createElement('div')
  7. //(2)设置内容
  8. div.innerHTML = `<p>${arr[i].type || arr[i]}</p>`
  9. //(3)添加到父盒子
  10. father.appendChild(div)
  11. //如果菜单还有data,说明还有子菜单,则需要继续遍历添加
  12. if( arr[i].data ){
  13. addElement(arr[i].data,div)
  14. }
  15. }
  16. }
  17. let menu = document.querySelector('.menu')
  18. //调用函数
  19. addElement(arr,menu)

三、ES6新语法

1.let与const关键字

let与const关键字特点

1.变量必须先声明,后使用
2.块级作用域,大括号里是局部变量

let与const区别

let :变量 。可以修改变量的数据
const:常量。只能在声明的时候赋值一次,不可以修改

2.对象解构

2.1取出对象的属性 赋值给变量

  1. let obj = {
  2. name: '王力宏',
  3. age: 18,
  4. sex: '男',
  5. hobby: '按摩',
  6. eat: function () {
  7. console.log('全都要');
  8. }
  9. }
  10. //相当于
  11. /* let name = obj.name
  12. let age = obj.age
  13. let sex = obj.sex
  14. let hobby = obj.hobby
  15. let eat = obj.eat--> function(){}
  16. */
  17. //1.取对象的值,赋值给变量
  18. let {
  19. name,
  20. age,
  21. sex,
  22. hobby,
  23. price,
  24. eat
  25. } = obj
  26. console.log(name, age, sex, hobby, price, eat);

2.2取变量的值 赋值给对象的属性

  1. //2.取变量的值, 赋值给对象的属性
  2. let uname = '路飞'
  3. let ages = 20
  4. let gender = '海贼王'
  5. let nengli = function () {
  6. console.log('橡胶果实');
  7. }
  8. let caoMao = {
  9. uname, //相当于 uname:uname
  10. ages,
  11. gender,
  12. nengli() { //相当于 nengli:function(){}
  13. console.log('橡胶果实');
  14. }
  15. }

2.3解构赋值其他用法

  1. //2.1 解构赋值默认值
  2. let {name,age=18,sex} = obj //age =18 表示如果对象没有age属性,age=18,有的话就是age =obj.age
  3. //2.2 自定义解构变量名
  4. let { sex:gender } = obj 相当于 let gender = obj.sex

3.数组解构

3.1取出数组的元素 赋值给变量

  1. let arr = [10,20,30]
  2. let [n1,n2,n3,n4] = arr
  3. //相当于:
  4. // let n1 = arr[0]
  5. // let n2 = arr[1]
  6. // let n3 = arr[2]
  7. // let n4 = arr[3]
  8. console.log( n1,n2,n3,n4)//10,20,30,undefined

3.2取变量的值 赋值给数组的元素

  1. let num1 = 66
  2. let num2 = 88
  3. let num3 = 100
  4. let arr1 = [num1,num2,num3]

4.函数参数解构

  1. //今后的开发中,很多函数的参数是对象类型
  2. function fn( {uname,pword} ){// {uname,pword} = {uname:'admin',pword:'123456'}
  3. //函数参数进
  4. // let {uname,pword} = obj
  5. console.log( uname,pword )
  6. }
  7. fn({uname:'admin',pword:'123456'})

5.箭头函数

  1. //1.箭头函数语法 就是function简写
  2. let fn = () => {} // fn = function(){}
  3. //2.当函数的参数只有一个的时候,可以省略小括号
  4. let fn1 = a => {
  5. return a * 5
  6. }
  7. console.log(fn1(5))
  8. //3.当函数的参数只有一个,且函数体只有一行的时候,可以省略小括号,大括号,省略了大括号,里面的return关键字也必须省略
  9. let fn3 = b => b * b
  10. console.log(fn3(10))

6.箭头函数this指向

箭头函数this指向 : 箭头函数没有this
箭头函数中使用this,会访问上级作用域this
原因(作用域链):箭头函数没有this, 就会往上级作用域找

  1. let obj = {
  2. name:'张三',
  3. age:20,
  4. eat:function(){
  5. //eat是function函数, eat里面的this指向obj
  6. let fn1 = function(){
  7. //fn1是function函数, this指向window (普通函数调用)
  8. console.log( this )//window
  9. }
  10. fn1()
  11. let fn2 = ()=>{
  12. //fn2是箭头函数, 访问上级作用域(eat函数)中的this
  13. console.log( this )//obj
  14. }
  15. fn2()
  16. },
  17. learn:()=>{
  18. //learn是箭头函数,this指向上级(全局)中的this:window
  19. let fn1 = function(){
  20. //fn1是function函数, this指向window (普通函数调用)
  21. console.log( this )//window
  22. }
  23. fn1()
  24. let fn2 = ()=>{
  25. //fn2是箭头函数,访问上级作用域(learn)this : window
  26. console.log( this )//window
  27. }
  28. fn2()
  29. }
  30. }
  31. obj.eat()
  32. obj.learn()

7.展开运算符 ‘ … ‘

作用:相当于遍历对象简写
应用场景:
连接数组
求数组最大值

  1. //1.用于连接数组 : 页面上拉加载下一页的时候,就需要连接数组
  2. let arr1 = [10,20,30,40]
  3. let arr2 = [50,60,70,80]
  4. //ES5 : arr1.push.apply(arr1,arr2)
  5. // arr1.push(arr2[0],arr2[1],arr2[2],arr2[3])
  6. // arr1.push.apply(arr1,arr2)
  7. //ES6 : arr1.push(...arr2)
  8. arr1.push(...arr2)
  9. console.log(arr1)
  10. //2.用于求数组最大值
  11. let arr = [20,0,60,88,100,50]
  12. //ES5 : Math.max.apply(Math,arr)
  13. let max1 = Math.max.apply(Math,arr)
  14. console.log( max1 )
  15. //ES6 : Math.max(...arr)
  16. let max2 = Math.max(...arr)
  17. console.log( max2 )