Function.prototype.call()
方法介绍
语法
function.call(thisArg, arg1, arg2, ...)
参数
thisArg
可选的。在 function
函数运行时使用的 this
值。请注意,this
可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动替换为指向全局对象,原始值会被包装。
arg1, arg2, ...
指定的参数列表。
返回值
使用调用者提供的 this
值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined
。
描述
call()
允许为不同的对象分配和调用属于一个对象的函数/方法。
call()
提供新的 this 值给当前调用的函数/方法。你可以使用 call
来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
手写思路总结
- 接收的第一个参数如果为null或undefined,自动指定this为window
- 调用call,在改变函数内部this指向后,立即执行函数,并将函数执行结果返回
- 执行的函数为调用当前call方法的函数,可以理解为call方法内部默认的this
- 在执行完毕后,将赋值的上下文对象清除
手写call方法
//方法挂载在Functin的原型上,这样所有的函数都可以调用
Function.prototype.myCall = function (context, ...arr) {
if (context === null || context === undefined) {
// 如果传入的this对象为 null 和 undefined ,则将函数内部的this自动指向全局对象(浏览器中为window)
context = window
} else {
//防止指定的为原始值,将其转化为原始值对应的实例对象
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
const specialPrototype = Symbol('only') // 定义一个唯一的属性用于存放接下来被执行的函数
//当前的this就是调用的函数
//相当于context.specialPrototype = 需要被执行的函数。那么需要被执行的函数就是由context调用的,也就是说,需要执行的函数的内部的this变为了调用他的对象context
context[specialPrototype] = this // 函数的this指向隐式绑定到context上。
let result = context[specialPrototype](...arr) // 通过隐式绑定执行函数并传递参数
delete context[specialPrototype] // 删除上下文对象的属性
return result // 返回函数执行结果
}
Function.prototype.apply()
方法介绍
语法
func.apply(thisArg, [argsArray])
参数
thisArg
必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。 返回值 调用有指定this值和参数的函数的结果。
描述
在调用一个存在的函数时,你可以为其指定一个 this
对象。 this
指当前对象,也就是正在调用这个函数的对象。 使用 apply
, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
apply
与 call()
非常相似,不同之处在于提供参数的方式。apply
使用参数数组而不是一组参数列表。apply
可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))
。
你也可以使用 arguments
对象作为 argsArray
参数。 arguments
是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply
函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数
手写思路总结
- 思路与call方法相似,只是apply的参数变成了数组
- 要对数组做额外的判断,是否是一个复合需求的数组
手写apply方法
//方法挂载在Functin的原型上,这样所有的函数都可以调用
Function.prototype.myApply = function (context) {
// 如果传入的this对象为 null 和 undefined ,则将函数内部的this自动指向全局对象(浏览器中为window)
if (context === null || content === undefined) {
context = window
} else {
// //防止指定的为原始值,将其转化为原始值对应的实例对象
context = Object(context)
}
// JavaScript权威指南判断是否为类数组对象
function isArrayLike(o) {
if (
o && // o不是null、undefined等
typeof o === 'object' && // o是对象
isFinite(o.length) && // o.length是有限数值
o.length >= 0 && // o.length为非负值
o.length === Math.floor(o.length) && // o.length是整数
o.length < Math.pow(2, 32) // o.length < 2^32
)
return true
else return false
}
const specialPrototype = Symbol('only') // 定义一个唯一的属性用于存放接下来被执行的函数
context[specialPrototype] = this // 隐式绑定this指向到context上
let args = arguments[1] // 获取参数数组
let result
if (args) { // 是否传递第二个参数
if (!Array.isArray(args) && !isArrayLike(args)) {
throw new TypeError('参数类型错误')
} else {
args = Array.from(args) // 转为数组
result = context[specialPrototype](...args) // 执行函数并展开数组,传递函数参数
}
} else {
result = context[specialPrototype]() // 执行函数
}
delete context[specialPrototype] // 删除上下文对象的属性
return result // 返回函数执行结果
}
Function.prototype.bind()
方法介绍
语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
参数
thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArg是null或undefined,执行作用域的 this 将被视为新函数的 thisArg。
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
描述
bind()
函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object
(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
绑定函数具有以下内部属性:
- [BoundTargetFunction] - 包装的函数对象
- [BoundThis] - 在调用包装函数时始终作为 this 值传递的值。
- [BoundArguments] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
- [Call] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用 BoundTargetFunction 上的内部方法 Call,就像这样 Call(boundThis, args)
。其中,boundThis
是BoundThis,args 是 BoundArguments 加上通过函数调用传入的参数列表。
绑定函数也可以使用 new
运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 this
值会被忽略,但前置参数仍会提供给模拟函数。
手写思路总结
通过以上的介绍,我们总结到bind方法的以下特性:
- 调用bind方法后,返回一个内部this改变的函数,函数不立即执行;
- 接收参数中,第一个是执行的this对象,后面的的参数是可选参数,用于指定返回函数的默认参数;
- 返回的函数在被调用的时候可能会继续传参,即二次传参
- 如果返回的方法是以new进行构造,提供的this参数会被忽略,this指向当前的函数的实例对象
手写bind
Function.prototype.myBind = function (newThis, ...params) {
const oldFn = this // 存储原函数
//定义bind方最终返回的函数,这个函数在执行的时候可以接收参数
let newFn = function (...secondParams) {
//首先判断newFn是否是通过new方式构造执行的,判断的方式是判断newFn是否是this上的实例对象
const isNew = this instanceof newFn
const context = isNew ? this : Object(newThis) // 通过new调用的话,this指向不变,否则就改版this指向,即将该方法绑定到传入的newThis上
//按照mdn上的介绍,内部可以使用call实现,改版this并执行函数
//执行函数的时候,需要将两次的参数解构后一起传入
return oldFn.call(context, ...params, ...secondParams)
}
if (oldFn.prototype) {
// 复制原函数的prototype给newFn 一些情况下函数没有prototype,比如箭头函数
newFn.prototype = oldFn.prototype
}
return newFn // 返回拷贝的函数
}
测试
let user = { name: 'Jason', age: 23, }
function Fun(school) {
this.school = school;
console.log(this.name);
console.log(this.age);
console.log(this.school);
}
let fun = Fun.myBind(user, '江南大学'); // this -> user fun() // this -> 对象实例
let obj = new fun();
console.log(obj.__proto__ === Fun.prototype) // ture