共计 6271 个字符,预计需要花费 16 分钟才能阅读完成。
关注前端小讴,浏览更多原创技术文章
相干代码 →
10.9 函数外部
- ES5 中函数外部有 2 个非凡对象
arguments
和this
,1 个外部属性caller
- ES6 新增
new.target
属性
10.9.1 arguments
arguments
是一个 类数组 对象,蕴含调用函数时传入的所有 参数- 只有以
funciton
关键字定义函数时才会有arguments
对象(箭头函数没有) -
对象有一个
callee
属性,指向arguments
所在函数的指针(留神:是指针即函数名,而非函数)- 严格模式下,拜访
arguments.callee
会报错
- 严格模式下,拜访
// 递归函数:计算阶乘
function factorial(num) {if (num <= 1) return 1
else return num * factorial(num - 1)
}
// 应用 arguments.callee 解耦函数逻辑与函数名
function factorial(num) {if (num <= 1) return 1
else 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) // undefined
console.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 个属性:
length
和prototype
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 1
else if (a[key] < b[key]) return -1
else return 0
} else if (sort === 'desc') {// 倒序:a[key] < b[key]
if (a[key] < b[key]) return 1
else if (a[key] > b[key]) return -1
else 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
2021-08-04