当前位置:   article > 正文

JS高级-函数this三种指向,函数上下文调用,闭包,递归 深浅拷贝_js 获取函数的this

js 获取函数的this

函数this的三种指向

01函数this的三种指向

1.环境对象 this : 谁调用我,this指向谁

            this相当于中文中的'我'

        2.this指向取决于函数的调用,函数调用有三种方式    

           (1) 普通函数;  函数名()  this->window

           (2)对象方法:    对象名.方法名  this->对象

           (3) 构造函数;  new 函数名() this-> new创建的实例对象

             一句话总结,this指向三选一   没点没new是window, 有new是实例,有点是点左边的对象

  1. <script>
  2. /*
  3. 1.环境对象 this : 谁调用我,this指向谁
  4. this相当于中文中的'我'
  5. 2.this指向取决于函数的调用,函数调用有三种方式
  6. 普通函数; 函数名() this-window
  7. 对象方法: 对象名.方法名 this-对象
  8. 构造函数; new 函数名() this-new创建的实例对象
  9. 一句话总结,this指向三选一
  10. */
  11. function fn(){
  12. console.log(this);
  13. }
  14. // (1)普通函数
  15. fn() //this -window
  16. //
  17. // (2)构造函数
  18. new fn() //this -new创建的实例对象
  19. let obj={
  20. name:'张三',
  21. eat:fn
  22. }
  23. // (3)对象方法
  24. obj.eat() //this-对象
  25. </script>

02this指向测试

  1. <script>
  2. /*
  3. 环境对象 this :
  4. 普通函数;
  5. 对象方法:
  6. 构造函数;
  7. */
  8. //作用域链
  9. let obj = {
  10. name: "张三",
  11. eat: function() {
  12. console.log(this)
  13. function fn() {
  14. console.log(this)
  15. }
  16. fn()
  17. }
  18. }
  19. let eat = obj.eat
  20. obj.eat()
  21. </script>


02函数的上下文调用

01-call()调用函数

上下文调用 : 修改函数内部的this

1 函数名.call ({修改后的this指向},形参1,形参2…………)

  1. <script>
  2. /*
  3. 1.环境对象 this : 谁调用指向谁
  4. 普通函数;函数名() this-window
  5. 对象方法: 对象名.方法名() this-对象
  6. 构造函数; new 函数名() this-new构造的实例对象
  7. *** 默认情况下,函数内的this是固定的,无法被修改
  8. *** 如果想要动态修改函数内部的this指向,啧需要使用上下文调用方法
  9. * 上下文调用 :函数作用域 上下文指向 修改函数作用域内部this指向
  10. 2.上下文调用 :
  11. 2.1 函数名.call() call(修改的this,参数1,参数2)
  12. 2.2 函数名.apply()
  13. 2.3 函数名.bind()
  14. 3. 面试必问: call 和 apply 和 bind三者区别
  15. */
  16. function fn(a,b){
  17. console.log(this);
  18. console.log(a+b);
  19. }
  20. // 函数名.call(修改的this,参数1,参数2)
  21. fn.call({name:'张三'},10,20)
  22. </script>

 call场景-伪数组转真数组

  //值类型

        // 字符串 数字 布尔 undefined null

  //引用类型      

        // 数组  函数  对象

        函数名.call() 应用:万能数据类型检测 
        
        1.typeof 数据:检测数据类型,但是有两种数据类型无法检测
         *typeof无法检测 数组和null两种数据类型得到的都是 object
        2.万能数据类型检测 :Object.prototype.toString.call(数据)
        加call的原因是 Object.prototype.toString 检测的是它自身的数据类型 

  1. <script>
  2. /* 函数名.call() 应用:万能数据类型检测 */
  3. /*
  4. 1.typeof 数据:检测数据类型,但是有两种数据类型无法检测
  5. *typeof无法检测 数组和null两种数据类型得到的都是 object
  6. 2.万能数据类型检测 :Object.prototype.toString.call(数据)
  7. 加call的原因是 Object.prototype.toString 检测的是它自身的数据类型
  8. */
  9. //值类型
  10. // 字符串 数字 布尔 undefined null
  11. let str='abc'
  12. let num=123
  13. let bol=true
  14. let und=undefined
  15. let nul=null
  16. //引用类型
  17. // 数组 函数 对象
  18. let arr=[1,2,3]
  19. let fn=function(){}
  20. let boj={name:'张三'}
  21. // 万能检测:
  22. console.log(Object.prototype.toString.call(str)); // [object String]
  23. console.log(Object.prototype.toString.call(num)); // [object Number]
  24. console.log(Object.prototype.toString.call(bol)); // [object Boolean]
  25. console.log(Object.prototype.toString.call(und)); // [object Undefined]
  26. console.log(Object.prototype.toString.call(unl)); // [object Null]
  27. console.log(Object.prototype.toString.call(arr)); // [object Array]
  28. console.log(Object.prototype.toString.call(fn)); // [object Function]
  29. console.log(Object.prototype.toString.call(obj)); // [object Object]
  30. // (1)Object.prototype.toString()内部会返回this的固定类型,得到固定格式字符串‘[object 数据类型 ]’
  31. // (2)使用object原型中的toString()要想得到数据类型,只需要把this修改成你想要检测的对象
  32. </script>

 apply()调用函数

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

