JavaScript复习——03 函数

这篇具有很好参考价值的文章主要介绍了JavaScript复习——03 函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

函数在JS中也是一个对象,它具有其它对象的所有功能,函数中可以存储代码,且可以在需要的时候调用这些代码

函数的操作

函数的定义

  1. 函数声明
function 函数名([参数列表]) {
	// 函数体
	return 返回值;
}
  1. 函数表达式
const 函数名 = function([参数列表]) {
	return 返回值;
}
  1. 箭头函数
const 函数名称 = ([参数列表]) => {
	return 返回值;
}

const 函数名称 = ([参数列表]) => console.log("箭头函数");

函数的调用

函数名称(参数1,参数2,.....);

函数的类型

function fn(){
	console.log("我是牛逼")
}

// 返回function
console.log(typeof fn)

函数的参数

参数:

  1. 如果实参和形参相同,那么对应的实参会赋值给形参
  2. 如果实参多于形参,则多余的实参不会使用
  3. 如果形参多于实参,则多余的形参为undefined

注意:JS不检查的参数的类型,任何类型都可以作为参数传递


箭头函数的参数

  1. 我们用箭头函数作为参数的时候,只有一个参数的时候,可以省略()
  2. 定义参数时,我们可以指定默认值
  3. 箭头函数没有arguments
  4. 箭头函数的 this 不能修改
const fn = (a,b) => {
    console.log('a=',a);
    console.log('b=',b);
}

// 当我们箭头函数,只有一个参数时,可以省略()
const fn2 = a => {
    console.log('a =',a);
}

// 定义参数的时候,我们可以指定默认值
const fn3 = (a = 10,b = 20,c = 0) => {
	console.log('a = ',a);
   	console.log('b = ',b);
    console.log('c = ',c);
}

对象作为参数

注意

  1. 我们传递参数的时候,我们传递的是变量中的值,而不是变量本身
  2. 函数每次调用,都会重新创建一个新的对象
function fn(a) {
    a.name = '🐖'
	// 打印:a={name:'🐖'}
    console.log('a =',a);
}

let obj = {name:'孙悟空'};
fn(obj);
// 打印:{name:'🐖'}
console.log(obj)

两次都是打印唐僧、孙悟空

如果第二次打印 孙悟空、孙悟空,就说明我们函数的调用只会创建一个对象,但是实际不是

function fn2(a = {name:'唐僧'}) {
    console.log(a);
    a.name = '孙悟空'
    console.log(a)
}

// print 唐僧 孙悟空
fn2()
// print 唐僧 孙悟空
fn2()

函数作为参数

在JS中,函数也是一个对象,函数作为对象传递,类似于Spring中的AOP思想,主要是增强代码的扩展性

function fn(a) {
    console.log('a =',a)
}

function fn2() {
    console.log('我是一个函数')
}

fn(fn2)
fn(()=>{
    console.log('我是箭头函数')
})

函数的返回值

  1. 任何值都可以是函数的返回值,包括对象、函数之类的
  2. 如果return之后不跟任何值,返回值就是undefined

箭头函数的返回值

注意

  1. 如果我们直接在箭头之后设置对象字面量设置返回值,对象字面量必须使用()包起来
const obj = () => ({name:'孙悟空'})

console.log(obj())
const sum = (a,b) => a + b


let result = sum(123,456)
console.log(result)

作用域

作用域指定是我们变量的可见区域,也就是我们变量在哪里可以被访问

作用域有两种

  1. 全局作用域
    1. 生命周期:在网页开启时创建,在网页关闭时销毁
    2. 所有直接编写在 script 标签中代码都位于全局作用域中
    3. 全局作用域的变量,是全局变量,可以在任意位置访问
  2. 局部作用域
    1. 块作用域
      1. 块作用域是一种局部作用域
      2. 块作用域在代码执行的时候创建,在代码执行结束的时候销毁
      3. 在块作用域中声明的变量是局部变量,只能在块内部访问
    2. 函数作用域
      1. 函数作用域也是一种局部作用域
      2. 函数作用域在我们函数调用时产生,调用结束后销毁
      3. 函数每次调用都会产生一个新的作用域
      4. 在函数内部定义的变量是局部变量,只能在函数内部访问
      5. var 虽然没有块作用域,但是有函数作用域

windows对象

在我们的浏览器中,浏览器为我们提供了一个window对象,可以直接访问

  • window对象代表我们的浏览器的窗口,通过对象可以对浏览器的窗口进行各种操作
    • 除此之外window对象还负责存储JS中内置对象和浏览器的宿主对象(浏览器作为编译器提供的对象)
  • window对象的属性可以通过window对象访问,也可以直接访问
  • 向window对象中添加的变量,会自动变成全局变量
