JSCORE----函数

1.作用域和作用域链

作用域

全局作用域:脚本中通用的作用域

局部作用域:函数调用时诞生的作用域

全局对象-window

浏览器自带的一个全局顶级对象,其中存放了浏览器提供的各种API

// window
window.alert('提供了浏览器中的弹窗功能')
// 友好写法:凡是window对象中的属性,使用时可以省略 window. 前缀
alert('弹窗')

全局污染

在全局作用域中声明的变量/函数都会存储到window里

window对象本意是存储系统提供的API,把自定义属性存放在这里,就属于污染

导致:

  1. 同名属性会覆盖, 导致系统的API被替换
  2. 外部自定义脚本的变量冲突

解决方案: 

  • 在局部作用域--匿名函数自调用形成的作用域声明变量,在用匿名函数自调用时,前面要用;将匿名函数域前面的代码分开
(function () {
// 把自定义的代码 放在这里写
    function alert() {
        var a = 1
    }
    alert()
})()

  • ES6中退出let/const这两种新的声明变量的方案,带来新的顶级对象-脚本对象,用于存储自定义变量

作用域链

作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,这种分层结构就形成了作用域链

  • 在使用变量时,会先查找当前作用域,如果当然作用域找不到,就往父作用域查找
  • 子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用
  • 遵循 `就近原则` : 使用最近的上级作用域的
var a = 1;
var b = 2;
function fun() {
    var a = 3;
    // 1.先找当前函数作用域内是否有a变量
    // 2.在当前函数作用域内部找到变量a,使用变量a
    console.log(a);
    // 1.先找当前函数作用域内是否有b变量
    // 2.在当前函数作用域内部找到变量b,没有找到
    // 3.继续像上一层作用域寻找,到变量b,使用变量b
    console.log(b);
}
fun();
// 1.先找当前函数作用域内是否有a变量,找到变量a,使用变量a
console.log(a);

执行过程:

1. 创建全局变量a,保存在全局作用域
2. 创建全局变量b,保存在全局作用域
3. 创建全局变量fun(保存函数对象),保存在全局作用域
4. 调用fun函数
4.1 创建fun函数作用域
4.2 创建局部变量a,保存在fun函数作用域
4.2.1 输出变量a
4.2.2 先找当前函数作用域内是否有a变量
4.2.3 在当前函数作用域内部找到变量a,使用变量a
4.3 输出变量b
4.3.1 先找当前函数作用域内是否有b变量
4.3.2 在当前函数作用域内部找到变量b,没有找到
4.3.4 继续像上一层作用域寻找,到变量b,使用变量b
5.输出变量a,此时的a使用全局作用域中的a

函数在调用结束后,会释放(销毁/干掉)函数作用域对象,作用域对象中的局部变量也跟着释放,所以函数内的局部变量在函数外无法访问

2.闭包

函数的被动技能:

  • 声明时 先查看自身用到哪些来自外部作用域的变量, 就会把这些外部作用域保存在自身的scopes 属性里

闭包:

  • 这些被保存在 函数的scopes 属性里的 函数作用域对象 称为闭包

为什么要这么设计?

  • 确保函数在任何位置运行时, 都能正常使用这些来自外部的变量

缺点:

  • 为了自身的运行, 需要保存这些外部的变量, 就会造成额外的内存消耗: 浪费内存
function fn() {
    var a = 0;
    function fun() {  // 内部函数,闭包
        console.log(a) // 使用了外层函数中的变量
    }
    fun()
}
fn()

闭包使用场景:

私有属性:专为指定函数服务的变量

所以不能放在全局作用域中

 解决:放在局部作用域中声明, 借助闭包的特性存储在函数的scopes里

 命名规范: 习惯上给 私有属性 用_开头

3.arguments

函数自带的变量,一个数组,保存函数传入的所有参数

属于类数组类型,属性结构和数组相同,但是原型不是数组

遍历方案:使用for...of 或 利用Object.setPrototypeOf修改其原型为数组

function fun(){
    console.log(arguments)
}
fun(1,2,3)
// 运行结果: [1, 2, 3]

函数重载

参数数量不固定的函数,根据参数的个数或类型不同,执行不同的逻辑操作

function show() {
    if (arguments.length == 3) {
        return arguments[0] * arguments[1] * arguments[2]
    }
    if (arguments.length == 2) {
        return arguments[0] + arguments[1]
    }
    if (arguments.length == 1) {
    // 再次细分
        if (typeof arguments[0] == 'string') {
            return arguments[0].toUpperCase()
        }
        if (typeof arguments[0] == 'number') {
            return arguments[0] * arguments[0]
        }
        if (typeof arguments[0] == 'boolean') {
            return arguments[0] ? '真' : '假'
        }  
    }
}
console.log(show(10, 20, 30)) //计算3个数字的乘积
console.log(show(10, 20)) //计算2个数字的和
console.log(show(10)) //计算数字的平方
console.log(show('kaikai')) //返回字符串的大写格式
console.log(show(true)) // 返回: '真'
console.log(show(false)) // 返回: '假'

4.函数的this

对象.函数() : this指向对象

函数() : this 指向window

new 函数(): 实例对象

箭头函数:没有this 按照作用域链原则到上层查找使用

5.call,apply,bind

强行修改函数中 this 指向的方案:

apply: 升级版 - 函数临时放到对象执行, 额外: 把数组 转换成 参数列表形式

bind: 把函数+对象+实参 绑定打包在一起, 后续调用时 再临时组合

call: 最基础 - 把函数临时放到某个对象中执行

var r1 = { a: 10, b: 20 }
function area(c, d, e) {
    return this.a + this.b + c + d + e
}
console.log(
    // 参数1: 指定this代表的对象
    // 参数2开始... : 实参
    area.call(r1, 100, 200, 300)
    // 参数1: 指定this代表的对象
    // 参数2: 实参列表
    area.apply(r1,[100,200,300])
    // bind: 绑定
    // 把 函数 和 运行时所需的对象 + 实参 绑定在一起 打包成1个整体
    let total = area.bind(r1, 100, 200, 300)
    total();
)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值