当前位置: 首页 > article >正文

想品客老师的第六天:函数

函数基础的部分写在这里

函数声明

在js里万物皆对象,函数也可以用对象的方式定义

    let func = new Function("title", "console.log(title)");
        func('我是参数title');

也可以对函数赋值:

  let cms = function (title) {
            console.log(title);
        };//表达式这里有写分号
        cms("后盾人");

es6新增了简写模式

举个栗子:围绕对用户模块的设置,在对象里面写方法

       let user = {
            name: null,
            setUsername(name) {
                this.name = name;
            },
            getUsername() {
                return this.name;
            }
        };
        user.setUsername("荷叶饭");
        console.log(user.getUsername());//荷叶饭

正常情况对方法的声明应该是【setUsername:function(){this.name-name;}】

es6的简写是【 setUsername(name) {this.name = name;}】可以不用写冒号和function

全局函数定义特点

我们普通的在全局写一个函数,会把函数托到全局上,自动给他一个window对象:

   function screenX(){
            console.log('我是screenX函数,已经有一个和我一样的函数名了')
        }
        console.log(window.screenX)

像这样:

但是实际上window里本来就有一个screenX属性,他们名字一样,新的会覆盖旧的,旧的旧不起作用了

    console.log(window.screenX)//返回浏览器左边界到操作系统桌面左边界的水平距离。

这就是全局变量的缺点

匿名函数

函数的调用可以写在函数的声明前

   show()//荷叶饭
        function show() {
            console.log('荷叶饭')
        }

这叫函数提升 函数提升看这里

匿名函数就是没名字的函数,匿名函数不存在函数提升,因为提升只提升声明,不提升表达式

函数的本质也是个对象

 console.log(hd instanceof Object)//true

立即执行函数与块作用域解决冲突

在引入其他人的库的时候,会出现函数名重复的问题,解决办法有两个:模块化类的思想和立即执行函数(之前也讲过)

在引入外来库的时候,我们可以通过自己给库私有化:

  <script src="./0124Js1.js"></script>
    <script src="./0124Js2.js"></script>
    <script>
        js1.hd()//把两个名字一样的函数封装在两个不要太的对象里
        js2.hd()//这样就不会冲突了
  </script>

看看立即执行函数:

js1:

(function(window) {
  function hd() {
    console.log("4.1.js-hd");
  }
  function show() {
    console.log("4.1.js-show");
  }
  window.js1 = { hd, show };
})(window);//不加window在全局调用不了,所以把它封装在window里

js2:

(function (window) {//把window传入
    function hd() {
        console.log("4.2.js-hd");
    }
    function show() {
        console.log("4.2.js-show");
    }
    window.js2 = { hd, show };
})(window);//把window传入

  作用域也可以看这里

形参和实参

形参就是函数里占位置的,形参的数量一般要和实参的数量对应

默认参数

给形参设置默认值

   function avg(total, year = 1) {
              console.log(year);//year=1
              return Math.round(total / year);
            }
            console.log(avg(2000))//year=1当作默认值传入函数,结果等于2000

 再举一个例子:

    function sortArray(array, type = "asc") {
            return array.sort((a, b) => (type == "asc" ? a - b : b - a));
        }
        console.log(sortArray([3, 1, 4, 2], "asc"));

这时候就按升序排序,如果传递的实参不是asc,按降序:

        function sortArray(array, type = "asc") {
            return array.sort((a, b) => (type == "asc" ? a - b : b - a));
        }
        console.log(sortArray([3, 1, 4, 2], "dasc"));

默认参数一般放在后面,放在后面在传递实参的时候可以不传,但是放在前面必须传递undefined:

 function sortArray(array, type = "asc") {
            return array.sort((a, b) => (type == "asc" ? a - b : b - a));
        }
        console.log(sortArray([3, 1, 4, 2]))//不报错
     function sortArray(type = "asc",array ) {
            return array.sort((a, b) => (type == "asc" ? a - b : b - a));
        }
        console.log(sortArray(undefined,[3, 1, 4, 2]))//传undefined不报错

函数参数

函数的参数也可以是函数,在filter里传函数:

       function fun(a) {
            return a <= 3
        }
        let arr = [1, 2, 3, 4, , 5, 6, 7].filter(fun)
        console.log(arr)//[1,2,3]

还讲到了参数不定数的时候,使用arguments参数,也就是可变参数,动态参数

