在第一部分中,主要总结了函数作为一个对象的常见属性(name/length
)、如何自定义属性以及如何使用函数构造器(Function
)。
这里总结函数作为对象的常见方法(apply/call/bind/toString
)。
使用 call 绑定 this
call
方法指定 this
并且逐个提供参数。
基本语法
-
thisArg
,调用f
时指定的this
值:- 非严格模式下,
null/undefined
将被替换为全局对象, - 原始值(
String/Number/...
)将被转换为对象;
- 非严格模式下,
-
arg1, arg2, /* …, */ argN
函数的参数,逐个传入。
f.call(thisArg)
f.call(thisArg, arg1)
f.call(thisArg, arg1, arg2)
f.call(thisArg, arg1, arg2, /* …, */ argN)
简单案例
下面一个案例:普通调用时,this
指向全局;call
调用时,this
指向具体对象。
function log() {
console.log(this.xxx);
console.log(this);
}
let o = { xxx: 100, yyy: 200 }
log(); // undefined或其他(globalThis.xxx) - globalThis/Window
log.call(o) // 100 - { xxx: 100, yyy: 200 }
log.call() // undefined或其他(globalThis.xxx) - globalThis/Window
log.call(null) // undefined或其他(globalThis.xxx) - globalThis/Window
上面的例子中,undefined/null
将转换成全局对象,但在严格模式下:
"use strict"
function log() {
console.log(this);
}
log.call() // undefined
log.call(null) // null
借用方法
一个对象的方法,给另外一个对象使用。达到目的方案如下:
- 直接通过对象调用,并且通过
call
指定其他的对象; - 先将方法赋值成为一个函数,在通过
call
的调用指定其他的对象; - 先将方法赋值成为一个函数,并对
call
进行自绑定。
const cat = {
name: 'Kitty',
showName () { console.log(this.name); }
}
const dog = {
name: '阿旺'
}
cat.showName.call(dog) // '阿旺'
let showName = cat.showName
showName.call(dog) // '阿旺'
let boundedShowName = showName.call.bind(showName)
boundedShowName(dog) // '阿旺'
对象原型链上的方法也可以借用。下面一个例子中,借用数组原型链上的切片方法。
let arr = [1, 2, 3, 4];
const slice = Array.prototype.slice;
const boundedSlice = slice.call.bind(slice);
slice.call(arr, 1) // [2, 3, 4]
boundedSlice(arr, 1) // [2, 3, 4]
apply 方法
apply
和 call
方法能够达到几乎等效目的。
基本语法
-
thisArg
,调用f
时指定的this
值:- 非严格模式下,
null/undefined
将被替换为全局对象, - 原始值(
String/Number/...
)将被转换为对象;
- 非严格模式下,
-
argsArray
,类数组对象,调用f
时的参数(或者如果不需要向函数提供参数,则为null/undefined
)。
f.apply(thisArg)
f.apply(thisArg, argsArray)
apply VS call
大多数情况下,使用 call
或 apply
均可,apply
可能会更快,因为大多数 JS 引擎在内部对其进行了优化。下面一个等效表达中:
-
arr
期望是一个类数组对象; -
itor
期望是一个可迭代对象。
f.apply(thisArg, arr)
f.call(thisArg, ...itor)
可以看出,apply/call
两者几乎相同,不同的是参数方式,参数在 call()
中逐个作为列表传递,而在 apply()
中它们会组合在一个类数组对象中。
Math.max.apply(null, [1, 3, 5]) // 5
Math.max.call(null, 1, 3, 2) // 3
简单案例
下面一个例子中:在一个数组后添加若干元素。
let arr = ["a", "b"];
arr.push.apply(arr, [0, 1, 2]);
arr // ["a", "b", 0, 1, 2]
arr.push.call(arr, 3, 4)
arr // ["a", "b", 0, 1, 2, 3, 4]
arr.push(arr, ...[5, 6])
arr // ["a", "b", 0, 1, 2, 3, 4, 5, 6]
呼叫转移
将所有参数、上下文一起传递给另一个函数被称为“呼叫转移”。下面是一种最简单的表达,在外部看来 f()
和 w()
没有声明区别。
function f() { console.log(arguments[0]) }
function w() { f.apply(this, arguments) }
f() // 1
w() // 1
使用 bind 创建一个 Bounded Function - 绑定函数
bind
方法创建一个新函数,当调用该新函数时,它会调用原始函数(目标函数-Target Function):
- 将其
this
上下文绑定为指定值; - 同时,还可以绑定一系列参数(会插到新函数传入参数的前面)。
基本语法
-
thisArg
,调用绑定函数给原始函数f
时指定的this
值:- 非严格模式下,
null/undefined
将被替换为全局对象, - 原始值(
String/Number/...
)将被转换为对象, - 如果使用
new
创建绑定函数,忽略该值;
- 非严格模式下,
-
arg1, arg2, /* …, */ argN
,在调用原始函数f
时,逐个插入到传入绑定函数的参数前的参数。。
f.bind(thisArg)
f.bind(thisArg, arg1)
f.bind(thisArg, arg1, arg2)
f.bind(thisArg, arg1, arg2, /* …, */ argN)
bind 参数的绑定顺序
给函数指定 this
值,并不复杂。下面一个案例具体讲述 bind
参数的绑定顺序问题。
-
boundedFn1
成功地绑定了 this 对象 为一个字符串xy
,并且将两个其余参数绑定,在调用时:- 目标函数
fn
的this
指定为xy
; - 通过
bind
绑定的其他参数插入在目标函数参数列表的前面,相当于fn('a', 'b', '1', '2')
。
- 目标函数
-
boundedFn2
失败地绑定了 this 对象 为一个字符串xxyy
,并且将两个其余参数绑定,在调用时:- 目标函数
fn
的this
指定仍然为xy
(因为boundedFn1
已经是一个绑定函数,this
的指定不会被覆盖); - 通过
bind
绑定的其他参数插入在目标函数参数列表的前面,以及第一次绑定参数的后面,相当于fn('a', 'b', 'c', 'd', '1', '2')
。
- 目标函数
function fn(...args) {
console.log(this, args.join(''));
}
let boundedFn1 = fn.bind('xy', 'a', 'b')
boundedFn1('1', '2') // String {'xy'} 'ab12'
let boundedFn2 = boundedFn1.bind('xxyy', 'c', 'd')
boundedFn2('1', '2') // String {'xy'} 'abcd12'
一般的,对于多层绑定函数,this
指定是第一层;其他参数按照层数,依次插入在目标函数的参数列表前面(fn(...argsFloor1, /* ... */, argsFloorX, ...args)
)。
使用 toString 方法获取函数源码字符串
- 函数作为对象,重写了从 Object 继承来的 toString() 方法。
toString
方法返回一个包含用于定义函数的源文本段的字符串。 - 对于内置函数、由
bind
创建的绑定函数、非 JavaScript 函数,调用toString()
,返回一个看起来像原函数的字符串。
function f() {
console.log(111)
}
f.toString() // 'function g() { \n console.log(111) \n}'
g.bind(null).toString() // 'function () { [native code] }'
Math.max.toString() // 'function max() { [native code] }'
实际源代码与 toString() 结果比较
注意:toString
方法可以修改。文章来源:https://www.toymoban.com/news/detail-826006.html
function f() { }
class A {
a() { }
}
function* g() { }
f.toString() // "function f() { }" 普通函数
A.toString() // "class A { a() { } }" 类
g.toString() // "function* g() { }" 生成器
((a) => a).toString() // "(a) => a" 箭头函数
({ a() {} }.a).toString() // "a() {}" 方法
({ *a() {} }.a).toString() // "*a() {}" 生成器方法
({ [x]() {} }[x]).toString()// "[x]() {}" 计算方法
Function.prototype.toString.toString() // "function toString() { [native code] }"
f.bind(null).toString() // "function () { [native code] }"
(Function("a", "b")).toString() // function anonymous(a\n) {\nb\n}
(Object.getOwnPropertyDescriptor({ get a() {} }, "a").get).toString() // "get a() {}"
(Object.getOwnPropertyDescriptor({ set a(x) {} }, "a").set).toString() // "set a(x) {}"
关于什么的计算方法/属性:计算变量不能点式读取。文章来源地址https://www.toymoban.com/news/detail-826006.html
let [x, y, z] = ['ad', 'cc', 'gg']
let o = { [x]: 11, [y]: function() {}, [z] (){} }
o.ad // 11
o.cc // f () {}
o.gg // ƒ [z](){}
o[x] // 11
o[y] // f () {}
o[z] // ƒ [z](){}
o.x // undefined
o.y // undefined
o.z // undefined
到了这里,关于6.函数是特殊的对象2 - JS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!