前置知识
apply只接受两个参数,其中第二个参数是一个类数组或者数组
call可以接受多个参数,即参数不固定
bind可以接受多个参数,即参数不固定,可分多次传参,绑定时不执行,调用时执行
this指向问题
JavaScript中的this关键字是一个非常重要的概念,它表示当前函数执行的上下文对象。this的指向在不同的情况下会有不同的值,下面列举一些常见的情况:
-
全局环境下,this指向全局对象window。
-
函数中,this的指向取决于函数的调用方式。
如果是作为普通函数调用,this指向全局对象window;
如果是作为对象的方法调用,this指向该对象;
如果是使用call、apply或bind方法调用,this指向传入的第一个参数。
构造函数中,this指向新创建的对象。 -
箭头函数中,this指向定义时所在的作用域。
手写
call
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
//call
Function.prototype.myCall = function (context) {
//call方法必须由一个函数调用
if (typeof this !== "function") {
throw new TypeError("not a function")
}
//symbol类型创建的,属性是唯一的,不会与其他属性冲突
const symbolFn = Symbol()
const args = [...arguments].slice(1) //类数组arguments转换为真数组,并删除第一项即obj
//其他两种调用数组方法的方式
// const args1 = [].slice.call(arguments).slice(1)
// const args2 = Array.prototype.slice.call(arguments).slice(1)
// console.log(args, args1, args2)
context = context || window
context[symbolFn] = this //this就是foo
const result = context[symbolFn](...args) //调用obj.foo(1,2,3) this是obj
delete context[symbolFn]
return result //obj
}
const obj = {
name: "obj",
}
function foo() {
console.log(this.name)
}
foo.myCall(obj, 1, 2, 3) // obj
</script>
</body>
</html>
apply
和call类似,处理arguments不同,call是参数列表,apply是数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
//apply
Function.prototype.myApply = function (context) {
if (typeof this !== "function") {
throw new TypeError("error")
}
context = context || window
context.fn = this
//console.log(arguments[1])
var result = arguments[1] ? context.fn(...arguments[1]) : context.fn()
delete context.fn
return result
}
function foo2() {
console.log(this.age)
}
var obj2 = {
age: 101,
}
foo2.myApply(obj2, [1, 2, 3]) // 输出101
</script>
</body>
</html>
bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
//bind
Function.prototype.bind = function () {
var self = this //fn
// var context = Array.prototype.shift.call(arguments) // 读取第一个参数,即this对象 obj3 改变了arguments
// var args = Array.prototype.slice.call(arguments) // 直接返回新的arguments 获取参数 1,2
var context = [...arguments].shift() // 读取第一个参数,即this对象 obj3 没改变arguments
var args = [...arguments].slice(1) // 删除第一项(obj)后获取参数 1,2
// console.log(arguments, self, context, args)
return function () {
// var newArgs = Array.prototype.concat.call( args, Array.prototype.slice.call(arguments) )
var newArgs = [...args, ...arguments]
// console.log(newArgs)
return self.apply(context, newArgs)
}
}
var obj3 = {
name: "why",
}
var fn = function (a, b, c, d) {
console.log(this.name)
console.log([a, b, c, d])
}
var func = fn.bind(obj3, 1, 2) //改变this不执行
func(3, 4) // 输出why [1,2,3,4]
</script>
</body>
</html>