arguments的一个属性:length,获取到的参数的长度

 function sum() {
            console.log(arguments.length)//6
        }
        console.log(sum(1, 2, 35, 4, 6, 22))

arguments本质上是一个对象:

function sum() {
            console.log(typeof arguments)//object
        }
        console.log(sum(1, 2, 35, 4, 6, 22))

剩余参数

也可以用剩余参数:

剩余参数:函数参数使用,得到真数组,吸收

展开运算符:数组中使用,数组展开,释放

   function sum(...args) {
            return args.reduce((a, b) => a + b);
        }
        console.log(sum(1, 23, 3, 43, 45, 53))//168

箭头函数

此事在这篇博客里亦有记载

递归、构造、事件处理的时候都用不了箭头函数

递归

边界条件和递归方程组成,这是阶乘的递归组成:

     function factorial(num) {
            if (num == 1) return 1
            return num * factorial(num - 1)
        }
        console.log(factorial(5))//5的阶乘=120

更简单的写法可以这么写:

function factorial(num) {
            return num == 1 ? 1 : num * factorial(--num);
        }
        console.log(factorial(5))//5的阶乘=120

写一个递归求和

  function sum(...args) {
            console.log(args)
            if (args.length == 0) {
                return 0
            }
            return args.pop() + sum(...args)
        }
        console.log(sum(1,2,3,4,5))

递归实现倒三角

        function star(sum) {
            return sum? document.write("*".repeat(sum) + "<br/>") || star(--sum): "";
        }
        star(5);

递归渲染

function change(lessons, num = 100, i = 0) {
        if (i == lessons.length) {
            return lessons;
        }
        lessons[i].click += num;
        return change(lessons, num, ++i);//i必须是++i,不能是i++,因为i需要先自增再传递,如果
        //++的话,传递给函数change的i永远是0
    }
    console.table(change(lessons, 90));

渲染:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>后盾人</title>
  </head>
  <body></body>
  <script>
    let lessons = [
      {
        title: "媒体查询响应式布局",
        click: 89
      },
      {
        title: "FLEX 弹性盒模型",
        click: 45
      },
      {
        title: "GRID 栅格系统",
        click: 19
      },
      {
        title: "盒子模型详解",
        click: 29
      }
    ];
    lessons = lessons.map(function(item) {
      item.click += 100;
      return item;
    });
    console.table(lessons);
  </script>
</html>

先写到这吧。。今天舟车劳顿好累好困

回调函数

把一个函数当成另一个函数的参数传过去,在这另一个函数的某个时刻会调用

比如这个:

 document.getElementById('bt').addEventListener('click',function(){
            console.log('我就是个回调函数')
        })

回调函数的使用非常多:

被画出来的就是回调函数的部分

展开语法的收放

此事在品客老师的课中亦有记载

点语法在左边,也就是声明部分,就是【收】;在右边就是【放】

  let [...edu]=[1,2,3,4]
        console.log(edu)//收
        
        let hd=[1,2,3]
        let [a,b,c]=[...hd]
        console.log(a,b,c)//放

收放自如的点语法捏

展开语法用作函数的剩余参数

function sum(...args){
    console.log(args)
    return args.reduce((a,b)=>a+b)
}
console.log(sum(1,2,3,4,5,77,2))//94

函数里的this

对象里的方法(方法也是函数)的this就是指向这个对象的:

    let edu = {
            site: "后盾人",
            show: function() {
            console.log(this);//this指向obj
            }
        };
        console.log(edu.show());

当我们在方法里写一个普通函数的时候:

  let edu = {
            site: "后盾人",
            show: function() {
            console.log(this);//这是个方法,这个方法的this是obj
            function render() {
            console.log(this)//render是个托管到全局的函数,所以this是window
            }
            render();//调用一下
            }
        };
        console.log(edu.show())//上面的方法没写返回值,所以是undefined

这个render函数是window的,所以它访问不了edu里的属性【site】(此事在作用域内亦有记载)

比如我们用构造函数这么写:

 function User(name) {
                this.name = name;
                this.show = function () {
                    return this.name;
                };
            }
            let lisi = new User('李四');
            console.log(lisi.show());

就可以访问到name,但是在方法里再写一个函数,这个函数就访问不到:

  function User(name) {
                this.name = name;
                this.show = function () {
                    function render() {
                        console.log(this);//window
                    }
                    render();
                    return this.name;//李四
                };
            }
            let lisi = new User('李四');
            console.log(lisi.show());

