《javascript高级程序设计》学习笔记 | 10.9-10.11.函数内部

2023-12-17 03:58

本文主要是介绍《javascript高级程序设计》学习笔记 | 10.9-10.11.函数内部,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关注[前端小讴],原创技术文章

相关代码 →

10.9 函数内部

  • ES5 中函数内部有 2 个特殊对象 argumentsthis,1 个内部属性 caller
  • ES6 新增new.target属性

10.9.1 arguments

  • arguments是一个类数组对象,包含调用函数时传入的所有参数
  • 只有以funciton关键字定义函数时才会有arguments对象(箭头函数没有
  • 对象有一个callee属性,指向arguments所在函数的指针(注意:是指针即函数名,而非函数)
    • 严格模式下,访问arguments.callee会报错
// 递归函数:计算阶乘
function factorial(num) {if (num <= 1) return 1else return num * factorial(num - 1)
}
// 使用arguments.callee解耦函数逻辑与函数名
function factorial(num) {if (num <= 1) return 1else return num * arguments.callee(num - 1) // callee指向arguments对象所在函数
}let trueFactorial = factorial // 保存函数的指针// 重写factorial函数,trueFactorial指针不变
factorial = function () {return 0
}
console.log(trueFactorial(5)) // 120,已用arguments.callee解除函数体内代码与函数名的耦合,仍能正常计算
console.log(factorial(5)) // 0,函数已被重写

10.9.2 this

  • 标准函数中,this指向调用函数的上下文对象,即函数执行的环境对象(全局作用域指向window
window.color = 'red' // vscode是node运行环境,无法识别全局对象window,测试时将window改为global
let o = { color: 'blue' }
function sayColor() {console.log(this.color)
}
sayColor() // 'red',this指向全局对象
o.sayColor = sayColor
o.sayColor() // 'blue',this指向对象o
  • 箭头函数中,this指向定义函数的上下文对象,即该函数外部的环境对象
let sayColor2 = () => {console.log(this.color) // this指向定义sayColor2的上下文,即全局对象
}
sayColor2() // 'red',this指向全局对象
o.sayColor2 = sayColor2
o.sayColor2() // 'red',this指向全局对象
  • 事件回调定时回调中调用某个函数时,this指向的并非想要的对象,将回调函数写成箭头函数可解决问题
function King() {this.royaltyName = 'Henry'setTimeout(() => {console.log(this.royaltyName) // 箭头函数,this指向定义函数的上下文,即King()的函数上下文}, 1000)
}
function Queen() {this.royaltyName = 'Elizabeth'setTimeout(function () {console.log(this.royaltyName) // 标准函数,this指向调用函数的上下文,即setTimeout()的函数上下文}, 1000)
}
new King() // 'Henry',1秒后打印
new Queen() // undefined,1秒后打印

10.9.3 caller

  • ES5 定义了 caller 属性,指向调用当前函数的函数(全局作用域中为 null)
function callerTest() {console.log(callerTest.caller)
}
callerTest() // null,在全局作用域种调用function outer() {inner()
}
function inner() {console.log(inner.caller)
}
outer() // [Function: outer],在outer()调用// 解除耦合
function inner() {console.log(arguments.callee.caller) // arguments.callee指向arguments所在函数的指针,即inner
}
outer() // [Function: outer],在outer()调用
  • arguments.caller的值始终是undefined,这是为了区分arguments.caller和函数的caller
  • 严格模式下,访问arguments.caller为函数的caller属性赋值会报错
function inner2() {console.log(arguments.caller) // undefinedconsole.log(arguments.callee) // [Function: inner2]
}
inner2()

10.9.4 new.target

  • ES6 在函数内部新增new.target属性,检测函数是否使用new关键字调用
    • 未使用new调用,new.target的值是undefined
    • 使用new调用,new.target的值是被调用的构造函数
function King2() {if (!new.target) {console.log(new.target, 'King2 must be instantiated using "new"')} else {console.log(new.target, 'King2 instantiated using "new"')}
}
new King2() // [Function: King2] 'King2 instantiated using "new"'
King2() // undefined 'King2 must be instantiated using "new"'

10.10 函数属性与方法

  • 函数包含 2 个属性:lengthprototype

    • length 保存函数希望接收的命名参数的个数
    function nameLength(name) {return name
    }
    function sumLength(sum1, sum2) {return sum1 + sum2
    }
    function helloLength() {return 'Hello'
    }
    console.log(nameLength.length, sumLength.length, helloLength.length) // 1 2 0
    
    • prototype 指向函数的原型对象,保存函数所有实例方法不可枚举(使用for-in无法发现)
    console.log(Array.prototype) // 在浏览器中查看Array的原型对象,包含sort()等方法
    console.log(Object.keys(Array)) // [],Array构造函数自身所有可枚举的属性
    console.log(Object.getOwnPropertyNames(Array)) // [ 'length', 'name', 'prototype', 'isArray', 'from', 'of' ],Array构造函数自身的所有属性
    
  • 函数有 3 个方法:apply()call()bind()

    function sumPrototype(num1, num2) {return num1 + num2
    }
    
    • apply()call()都会以指定的this值调用函数,即设置调用函数时函数体内this指向
    • apply()接收 2 个参数:① 运行函数的作用域(指定 this);② 参数数组(实例或 arguments 对象均可)
    function applySum1(num1, num2) {return sum.apply(this, arguments) // 传入arguments对象
    }
    function applySum2(num1, num2) {return sum.apply(this, [num1, num2]) // 传入数组实例
    }
    console.log(applySum1(10, 10)) // 20
    console.log(applySum2(10, 10)) // 20
    
    • call()接收若干参数:① 运行函数的作用域(指定 this);剩余参数逐个传入
    function callSum(num1, num2) {return sum.call(this, num1, num2) // 逐个传入每个参数
    }
    console.log(callSum(10, 10)) // 20
    
    • apply()call() 真正强大的地方在于能够扩充函数运行的作用域,即控制函数体内this
    window.color = 'red' // vscode是node运行环境,无法识别全局对象window,测试时将window改为global
    let o2 = { color: 'blue' }
    function sayColor3() {console.log(this.color)
    }
    sayColor3() // 'red',this指向全局对象
    sayColor3.call(this) // 'red',this指向全局对象
    sayColor3.call(window) // 'red',this指向全局对象,测试时将window改为global
    sayColor3.call(o2) // 'blue',this指向对象o2
    
    • Function.prototype.apply.call(),将函数原型的apply方法利用call()进行绑定(可通过Reflect.apply()简化代码)
    let f1 = function () {console.log(arguments[0] + this.mark)
    }
    let o3 = {mark: 95,
    }
    f1([15]) // '15undefined',this指向f1的函数上下文,this.mark为undefined
    f1.apply(o3, [15]) // 110,将f1的this绑定到o3
    Function.prototype.apply.call(f1, o3, [15]) // 110,函数f1的原型对象的apply方法,利用call进行绑定
    Reflect.apply(f1, o3, [15]) // 110,通过指定的参数列表发起对目标函数的调用,三个参数(目标函数、绑定的this对象、实参列表)
    
    • bind()创建一个新的函数实例,其this绑定传给bind()的对象
    let o4 = { color: 'blue' }
    function sayColor4() {console.log(this.color)
    }
    let bindSayColor = sayColor4.bind(o4) // 创建实例bindSayColor,其this被绑定给o4
    sayColor4() // 'red',this指向全局对象
    bindSayColor() // 'blue',this被绑定给对象o4
    

10.11 函数表达式

  • 函数声明的关键特点是函数声明提升,即函数声明会在代码执行之前获得定义
sayHi() // 'Hi',先调用后声明
function sayHi() {console.log('Hi')
}
  • 函数表达式必须先赋值再使用,其创建一个**匿名函数(function后没有标识符)**再把它赋值给一个变量
    • 匿名函数的name属性是空字符串
sayHi2() // ReferenceError: Cannot access 'sayHi2' before initialization,不能先调用后赋值
let sayHi2 = function sayHi() {console.log('Hi')
}
  • 函数声明与函数表达式的区别在于提升,在条件块避免使用函数声明可以使用函数表达式
let condition = false
if (condition) {function sayHi3() {console.log('true')}
} else {function sayHi3() {console.log('false')}
}
sayHi3() // 不同浏览器的结果不同,避免在条件块中使用函数声明let sayHi4
if (condition) {sayHi4 = function () {console.log('true')}
} else {sayHi4 = function () {console.log('false')}
}
sayHi4() // false,可以在条件块中使用函数表达式
  • 创建函数并赋值给变量可用于在一个函数中把另一个函数当作值返回
/*** 按照对象数组的某个object key,进行数组排序* @param {String} key 要排序的key* @param {String} sort 正序/倒序:asc/desc,默认为asc*/
function arraySort(key, sort) {return function (a, b) {if (sort === 'asc' || sort === undefined || sort === '') {// 正序:a[key] > b[key]if (a[key] > b[key]) return 1else if (a[key] < b[key]) return -1else return 0} else if (sort === 'desc') {// 倒序:a[key] < b[key]if (a[key] < b[key]) return 1else if (a[key] > b[key]) return -1else return 0}}
}
var userList = [{ name: 'Tony', id: 3 },{ name: 'Tom', id: 2 },{ name: 'Jack', id: 5 },
]
console.log(userList.sort(arraySort('id'))) // [{ name: 'Tom', id: 2 },{ name: 'Tony', id: 3 },{ name: 'Jack', id: 5 }],按 id 正序排列
console.log(userList.sort(arraySort('id', 'desc'))) // [{ name: 'Jack', id: 5 },{ name: 'Tony', id: 3 },{ name: 'Tom', id: 2 }],按 id 倒序排列
console.log(userList.sort(arraySort('name'))) // [{ name: 'Jack', id: 5 },{ name: 'Tom', id: 2 },{ name: 'Tony', id: 3 }],按 name 正序排列

总结 & 问点

  • arguments 是什么?arguments.callee 指向哪里?写一段代码,表示函数名与函数逻辑解耦的阶乘函数
  • this 在标准函数和箭头函数的指向有什么不同?在事件回调或定时回调中,为什么更适合使用箭头函数?
  • 函数的 caller 属性指向哪里?arguments.caller 的值是什么?严格模式下 caller 有哪些限制?
  • new.target 的作用和值分别是什么?
  • 函数有哪些属性?其指向和用法分别是什么?
  • 请用代码证明 apply()、call()、bind()是如何扩充函数作用域的,并解释 Function.prototype.apply.call()的含义
  • 函数声明和函数表达式最大的区别是什么?如何理解声明提升?
  • 写一段代码,根据对象数组的某个对象属性进行排序,可根据参数决定排序属性及升/降序

这篇关于《javascript高级程序设计》学习笔记 | 10.9-10.11.函数内部的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/502973

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06