2021-04-22JS高级第三天

一、函数的定义和调用

1.函数的定义方式

(1) 函数声明方式 function 关键字 (命名函数)
  function fn( ){ }
(2)函数表达式(匿名函数)
  var fn = function( ){ }
(3)方式3 new Function( )
  var fn = new Function('参数1','参数2'..., '函数体')

注意:①Function 里面参数与函数体都必须是字符串格式
   ②所有函数都是 Function 的实例(对象)
   ③函数也属于对象

2.函数的6种调用
     /* 1. 普通函数 */
      function fn() {
        console.log('人生的巅峰');
      }
      fn();

      /* 2. 对象的方法 */
      var o = {
        sayHi: function () {
          console.log('人生的巅峰');
        },
      };
      o.sayHi();

      /* 3. 构造函数*/
      function Star() {}
      new Star();

      /* 4. 绑定事件函数*/
      btn.onclick = function () {}; // 点击了按钮就可以调用这个函数

      /* 5. 定时器函数*/
      setInterval(function () {}, 1000);// 这个函数是定时器自动1秒钟调用一次
      
      /* 6. 立即执行函数(自调用函数)*/
      (function () {
        console.log('人生的巅峰');
      })();

二、函数的this指向

1.六中函数的this指向
捕获.PNG
2.call( )方法改变函数内部this的指向

具体见JS高级第二天。构造函数的继承会用到。

3.apply( )方法改变函数内部this的指向

函数名.apply( this的指向,[ ] )

①applay( )方法可以调用函数
②第一个参数代表this的指向
③第二个参数必须是一个数组(伪数组)
④注意apply( )方法的参数数组有几个元素,原函数的参数就要有几个形参接收。

function fn(a,b,c){
            console.log(this); // 指向对象o
            console.log(a,b,c);
        }
        o = {name: '吴磊'};
        fn.apply(o,[1,2,3]);
  • 应用:可以利用数学的内置对象求数组的最值等。
// 利用数学的内置对象求数组的最值
        var arr = [12,34,33,64];
        var max = Math.max.apply(Math,arr);
        console.log(max);
4.bind( )方法

函数.bind( this的指向 , 参数1 , 参数2...)

①bind( )方法不会调用函数,但是可以改变函数的this指向
②返回的是一个改变this指向的新函数。(了解)

  • 应用:改变定时器函数的this指向,并且不会调用这个函数。
       var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
          btns[i].addEventListener('click', function () {
            this.disabled = true; //这个this指向事件绑定对象--当前button按钮
            setTimeout(function () {
                this.disabled = false; //改变后的this
            }.bind(this),2000);  //将定时器函数的this指向事件绑定对象--当前button按钮
          });
        }

三、严格模式

1.开启严格模式

(1)给script标签开启严格模式:在script标签内部的最顶端添加" use strict "
(2)给立即执行函数开启严格模式:在function花括号内部的最顶端添加" use strict "
(3)给单个函数开启严格模式:在函数内部的最顶端添加" use strict "

2.严格模式的主要变化

(1)严格模式下变量必须声明后才能使用。非严格模式下不声明的变量是全局变量。
(2)严格模式下不允许删除变量。
(3)严格模式下全局作用域中的函数this指向underfined。非严格模式下指向window。

function fn() {
 console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined
}
fn(); 

(4)严格模式下的构造函数必须实例化对象后才能调用,否则this指向underfined会出错。

function Star() {
     this.sex = '男';
}
// Star();严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错.

(5)严格模式下函数的参数不能重名。
(6)严格模式下if语句、for循环语句内不能书写函数。

四、闭包

1.作用域

①函数内部可以使用全局变量。
② 函数外部不可以使用局部变量。
③ 当函数执行完毕,本作用域内的局部变量会销毁。

2.什么是闭包

闭包是一个函数,其他函数作用域可以访问闭包函数中的局部变量。

3.闭包的作用