alert('哈哈')
window.console.log('哈哈')
  • 在全局中使用var声明的变量,会自动变成window对象的属性保存
  • 使用function声明的函数,都会作为window对象的方法保存
  • 使用 let 声明的变量不会存在window对象中,而是其它地方
    • 在我们访问变量的时候,会先访问 let 声明的变量,没有再去找window的属性
    • let会存在我们的Script这个对象上,它与Globe(Window的“代理”对象)同级

注意

  1. 我们在局部作用域中,既没有使用 var 来声明变量,也没有使用 let 声明变量,它会自动变成window对象的属性

提升

变量的提升:使用 var 声明的变量,它会在我们所有代码执行前被声明(没有啥子意义)

函数的提升:使用函数声明来创建函数的时候,会在其它的代码执行前被创建,所以我们可以在函数声明前调用函数(也就是函数的创建会被提升到最前面,但是你使用变量形式来声明函数,就不能在函数声明之前使用函数)

let 声明的变量也会被提升,但是在赋值之前,浏览器禁止我们访问

// 这里会输出 undefined 因为只是被声明,而赋值则是在被执行的时候才会赋值
console.log(a)

var a;
a = 10;
// 这里函数会提升
fn()
function fn() {
    alert("我是function")
}

// 这里就不行
// 因为这里使用 var 定义只会提升fn2变量的声明,但是他没有赋值
fn2()
var fn2 = function() {
    console.log("我是function2")
}

// let 声明的变量也会被提升,但是在赋值之前,浏览器禁止我们访问 
fn3()
let fn3 = function() {
    console.log("我是function3")
}

立即执行函数

我们应该减少在全局作用域中编写代码,我们代码要尽量编写到局部作用域中,这样代码就不会相互干扰


匿名立即执行函数创建(IIFE)

  1. 立即执行函数只会调用一次,它时一个匿名函数

  2. 我们可以利用这个IIFE来解决变量冲突问题

  3. 由于JS解析器解析的时候,我们如果有多个立即执行函数,会解析成 (XXX)(XXX)

    1. 这样会报错,因为解析器解析后面加 () 会默认为函数,但是,实际不是函数
    2. 解决这个问题我们需要在两个立即执行函数末尾加上分号;
(function() {
     var a = 10;
    console.log(a)
})();
(function() {
    var a = 11;
    console.log(a)
})();

this

函数在执行时,JS解析器每次都会传递一个隐含的参数,这个参数就是this,this会指向一个对象

this指向的对象会根据函数调用的方式不同而不同

  1. 函数形式调用时,this指向的是window
function fn() {
	// 这里this ==> window
	console.log(this)
}
  1. 当我们以方法的形式调用的时候,this指向的是调用方法的对象本身
function fn() {
	// 这里this ==> window
	console.log(this)
}

const obj = { name:'jack' }
obj.test = fn;
// 这里this ==> obj
console.log(obj.fn())
  1. 箭头函数,this是由外层来决定,外层的 this 是什么就是什么
    1. 它的this与调用方式无关
function fn() {
    // window
    console.log('fn --->',this)
}

const fn2 = () => {
    // window
    consloe.log('fn2 --->',this)
}

const obj = {
    name:'jack',
    // 这里属性值和属性名一样可省略属性名
    fn:fn,
    fn2:fn2,
    sayHello() {
		console.log('syHello -->',this)
        
      
        function t() {
            console.log("t-->",this)
        }
        /*
        	这里是以函数的形式调用,所以它的 this 是 window
        */
        t()
        
        const t2 = () => {
            console.log("t2-->",this)
        }
         /*
        	这里是以箭头函数来调用,它的this由外层来决定,外层的this是obj
        	所以this就是obj
        */
        t2()
    }
}

// obj
obj.fn()
// window
obj.fn2()

高阶函数

根据OCP原则,我们对修改关闭,对扩展开放,有些地方我们需要扩展,我们可以往我们一直使用的函数的参数中传递一个函数,通常回调函数都是匿名函数,而我们将一个函数的参数或返回值时函数,则称为这个函数为高阶函数

将函数作为参数,意味着,我们可以往函数里面动态的传递代码

function filter(arr,fn) {
    for(let i = 0; i < arr.length; i++) {
        if(fn()) {
            return arr[i];
        }
    }
}

