基本语法
fun的this指向thisArg对象
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
联想记忆:apply首字母'a'代表Array
返回值
call/apply:fun执行的结果,即改变了函数的上下文候马上执行函数 bind:返回fun的拷贝,并拥有指定的this值和初始参数,即改变了函数的上下文,但是不执行
核心理念:借用方法
借助已实现的方法,改变方法中数据的this指向,减少重复代码,节省内存
应用场景
- 判断类型
Object.prototype.toString.call(data)
借用它我们几乎可以判断所有类型的数据
'[object String]': 'string',
'[object Number]': 'number',
'[object Boolean]': 'boolean',
'[object Null]': 'null',
'[object Undefined]': 'undefined',
'[object Object]': 'object',
'[object Array]': 'array',
'[object Function]': 'function',
'[object Date]': 'date', // Object.prototype.toString.call(new Date())
'[object RegExp]': 'regExp',
'[object Map]': 'map',
'[object Set]': 'set',
'[object HTMLDivElement]': 'dom', // document.querySelector('#app')
'[object WeakMap]': 'weakMap',
'[object Window]': 'window', // Object.prototype.toString.call(window)
'[object Error]': 'error', // new Error('1')
'[object Arguments]': 'arguments'
- 类数组借用数组的方法
var arrayLike = {
0: 'OB',
1: 'Koro1',
length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}
- apply获取数组最大值最小值
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
- 通过借用父类的构造方法来实现父类方法/属性的继承
// 父类
function supFather(name) {
this.name = name;
this.colors = ['red', 'blue', 'green']; // 复杂类型
}
function sub(name, age) {
// 借用父类的方法:修改它的this指向,赋值父类的构造函数里面方法、属性到子类上
supFather.call(this, name);
this.age = age;
}
- bind的应用场景
(1). 保存变量
for (var i = 1; i <= 5; i++) {
// 缓存参数
setTimeout(function (i) {
console.log('bind', i) // 依次输出:1 2 3 4 5
}.bind(null, i), i * 1000);
}
因为bind返回是一个函数,这个函数也就是闭包
(2) 解决回调函数this丢失
this.pageClass = new Page(this.handleMessage.bind(this)) // 绑定回调函数的this指向
手写call,apply,bind
call的实现
Function.prototype.myCall = function (context, ...args) {
if (context === null || context === undefined) {
context = window;
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context);
}
const specialPrototype = Symbol('特殊属性的Symbol');
context[specialPrototype] = this; // 此时this指向调用该方法的函数本身,函数内部的this则指向context
let result = context[specialPrototype](...args); // 通过隐式绑定执行函数并传递参数
delete context[specialPrototype];
return result;
}
apply的实现,相比较于call,只是多了个传入参数的判断
Function.prototype.myApply = function (context) {
if (context === null || context === undefined) {
context = window;
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context);
}
const 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 < 4294967296) // o.length < 2^32
return true
else
return false
};
const specialPrototype = Symbol('特殊属性的Symbol');
context[specialPrototype] = this;
let args = arguments[1]; // 获取参数数组
let result;
if (args) {
if (!Array.isArray(args) && !isArrayLike(args)) {
throw new TypeError('myApply 第二个参数不为数组并且不为类数组对象抛出错误');
} else {
args = Array.from(args);
result = context[specialPrototype](...args);
}
} else {
result = context[specialPrototype]();
}
delete context[specialPrototype];
return result;
}
bind的实现
Function.prototype.myBind = function (thisObj, ...args) {
const thisFn = this; // 存储源函数
let fToBind = function (...secondArgs) {
const isNew = this instanceof fToBind; // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
const context = isNew ? this: Object(thisObj); // new调用就绑定到this上,否则就绑定到传入的thisObj上
return thisFn.call(context, ...args, ...secondArgs); // 用call调用源函数绑定this的指向并传递参数,返回执行结果
};
fToBind.prototype = Object.create(thisFn.prototype); // 复制源函数的prototype给fToBind
return fToBind;
}
核心思想:返回一个新的函数,函数的最终执行调用call返回结果
其中this instanceof fToBind语句的作用是为了区别是否通过new来调用
let obj = {
name: 'ymhd'
}
function output(msg,other) {
console.log(`Hello ${this.name}, ${msg} ${other}`);
}
let fuc = output.myBind(obj, 'welcome to');
fuc('shenzhen'); // 正常调用
输出
Hello ymhd, welcome to Shenzhen
下面通过new来调用
new fuc('Shenzhen');
输出
Hello undefined, welcome to Shenzhen