改变this指针

通过常量

可以设置一个常量等于对象this,在方法里的函数里调用这个常量

不用担心这个函数里找不到这个常量,js的机制会循着作用域链往上找的

  let Lesson = {
                site: "后盾人",
                lists: ["js", "css", "mysql"],
                show: function () {
                    const self=this
                    return this.lists.map(function (value) {
                        return `${self.site}-${value}`
                    });
                }
            };
            console.table(Lesson.show());

也有的函数里面有thisArg,这个参数就是改变当前this指向的参数,比如map函数:

 let Lesson = {
                site: "后盾人",
                lists: ["js", "css", "mysql"],
                show: function () {
                    return this.lists.map(function (value) {
                        return `${this.site}-${value}`
                    }, this);
                }
            };
            console.table(Lesson.show());

效果是一样的

通过箭头函数

箭头函数改变原理匿名函数this的指向

let Lesson = {
                site: "后盾人",
                lists: ["js", "css", "mysql"],
                show: function () {
                    return this.lists.map(title=>`${this.site}-${title}`);
                }
            };
            console.table(Lesson.show());

有时候我们不想让匿名函数的this指向全局,也不想指向上一级,如果有事件的话,可以使用event参数,也就是之前学的事件对象,使用event+箭头函数,可以让this指向会被触发的事件的对象

 let Dom={
                site:'荷叶饭',
                bind:function(){
                    const button=document.querySelector('button')
                    button.addEventListener('click',event=>{
                        console.log(this)
                        console.log(this.site+event.target.innerHTML)
                    })
                }
            }