// 使用高阶函数
const arr = [1,2,5,3,4,3,5];
const arr1 = [{name:'孙悟空',name:'白菜'}]

// 这样就可以动态的改变条件
filter(arr,(a)=>{ return a>5 })
filter(arr,a => a.name === '孙悟空')
/*
	希望在使用someFn()函数的时候,可以记录一条日志
	在不修改原函数的基础上,为其增加记录日志的功能
	
	可以通过告诫函数,来动态的生成一个函数
*/
function someFn() {
    return 'hello';
}

function outer(cb) {
    return () => {
        // 这里不仅仅可以写这个东西
        console.log('someFn执行')
        return cb()
    }
}

let result = outer(someFn)
console.log(result())

闭包

我们现在希望创建一个函数,第一次调用时,打印1,第二次调用,打印2

我们要完成上面这种操作,就需要定义全局变量,但是我们定义全局变量,当我们协同工作时,变量很可能被别人修改,风险是比较大的

而为了解决上面这种变量不安全的问题,我们就需要将变量放在一个局部作用域中,而将变量放在函数作用域这种局部作用域就称之为闭包

闭包:

​ 闭包就是能访问外部函数作用域中变量的函数

什么时候使用:

​ 当我们需要隐藏一些不希望被别人访问的内容,就可以使用闭包

// 我们可以将不希望别人访问的变量放在这样的函数中
function outer() {
    let num = 0
    
    return () => {
        num++
        console.log(num)
    }
}

const newFn = outer()
newFn()

闭包的要求:

  1. 函数的嵌套
  2. 内部函数要引用外部函数中的变量
  3. 内部函数作为返回值返回

闭包的原理

我们函数的外层作用域,在函数创建的时候就已经确定了(语法作用域),与调用的位置无关

闭包利用的就是 词法作用域


闭包的注意事项

注意

  1. 闭包主要用来隐藏一些希望不被外部访问的变量,这就意味着闭包要占用一些内存空间

  2. 相较于类来说,闭包比较浪费空间(类可以使用原型,而闭包不可以使用原型)

  3. 需要执行比较少的时候用闭包,需要执行次数比较多的时候使用类

  4. 闭包的生命周期:闭包在外部函数调用时产生,外部函数每次调用就会产生一个新的闭包

  5. 闭包在内部函数丢失时销毁(内部函数被CG回收了,闭包就会消失)

function outer() {
    let someValue = 'someValue'
     return function() {
        console.log(someValue)
    }
}
 	function outer() {
        let someValue = 1
        return function () {
          console.log(someValue++)
        }
      }
      let result = outer()
      // 这里每次调用都要用一块新空间来给内部的匿名函数使用
      result()
      result()

可变参数


arguments

除了this以外,我们函数中还隐藏了一个参数arguments,arguments时一个类数组对象

  • arguments用来存储我们的实参,无论我们是否定义了形参
  • arguments可以通过索引来获取元素,也可以通过for循环遍历,但是它不是数组对象,不能用数组的方法

通过arguments 我们可以不受参数的数量限制,但是它也缺点

  1. 它不知道参数的数量
  2. 它不能调用数组的方法
function fn() {
    let result = 0
    for(const arg of arguments) {
        result += arg
    }
    return result
}

可变参数

特点;

  1. 可变参数可以接收任意数量的参数,并且把它存到一个数组中
  2. 可变参数的名字我们可以自己指定
  3. 可变参数可以使用数组的方法
  4. 可变参数可以配合其它的参数一起使用
  5. 可变参数一定要定义到形参列表的最后
function fn(...args) {
    return args.reduce((a,b) => a+b,0)
}

call 和 apply

根据我们函数调用的不同,我们的this也不同

  • call()和apply()的第一个参数可以指定函数的this
  • bind()可以为我们的新函数绑定this,绑定之后无法修改

函数的调用,除了使用函数名()调用,还可以使用call和apply方法
call和apply除了可以调用函数,还可以指定函数中的this

注意

  1. call()和apply()的第一个参数可以指定函数的this
  2. 通过call()第一参数之后参数会作为函数的参数传递过去
  3. 通过apply()要向函数传递参数,需要传递数组
let obj = {name:'孙悟空'}
函数.call(obj,函数的参数1,函数的参数2……) // this ---> obj
函数.apply(函数的this,[函数的参数1,函数的参数2……]) // this ---> window

bind()

bind()是函数的方法,用来创建一个新的函数

作用文章来源地址https://www.toymoban.com/news/detail-741801.html

  1. bind()可以为我们的新函数绑定this
  2. bind()可以我们的新函数绑定参数(也就是将我们的新函数的参数固定不变)