①可以延伸局部变量的作用域(函数外部可以访问内部的局部变量)
②函数执行完毕后,某些局部变量不会立即销毁,因为外部也可以访问局部变量。
③核心原理:父函数内部return返回子函数

    function father() {
        var num = 10; //闭包函数里的局部变量
        function son() {
          console.log(num); //其他函数访问闭包函数的局部变量
        }
        return son; //将son函数作为返回值
      }
      var s = father(); //用s接收返回值son()
      //这句话相当于:
      // var s = function son() {
      //     console.log(num);
      //   }
      s(); //调用son(),函数外部访问了函数内部的局部变量。
4.闭包的应用

https://zhuanlan.zhihu.com/p/686959137
封装节流函数和防抖函数

    /**
     * 封装节流函数
     * @param {Function} func 需要节流的函数
     * @param {Number} delay 节流时间
     * @returns {Function} 节流函数
     */
    function throttle(func, delay) {
      let timer = null;
      return function () {
        if (!timer) {
          timer = setTimeout(() => {
            func.apply(this, arguments);
            timer = null;
          }, delay)
        }
      }
    }
    // 页面滚动事件节流
    window.addEventListener('scroll', throttle(() => {
      console.log("scrolling!");
    }, 1000, true))

    /**
     * 封装防抖函数
     * @param {Function} func 需要防抖的函数
     * @param {Number} delay 防抖时间
     * @returns {Function} 防抖函数
     */
    function debounce(func, delay) {
      let timer = null;
      return function () {
        if (timer) {
          clearTimeout(timer);
        }
        timer = setTimeout(() => {
          func.apply(this, arguments);
        }, delay)
      }
    }
    // 输入框输入事件防抖
    document.getElementById('input').addEventListener('input', debounce(() => {
      console.log("inputing!");
    }, 1000))

利用闭包实现给多个li标签绑定点击事件,输出当前li的索引号。
分析:
①由于for循环是同步任务,点击事件是异步任务,当点击事件触发时循环已经结束,索引号i已经变成最大值。
②普通方法中,需要在循环中给每个li标签添加一个自定义属性,属性值为索引。
③使用闭包,每次循环都创建一个立即执行函数,索引号i作为实参传入立即执行函数。立即指向函数内部就可以使用索引i了。

     var lis = document.querySelector('ul').children;
      for (var i = 0; i < lis.length; i++) {
        (function (a) {
          lis[a].onclick = function () {
            console.log(a);
          };
        })(i); //索引号i作为实参传入立即执行函数
      }
      }

五、浅拷贝和深拷贝

1.原生js的浅拷贝====》jQuery的$extends( )

浅拷贝只拷贝数据的第一层,更深层次的对象类型数据只拷贝地址。
Object.assign( 目标拷贝对象,被拷贝对象 )

     // 把对象a拷贝给对象b
      var a = {
        uname: '吴磊',
        age: 22,
        hobby: {
          play:" ball",
        },
      };
      var b = {};

      // 1.浅拷贝
      //   Object.assign(b, a);

      // 2.深拷贝
      function deepCopy(newobj, oldobj) {
        for (var k in oldobj) {
          var item = oldobj[k]; //把被拷贝对象的每一个属性值赋给item变量
          if (item instanceof Array) {
            // 判断item是不是数组
            newobj[k] = [];
            deepCopy(newobj[k], item);
          } else if (item instanceof Object) {
            // 判断item是不是对象
            newobj[k] = {};
            deepCopy(newobj[k], item);
          } else {
            // 简单数据类型
            newobj[k] = item;
          }
        }
      }

      deepCopy(b, a);
      console.log(b);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 学习目标: 理解面向对象开发思想 掌握 JavaScript 面向对象开发相关模式 掌握在 JavaScript ...
    金桔柠檬加冰阅读 3,017评论 0 0
  • 一、JavaScript基础知识回顾 1.1 JavaScript 1.1.1 javascript是什么? Ja...
    福尔摩鸡阅读 5,195评论 0 7
  • 学习目标: 理解面向对象开发思想 掌握 JavaScript 面向对象开发相关模式 掌握在 JavaScript ...
    小青年coder阅读 4,516评论 0 2
  • 学习目标: 理解面向对象开发思想 掌握 JavaScript 面向对象开发相关模式 掌握在 JavaScript ...
    璐璐熙可阅读 2,682评论 0 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 12,717评论 28 53