小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
显示改变this
指向是一个非常高频的操作,在JS中可以通过apply
,call
和bind
三种方式显示的修改this
指向,掌握其原理以及常见使用场景是初学者必不可少的,本文主要通过apply
来阐述
首先看一下apply
的使用
const obj = {
name: "nordon",
};
var name = "globalName";
function fn() {
console.log(this.name);
}
直接调用fn()
,此时输出globalName
若是期望调用fn
时输出的是obj
中的name
属性,此时常用的一种方式,便是通过apply
显示改变其函数内部this
指向
fn.apply(obj);
此时输出nordon
,符合期望
🤔那么若是将name
的声明方式修改一下,直接调用fn
输入什么?
--- var name = "globalName";
+++ const name = "globalName";
实现apply
接下来便通过分析通过apply
做了什么事情,然后手动实现一版
fn.apply(obj);
调用时等价于直接调用obj.fn()
,可以将上述代码修改一下
const obj = {
name: "nordon",
fn() {
console.log(this.name);
},
};
此时直接调用完全符合我们的期望,明白这点之后,那么我们便可以知道其实apply
内部完成的事情就这么简单
实现版本如下:
Function.prototype._apply = function (targetObject, argsArray) {
// 若是没有传递,则置为空数组
if(typeof argsArray === 'undefined' || argsArray === null) {
argsArray = []
}
// 是否传入执行上下文,若没有指定,则指向 window
if(typeof targetObject === 'undefined' || targetObject === null){
targetObject = window
}
// 利用Symbol的特性,设置为key
const targetFnKey = Symbol('key')
// 将调用_apply的函数赋值
targetObject[targetFnKey] = this
// 执行函数,并在删除之后返回
const result = targetObject[targetFnKey](...argsArray)
delete targetObject[targetFnKey]
return result
}
套用上述案例代码并结合手动实现apply
代码进行分析
当调用fn._apply(obj)
时,Function.prototype._apply
内部的this
是当前的调用者fn
函数,targetObject
是obj
在函数内部,主要就是将fn
挂载到obj
上,然后调用函数obj.fn
应用场景
结合下面的应用场景,帮助我们更好的理解apply
判断数据类型
判断一个数据类型可以使用typeof
、instanceof
等方式,但是最推荐的方式是使用Object.prototype.toString
进行类型判断
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.apply([]) //"[object Array]"
Object.prototype.toString.apply('1') // "[object String]"
上述代码可以看到Object
可以直接调用toString
方法,其他类型就需要通过apply
借助Object
原型的方法来实现
类数组借助数组方法
类数组并不是真正的数组,没有数组类型上的方法,但是我们可以利用apply
、call
借用数组的方法,比如借用数组的 push 方法
函数的arguments
就是一个典型的类数组
function fn() {
console.log(arguments); // [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]0: 11: 2callee: ƒ fn()length: 2Symbol(Symbol.iterator): ƒ values()[[Prototype]]
}
fn(1, 2);
类数组不具备数组的方法:
arguments.shift();
会直接在控制台报错:Uncaught TypeError: arguments.shift is not a function
此时可以借助aplly
Array.prototype.shift.apply(arguments);
继承
构造函数通常时借助call、apply来完成继承
function Person(name) {
this.name = name;
this.permission = ["user", "salary", "vacation"];
}
Person.prototype.say = function () {
console.log(`${this.name} 说话了`);
};
function Staff(name, age) {
Person.call(this, name);
this.age = age;
}
Staff.prototype.eat = function () {
console.log('吃东西啦~~~');
}
const zs = new Staff("张三", 12);
console.log(zs);