function fn() {
    console.log('fn执行了')
}

let obj = {name:'孙悟空'}
// 函数的前三个参数固定为 10 20 5
const newFn = fn.bind(obj,10,20,5) // this ---> obj
newFn()

到了这里,关于JavaScript复习——03 函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端JavaScript入门-day03

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 1、循环-for 1. for 循环-基本使用 1. for循环语法 2. 退出循环  2. for 循环嵌套  2、数组  1 数组是什么 2 数组的基本使用  1. 声明语法 2. 取值语法  3. 一些术语:  4. 遍历

    2024年02月11日
    浏览(52)
  • JavaScript复习——02

    写法: 使用代码块,可以对代码进行分组,同一个代码块中的代码就是同一组代码,一个代码块中的代码,要么都执行,要么都不执行 在JS中,let声明的变量具有块级作用域 var声明的变量,不具有块级作用域 var声明的变量会将变量的声明提至最前 原始值只能表示一些简单的

    2024年02月08日
    浏览(41)
  • JavaScript复习——01

    这是我用于复习我一年前学习的JavaScript的笔记,由于一年过去了,我大概已经4~5个月没有写过什么代码,所以需要整理自己的知识体系,如果文章出错,也希望大家评论给我改错 JS中的类型有以下几种: Number(数字类型) BigInt (大数类型) String(字符串类型) Boolean (布

    2024年02月08日
    浏览(41)
  • JavaScript复习小案例

    JavaScript实现简易留言板 效果图 完整代码  HTML结构 CSS样式 JS 登录-填写密码 效果图 tab栏切换 效果图 发送短信案例 效果图

    2024年01月18日
    浏览(25)
  • JavaScript 复习

    在1995年时,由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。Netscape在最初将其脚本语言命名为LiveScript,因为Netscape与Sun合作,网景公司管理层希望蹭Java的热度,因此取名为JavaScript。 JavaScript总共分成三部分: ECMAScript(基本语法)、BOM(浏览器对象模型)、DO

    2024年02月10日
    浏览(35)
  • JavaScript复习——04 事件

    事件对象是由浏览器在外面触发事件的时候创建的,这个对象封装了各种事件相关的各种信息 例如: 鼠标的位置 键盘的按键 浏览器创建事件对象后,会将事件对象作为响应参数传递 在DOM类型中有多种不同类型的事件对象,但是他们都一个祖先Event event.clientX:获取鼠标的

    2024年02月05日
    浏览(37)
  • 重生前端之我在javascript敲代码(03-数组)

    一. 数组(重点) 思考:如何保存一个班级的所有学生的姓名? 回答:一种方法利用前面学习过的知识,则每一条信息都需要一个变量去保存,缺点是这样做很麻烦,而且容易出错,又不合理;另一种方法就是利用数组。 概念:数组是存储一系列值的变量集合,可以存储多

    2024年04月11日
    浏览(49)
  • 【03】基础知识:typescript中的函数

    函数声明法 函数表达式/匿名函数 1、typescript 中定义函数传参 函数声明 函数表达式/匿名函数 2、可选参数 在 es5 中,方法的形参和实参个数可以不一样;但是在 ts 中必须一样,如果不一样就需要配置可选参数。 ts 中 通过【 形参?: 数据类型 】形式定义可选参数,代表该参数

    2024年02月13日
    浏览(39)
  • 数据分析03——矩阵常用计算方法和函数

    数组:计算机领域的概念 矩阵:数学领域的概念 对于Numpy而言,矩阵是数组的分支 字符串创建矩阵:mat1 = np.matrix(‘1 2;3 4’) 列表形式创建矩阵:mat2 = np.matrix([[5, 6], [7, 8]]) 通过数组创建矩阵: arr = np.array([[5, 6], [7, 8]]) mat3 = np.matrix(arr) 创建3*3的0矩阵: np.matrix(np.zeros((3, 3))

    2024年02月07日
    浏览(50)
  • c语言复习:常见函数与错误

    log(a) / log10(a) exp(a) sqrt(a) round(a) // (int)(a+0.5) ceil / floor fabs() strcat strcpy / strncpy (str1,str2,n) strcmp ==返回为 0 ;/ strncmp strlen 不包括 ’\\0’ strlwr / strupr strstr fopen(“FILE Path”,“w”) / w, r, a, rb, wb, ab, (+) fclose fwrite(ptr,size,count,stream) fread(ptr,size,count,stream) 执行条件为真后不执行break会继

    2024年01月21日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包