apply会自动帮你遍历数组,然后按照顺序逐一传参

  1. <script>
  2. function fn(a,b){
  3. console.log(this);
  4. console.log(a+b);
  5. }
  6. // (1) 函数名.call(修改的this,参数1,参数2)
  7. fn.call({name:'张三'},10,20)
  8. // (2)函数名.apply(修改的this,数组/伪数组)
  9. // apply会自动遍历数组和伪数组 ,然后逐一传参
  10. fn.apply({name:'李四'},[50,60])
  11. </script>

 apply场景 :伪数组转数组

 apply()场景 :伪数组转真数组

        伪数组: 有 数组三要素(下标,元素 ,长度),不能使用数组的方法

         *伪数组的本质是 对象

  1. <script>
  2. /*
  3. apply()场景 :伪数组转真数组
  4. 伪数组: 有 数组三要素(下标,元素 ,长度),不能使用数组的方法
  5. *伪数组的本质是 对象
  6. */
  7. let obj={
  8. 0:10,
  9. 1:20,
  10. 2:30
  11. }
  12. console.log(obj);
  13. // 需求 有时候伪数组 想要使用真数组的方法 就需要把伪数组转换成真数组
  14. // (1)把伪数组的元素取出,push到真数组中
  15. let arr=[]
  16. // arr.push(obj[0],obj[1],obj[2])
  17. // console.log(arr)
  18. // (2)手动写循环遍历添加
  19. // for (let i = 0; i < obj.length; i++) {
  20. // arr.push(obj[i])
  21. // }
  22. // (3)arr.push.apply(arr,伪数组)
  23. // 这里使用apply不是为了修改this ,而是借助apply的传参特点: 自动遍历伪数组/数组传参 所以第一个参数应该写arr(保持this不变)
  24. arr.push.apply(arr,obj)
  25. console.log(arr);
  26. // ES6 伪数组转真数组,固定静态方法 Array.from(伪数组)
  27. let newArr=Array.from(obj)
  28. console.log( newArr);
  29. </script>

ES6 语法

伪数组转真数组,固定静态方法 Array.from(伪数组)

 let newArr=Array.from(obj)

    console.log( newArr);

apply场景02求数组最大值

 apply()场景 :求数组最大值

  1. <script>
  2. /*
  3. apply()场景 :求数组最大值
  4. */
  5. let arr=[4,845,65,8,99,24,3]
  6. // (1)js基础 :擂台思想
  7. let max=arr[0]
  8. for (let i = 1; i < arr.length; i++) {
  9. if(arr[i] >max){
  10. max=arr[i]
  11. }
  12. }
  13. console.log(max);
  14. // (2)js高级 Math.max
  15. let max1=Math.max.apply(Math,arr)
  16. console.log(max1);
  17. // ES6 :Math.max(...arr)
  18. // 功能类似于apply,也会自动的把数组给遍历
  19. let max2=Math.max(...arr)
  20. console.log(max2);
  21. </script>

 bind()调用函数

 1.环境对象 this : 谁调用指向谁

            普通函数;函数名()  this-window

            对象方法:  对象名.方法名() this-对象

            构造函数;  new 函数名() this-new构造的实例对象

   *** 默认情况下,函数内的this是固定的,无法被修改

   *** 如果想要动态修改函数内部的this指向,则需要使用上下文调用方法

     * 上下文调用 :函数作用域 上下文指向 修改函数作用域内部this指向

        2.上下文调用 :

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

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

            2.3 函数名.bind() 函数名.bind(修改的this)

             *bind 不会立即执行函数,而是得到修改的this的新函数

             *bind() 一般修改:定时器函数 ,事件处理函数

  1. <script>
  2. function fn(a,b){
  3. console.log(this);
  4. console.log(a+b);
  5. }
  6. // (1) 函数名.call(修改的this,参数1,参数2)
  7. fn.call({name:'张三'},10,20)
  8. // (2)函数名.apply(修改的this,数组/伪数组)
  9. // apply会自动遍历数组和伪数组 ,然后逐一传参
  10. fn.apply({name:'李四'},[50,60])
  11. //(3)函数名.bind(修改的this)
  12. //bind 不会立即执行函数,而是得到一个修改的this之后的新函数
  13. let newFn=fn.bind({name:'王五'})
  14. newFn(22,33)
  15. </script>

 bind场景-修改定时器this

