开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
语法及使用
call
call
使用一个指定的 this
值和一个或多个参数调用函数,这个函数中的 this
指向 call
方法传递的值。
call
方法 本身无返回值 ,其返回值由 被调用函数 决定。
语法:
function.call(thisArg, arg1, arg2, ...)
用法1:实现继承
在一个子构造函数中,可以通过调用父构造函数的 call
方法来实现继承。
function Person(name, age) {
this.name = name;
this.say = function () {
console.log('my name is ' + this.name)
}
}
function Male(name) {
// 通过 Person 构造函数的 call 方法调用Person,Person函数中的 this 指向:使用 new Male 创建出来的实例对象
Person.call(this, name);
this.sex = 'male'
}
function Female(name) {
// 通过 Person 构造函数的 call 方法调用Person,Person函数中的 this 指向:使用 new Female 创建出来的实例对象
Person.call(this, name);
this.sex = 'female'
}
// 通过 new 关键字创建实例对象 m f,构造函数中的 this 指向对应的 实例对象
const m = new Male('小明');
const f = new Female('小红');
m.say(); // my name is 小明
f.say(); // my name is 小红
用法2:使用 call
方法调用函数时,第一个参数为非正常类型的值
this
指向默认情况下都是一个 对象 ,但是在使用时也允许 不传递/传递其他类型 的值
-
不传参:函数中的
this
默认指向 全局对象(window/global
,由代码运行环境决定,本文默认运行环境是浏览器)注:严格模式下,函数中的
this
指向undefined
-
第一个参数为原始值:默认会将 原始类型 转换为 包装类型
-
null、undefined
:指向 window
function print() {
console.log(this)
console.log( Object.getOwnPropertySymbols(this))
}
print.call(); // window
print.call(null); // window
print.call(undefined); // window
print.call(true); // Boolean {true}
print.call(0); // Number {0}
print.call(''); // String {''}
apply
该方法的语法和作用与 call
方法类似,只有一个区别,就是 call
方法接受的是一个参数列表,而 apply
方法接受的是一个包含多个参数的数组。
语法:
function.call(thisArg, argsArray)
bind
bind
方法会 返回 一个 新函数
,这个 新函数
的 this
指向 bind
方法的第一个参数,而其余参数作为 新函数
的参数。
语法:
const newFunc = function.bind(thisArg[, arg1[, arg2[, ...]]])
用法1:创建绑定函数
bind
最简单的用法就是 创建一个有固定 this
指向的函数
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
// 直接调用函数,this 指向 window
retrieveX(); // 9
// boundGetX 就是 将 retrieveX 函数体中的 this 绑定到 module上的一个新函数
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
同样可以应用在 setTimeout
的 回调函数中。
this.petalCount = 12
function LateBloomer() {
this.petalCount = 100;
}
LateBloomer.prototype.bloom = function() {
// 回调函数中的this默认指向window
setTimeout(this.declare, 1000); // I am a beautiful flower with 12 petals!
// 手动修改回调函数的this
setTimeout(this.declare.bind(this), 1000); // I am a beautiful flower with 100 petals
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后,调用 'declare' 方法
用法2:快捷调用
为需要特定 this
值的函数创建一个快捷方法,也可以使用 bind
。
let arr = [1, 2, 3];
let unboundSlice = Array.prototype.slice;
// 这里的 slice 方法,是 Function.prototype.call 函数,且函数体内的 this 已经绑定为 unboundSlice
let slice = Function.prototype.call.bind(unboundSlice);
slice(arr, 0, 1);
// ====> 等同于
Array.prototype.slice.call(arr, 0, 1);
// ===> 等同于
arr.slice(0, 1);
原理及实现
注: 默认代码运行在 浏览器环境。
需要判断运行环境可以加上这行代码,然后将函数中的 window
改为 __global
const __global = typeof window === 'undefined' ? global : window;
call
// 定义生成三种包装类型的map
const thisArgMap = {
'number': (n) => new Number(n),
'string': (s) => new String(s),
'boolean': (b) => new Boolean(b)
}
// 自定义 call 方法
Function.prototype.myCall = function(thisArg, ...args) {
const thisType = typeof thisArg;
if([undefined, null].includes(thisArg)) { // 传递的是 undefined、null或者为传递参数(未传递默认为undefined)
thisArg = window;
} else if(thisType !== 'object') { // 传递的是基本数据类型
thisArg = thisArgMap[thisType](thisArg); // 生成对应的包装类型
}
// 生成唯一属性,避免污染 thisArg 上的原有属性
const func = Symbol('func');
// 此处 this 指向 调用 该方法的函数, 如:fn.call() this ==> fn
thisArg[func] = this; // 将原函数引用复制到 thisArg 的唯一属性上
// 通过 obj.fn 的方式调用,则原函数体内的 this 会指向 obj;并将参数展开传入
const res = thisArg[func](...args);
// 移除 唯一属性
delete Object.getOwnPropertySymbols(thisArg);
// 返回结果是:原函数的返回值
return res;
}
apply
apply
实现原理和 call
也基本一致,区别在于:call
调用原函数时,要将 args
展开传递;apply
直接传递整个 args
即可。
// call
// ...
const res = thisArg[func](...args);
// ...
// apply
// ...
const res = thisArg[func](args);
// ...
bind
- 记录原函数和需要绑定的
this
指向 - 返回一个新函数
- 新函数内部确定
this
,考虑普通函数
调用和new
关键字调用 - 新函数内部通过
call
/apply
方法调用原函数,将bind
方法传递的参数和新函数调用时传递的参数一起传递给原函数
Function.prototype.myBind = function(thisArg, ...args1) {
// 记录调用 myBind 方法的原函数
const originFunc = this;
// 记录要绑定的 this 指向
const _this = thisArg;
return function newFunc(...args2) {
// 可能将 myBind 返回的函数当作构造函数使用, 即:new 函数
const that = this instanceof newFunc ? this : _this;
originFunc.call(that, ...args1, ...args2)
}
}