Dom.bind()

    在普通函数中,this 指向触发事件的元素(即 button)因此,this.site 会是 undefined,因为 button 元素没有 site 属性。

    MDN上有addEventListener的参考,aEL第二个参数可以是一个函数或者是一个实现了EventListener接口的对象,也就是说this可以当第二个参数:

    const button = document.querySelector("button");
        button.addEventListener("click", this);
        console.log(button);
    

    总之箭头函数的this指向父级的this,普通函数的this指向window,具体使用看具体情况

    构造函数怎么构造对象的:通过对构造函数里的this的属性名增删改查,new的时候创建对象

    call、bind、apply

    call会立即调用构造函数

    apply也会立即调用构造函数

    在这里再讲讲为什么这里可以传入Math:

    首先Math是什么?没错是个js的内置对象,Math.max()是一个方法,更是一个函数

    apply是什么?是函数的方法

    那Math.max.apply这句是不是没有问题,因为apply是函数的方法,而Math.max()就是个函数啊!

    apply的第一个参数是什么?是thisArg,改变this指向,传入Math就是把指向改为Math,没问题吧!

    所以可以借用数学运算求数组的最大值

    如果想让bind执行就加个括号:

    function show(){
                console.log(this.name)
            }
            show.bind({name:'荷叶饭'})()//有反应,执行了函数,打印this.name=荷叶饭
          function show(){
                console.log(this.name)
            }
            show.bind({name:'荷叶饭'})//没有反应

    还有就是经过bind的两个对象不相等:

      let a = function () { };
                let b = a;
                console.log(a === b);//true
                b = a.bind();
                console.log(a === b);//false

    因为call和apply这两个方法是立即调用函数的,立即调用函数就得把参数传上

    而bind不立即执行,所以可以在执行的时候再传参:

     function fun(a,b){
                console.log(a,b)
                return this.f+a+b            
            }
            let func=fun.bind({f:1})//改变指向
            func(3,4)//这一步真正执行:3,4

    如果在bind的时候传了一个参数,那么在执行的时候传的两个参数,后面那个就多出来了:

      function fun(a,b){
                console.log(a,b)
                return this.f+a+b            
            }
            let func=fun.bind({f:1},3)//改变指向
            func(3,4)//这一步真正执行:3,3

    bind这种不立即执行的方法就适用在事件处理上,因为只有事件触发的时候才执行:

     document.querySelector("button").addEventListener(
                    "click",
                    function (event) {
                        document.write(this.url + event.target.innerHTML);
                    }.bind({ url: "你点击了我" })
                );

    对bind加深印象:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>后盾人</title>
      </head>
      <style>
        * {
          padding: 0;
          margin: 0;
        }
    
        body {
          width: 100vw;
          height: 100vh;
          font-size: 3em;
          padding: 30px;
          transition: 2s;
          display: flex;
          justify-content: center;
          align-items: center;
          background: #34495e;
          color: #34495e;
        }
        h1 {
          padding: 30px;
          font-size: 30px;
        }
      </style>
      <body>
        <h1>houdunren.com</h1>
        houdunren.com
      </body>
      <script>
        function Color(elem) {
          this.elem = elem;
          this.colors = ["#1abc9c", "#f1c40f", "#9b59b6", "#f39c12"];
          this.run = function() {
            setInterval(
              function() {
                let i = Math.floor(Math.random() * this.colors.length);
                this.elem.style.backgroundColor = this.colors[i];
              }.bind(this),
              1000
            );
          };
        }
        let obj = new Color(document.body);
        obj.run();
        let h1 = new Color(document.querySelector("h1"));
        h1.run();
      </script>
    </html>
    

    构造函数方法继承

    例如现在向后台请求url可以多个构造函数复用方法:

        function Request() {
          this.get = function(params) {
            let str = Object.keys(params)
              .map(k => `${k}=${params[k]}`)
              .join("&");
            let url = `https://api.houdunren.com?${this.url}/${str}`;
            document.write(url + "<hr/>");
          };
        }
        //https://houdunren.com/article/lists?id=1&cat=js
        function Article() {
          this.url = "article/lists";
          Request.apply(this);
        }
        let a = new Article();
        console.log(a.get({ id: 1, cat: "js" }));
        function User() {
          this.url = "user/lists";
          Request.call(this);
        }
        let user = new User();
        user.get({ id: 2, role: "admin" });

    举个栗子:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>后盾人</title>
    </head>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
    
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100vw;
            height: 100vh;
        }
    
        dl {
            width: 400px;
            display: flex;
            flex-direction: column;
        }
    
        dt {
            background: #e67e22;
            border-bottom: solid 2px #333;
            height: 50px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
        }
    
        dd {
            height: 200px;
            background: #bdc3c7;
            font-size: 5em;
            text-align: center;
            line-height: 200px;
        }
    </style>
    
    <body>
        <dl>
            <dt>上一页</dt>
            <dd>1</dd>
            <dt>下一页</dt>
            <dd hidden="hidden">2</dd>
        </dl>
    </body>
    <script>
        function panel(i) {
            let dds = document.querySelectorAll("dd");
            dds.forEach(dd => dd.setAttribute("hidden", "hidden"));
            dds[i].removeAttribute("hidden");
        }
        document.querySelectorAll("dt").forEach((dt, i) => {
            dt.addEventListener("click", () => panel.call(null, i));
        });
    </script>
    
    </html>

    孩子们我要学疯了


    http://www.kler.cn/a/520458.html

    相关文章:

  • Cursor的简单使用
  • 练习题 - Django 4.x File 文件上传使用示例和配置方法
  • BLE透传方案,IoT短距无线通信的“中坚力量”
  • Charles 4.6.7 浏览器网络调试指南:HTTPS抓包(三)
  • Neural networks 神经网络
  • jira.issueviews
  • 论文阅读(四):混合贝叶斯和混合回归方法推断基因网络的比较
  • Docker快速部署高效照片管理系统LibrePhotos搭建私有云相册
  • HarmonyOS:创建应用静态快捷方式
  • k8s支持自定义field-selector spec.hostNetwork过滤
  • [MoeCTF 2022]ezhtml
  • 2014年蓝桥杯第五届CC++大学B组真题及代码
  • 【Postman接口测试】接口用例设计实战—以聚合数据的新闻头条接口为例
  • pytorch卷积的入门操作
  • 深入理解Pytest中的Setup和Teardown
  • LLM大模型推理中的常见数字
  • Windows上通过Git Bash激活Anaconda
  • 【算法】图解面试笔试热点二叉树相关算法题汇总
  • 人工智能:从基础到前沿
  • el-autocomplete组件模糊查询及显示空白解决方法
  • 【蓝桥杯】43695.填字母游戏
  • 【Linux】gcc/g++的使用
  • 淘宝商品数据解析的具体步骤是什么?
  • go单元测试和基准测试
  • wow-agent---task4 MetaGPT初体验
  • CNN-BiLSTM卷积双向长短期记忆神经网络时间序列预测(Matlab完整源码和数据)