/* bind()场景: 修改定时器的this */

        // 定时器中的this默认指向window,如果要修改定时器的this,就需要使用this

  1. <script>
  2. /* bind()场景: 修改定时器的this */
  3. // 定时器中的this默认指向window,如果要修改定时器的this,就需要使用this
  4. let fn= function (){
  5. console.log(this);
  6. }
  7. let newFn=fn.bind({name:'1111'})
  8. setTimeout(newFn,2000)
  9. // 上面代码可以简写一行
  10. // setTimeout(function(){}.bind(),2000)
  11. setTimeout(function(){
  12. console.log(this);
  13. }.bind({name:'李四'}),2000)
  14. /*
  15. 变量:是内存空间 只有存储功能,没有运算功能
  16. 变量只有两种语法 :存 ,取
  17. 字面量 :是数据 .只有运算功能,没有存储功能
  18. */
  19. // let arr=[10,20,30]
  20. // [10,20,30][0]
  21. </script>

 经典面试题 call apply bind区别

3. 面试必问:  call 和 apply 和 bind三者区别

   相同点 都可以修改this指向

   不同点

     (1)传参方式不同 call是单个传参,apply是数组/伪数组传参

     (2)执行机制不同 call和apply会立即执行函数,bind不会立即执行而是得到修改this的新函数    

03闭包

 1.闭包closure是什么 :  两个条件 a:函数 b:函数内部还要访问 其他函数的变量

        其他函数的变量不能是全局变量,也不能是自己的变量,必须是其他函数的

            (1)闭包是一个 访问其他函数内部变量 的函数

            (2) 闭包 =函数 + 上下文引用

           

        2.闭包作用 : 解决变量污染

  1. <script>
  2. //num +fn1()组成了闭包
  3. let age=20
  4. function fn(){
  5. let num=20
  6. function fn1(){
  7. console.log(num);
  8. console.log(age);
  9. }
  10. fn1()
  11. }
  12. fn()
  13. </script>

闭包案例

  1. <script>
  2. // 打事件断点 从函数体开始 点击事件类型
  3. // 点击
  4. document.querySelector('.btn').addEventListener('click',function(){
  5. // (1)获取输入框文本
  6. let text=document.querySelector('input').value
  7. // (2)模拟网络请求
  8. setTimeout(function(){
  9. alert(`${text}的搜索结果为123456`)
  10. },1000)
  11. })
  12. </script>

04递归

1.递归函数: 一个函数 在内部 调用自己

* 递归作用和循环类似的,也需要有结束条件

 1.递归函数:  在函数中调用自己

        *递归类似于循环,也要有结束条件

  1. <script>
  2. /*
  3. 1.递归函数: 在函数中调用自己
  4. *递归类似于循环,也要有结束条件
  5. 2.递归应用:
  6. */
  7. function fn(){
  8. console.log('hh');
  9. // 递归调用
  10. fn()
  11. }
  12. // 双函数递归
  13. function fn1(){
  14. console.log('嘿嘿');
  15. fn2()
  16. }
  17. function fn2(){
  18. console.log('呵呵');
  19. fn1()
  20. }
  21. </script>

 02浅拷贝与深拷贝json实现

 1. 浅拷贝与深拷贝 有两种实现方式  

        浅拷贝:拷贝地址,修改拷贝后的数据对原数据  有影响

        深拷贝 :拷贝数据,修改拷贝后的数据对原数据  没有影响

        2.深拷贝两种方式

        (1)  json方式  let newObj=JSON.parse(JSON.stringify(js对象))

        */  

浅拷贝

浅拷贝 :拷贝地址

浅拷贝如果是一层对象,不互相影响,可以直接拷贝值

如果是复杂类型的拷贝 ,出现多层拷贝会相互影响

  1. <script>
  2. let obj = {
  3. name:'张三',
  4. age:20,
  5. sex:'男',
  6. hobby:['吃饭','睡觉','学习']
  7. }
  8. let newObj=obj
  9. newObj.name='李四'
  10. console.log(obj.newObj);
  11. </script>

深拷贝

深拷贝拷贝的是对象,不是地址

常见方法

1,通过递归实现深拷贝

2.lodash/cloneDeep

3.通过JSON.stringfiy()实现

js库loadsh里面cloneDeep内实现了深拷贝

 json深拷贝

// json 深拷贝

