面试:call、apply、bind原理以及自己手写简易模式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>call-apply-bind的实现</title>
</head>
<body>
<script type="text/javascript">
// 1.call的使用 call(ctx,...args) ctx:执行函数需要改变的this对象,args:执行函数需要传入的参数 call改变函数this指向
function fn(name) {
console.log(this, name); // window 老王
}
fn('老王');
// 创建一个 obj
let obj = {name: '老李'}
// 通过call执行
fn.call(obj, '我是谁') // {name: "老李"} "我是谁" [注:如果有多个参数在后面接着即可]
// 2.apply的使用 apply(ctx, array) ctx:执行函数的this指向; array:传入一个数组,我将数组的每一项作为函数的参数
function fn1(name1, name2) {
console.log(this, name1, name2);
}
let obj1 = {name: '老王'};
let arr = ['小王', '小明'];
fn1.apply(obj1, arr) // {name: "老王"} "小王" "小明"
// 小结: call传入的是不定参数,apply传入的是一个具体的数组,数组里面包含所有参数
// 例子:求数组中最大值
let list = [1,2,3,4,9,20,6,8],
max = Math.max.apply(null, list);
console.log(max) // => 20
// 3.bind的使用
// bind(ctx, arg...): bind 和call方法的参数是一样的,只是用法有些区别
// bind执行后返回值是一个函数,
// 这个函数的this被定死(绑定)为传入的ctx对象且不可变,
// 这个函数的参数也被定死(绑定)为 ...arg且不可变
function fn3(age) {
console.log(this, age)
}
let newoObj = {name: '小明'}
let fun = fn3.bind(newoObj, 18)
fun() // {name: "小明"} 18
// 参数和this都被绑死
fun.bind(window, 18)() // {name: "小明"} 18
fun.call(window, 18) // {name: "小明"} 18
fun.apply(window, [18]) // {name: "小明"} 18
fun(18) // => {name: "小明"} 18
// 小结:通过bind绑定过后的方法,无论怎么执行都无法修改this指向和参数的,注:以上三个方法如果不传第一个参数,this指向在非严格模式下均为window
// 面试题:
function fn() {
console.log(this)
}
let objtest = {name: '老王'}
fn.call(objtest) // {name: "老王"} call改变函数this指向
fn.call.call(function () {
console.log(this) // window
})
fn.call.call.call.call(function () {
console.log(this) // {name: "老王"}
}, objtest)
/*****************************************************/
// 1、手写call (1)改变this指向 (2)处理传入的参数
Function.prototype.myCall = function(ctx){
// 先将fn挂在context上
ctx.fn = this
var args = [...arguments].slice(1)
var res = ctx.fn(...args)
// ctx调用fn 使fn中的this指向到ctx上
delete ctx.fn
return res
}
// 2、手写apply (1)改变this指向 (2)参数通过数组的形式
Function.prototype.myApply = function(ctx){
// 先将fn挂在context上
ctx.fn = this
var res
if(arguments[1]){
res = [...arguments[1]];
} else {
res = ctx.fn;
}
delete ctx.fn
return res
}
// 3、手写bind bind终生绑定不立即执行返回一个新函数,传参和 call 是一样的
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
// 返回一个绑定this的函数,这里我们需要保存this
const _this = this
const args = [...arguments].slice(1)
//返回一个函数
return function F() {
// 因为返回一个函数,我们可以new F()需要判断能当做构造函数吗
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
let testObj = {
name: '测试手写'
}
let testArr = ['小王', '小明'];
function test(){
console.log(this, name);
}
test.myCall(testObj) // {name: "测试手写", fn: ƒ}
function test2(name1, name2) {
console.log(this, name1, name2);
}
test2.apply(testObj,testArr) // {name: "测试手写"} "小王" "小明"
function foo() {
console.log(this.name) // 测试手写
console.log(arguments) // ['a','b','c']
}
foo.myBind(testObj, 'a', 'b', 'c')()
</script>
</body>
</html>