//   (1)JSON.stringify(js对象):把js对象 ->json字符串(json 底层会自动深拷贝)

  1. <script>
  2. let obj = {
  3. name:'张三',
  4. age:20,
  5. sex:'男',
  6. hobby:['吃饭','睡觉','学习']
  7. }
  8. // let json =JSON.stringify(obj)
  9. // (2) JSON.parse(json字符串) :json字符串 ->js对象
  10. // let newObj=JSON.parse(json)
  11. // 简写一行
  12. let newObj=JSON.parse(JSON.stringify(obj))
  13. newObj.name='李四'
  14. newObj.hobby[0]='游戏'
  15. console.log(obj,newObj);
  16. </script>

 03浅拷贝与深拷贝  递归实现

        2.递归应用:

            浅拷贝与深拷贝 :

            json方式  let newObj=JSON.parse(JSON.stringify(js对象))

               

            遍历dom树

如果是数据,直接进行拷贝 如果是数组或者对象 要 (1)声明一个空数组 (2)递归遍历旧的数组,把旧的数组里的数据追加给新的数组

(两者数组和对象的地址一样,所以修改新的数据的同时修改了旧的数据)

  1. <script>
  2. /*
  3. 1.递归函数:
  4. 2.递归应用:
  5. 浅拷贝与深拷贝 :
  6. json方式 let newObj=JSON.parse(JSON.stringify(js对象))
  7. 遍历dom树
  8. */
  9. let obj = {
  10. name:'张三',
  11. age:20,
  12. sex:'男',
  13. hobby:['吃饭','睡觉','学习'],
  14. student:{
  15. name:"班长",
  16. score:90
  17. }
  18. }
  19. // 深拷贝函数封装
  20. function kaobei(obj,newObj){
  21. // 遍历obj ,把里面的数据拷贝给newObj
  22. for(let key in obj){
  23. // 判断是不是数组,如果是数组还需要继续遍历拷贝
  24. if(obj[key] instanceof Array){
  25. // (1)声明一个空数组
  26. newObj[key]=[]
  27. // (2)递归遍历数组
  28. kaobei(obj[key],newObj[key])
  29. }else if(obj[key] instanceof Object){
  30. // (1)声明一个空数组
  31. newObj[key]=[]
  32. // (2)递归遍历数组
  33. kaobei(obj[key],newObj[key])
  34. }else{
  35. newObj[key]=obj[key]
  36. }
  37. }
  38. }
  39. </script>

递归遍历dom数 (了解)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>Document</title>
  8. <style>
  9. * {
  10. padding: 0;
  11. margin: 0;
  12. }
  13. .menu p {
  14. width: 100px;
  15. border: 3px solid;
  16. margin: 5px;
  17. }
  18. .menu > div p {
  19. margin-left: 10px;
  20. border-color: red;
  21. }
  22. .menu > div > div p {
  23. margin-left: 20px;
  24. border-color: green;
  25. }
  26. .menu > div > div > div p {
  27. margin-left: 30px;
  28. border-color: yellow;
  29. }
  30. </style>
  31. </head>
  32. <body>
  33. <div class="menu">
  34. <!-- <div>
  35. <p>第一级菜单</p>
  36. <div>
  37. <p>第二级菜单</p>
  38. <div>
  39. <p>第三级菜单</p>
  40. </div>
  41. </div>
  42. </div> -->
  43. </div>
  44. <script>
  45. //服务器返回一个不确定的数据结构,涉及到多重数组嵌套
  46. let arr = [
  47. {
  48. type: "电子产品",
  49. data: [
  50. {
  51. type: "手机",
  52. data: ["iPhone手机", "小米手机", "华为手机"]
  53. },
  54. {
  55. type: "平板",
  56. data: ["iPad", "平板小米", "平板华为"]
  57. },
  58. {
  59. type: "智能手表",
  60. data: []
  61. }
  62. ]
  63. },
  64. {
  65. type: "生活家居",
  66. data: [
  67. {
  68. type: "沙发",
  69. data: ["真皮沙发", "布沙发"]
  70. },
  71. {
  72. type: "椅子",
  73. data: ["餐椅", "电脑椅", "办公椅", "休闲椅"]
  74. },
  75. {
  76. type: "桌子",
  77. data: ["办公桌"]
  78. }
  79. ]
  80. },
  81. {
  82. type: "零食",
  83. data: [
  84. {
  85. type: "水果",
  86. data: []
  87. },
  88. {
  89. type: "咖啡",
  90. data: ["雀巢咖啡"]
  91. }
  92. ]
  93. }
  94. ]
  95. // 封装一个添加菜单的函数
  96. function addElement (arr,father) {
  97. for(let i = 0;i<arr.length;i++){
  98. let div = document.createElement('div')
  99. div.innerHTML = `<p>${arr[i].type || arr[i]}</p> `
  100. father.appendChild(div)
  101. if(arr[i].data) {
  102. addElement (arr[i].data,div)
  103. }
  104. }
  105. }
  106. // 调用函数
  107. addElement (arr,document.querySelector('.menu'))
  108. </script>
  109. </body>
  110. </html>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/63539
推荐阅读
相关标签
  

闽ICP备14008679号